Saltar al contenido
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
Primeros pasosWebReactSwiftUIJetpack ComposeFlutterReact NativePreajustesTu propio sistema de diseñoUsar Tanrei con agentes de IAMarcaTokens de diseñoSistemas de diseño y personalizaciónTheme StudioColorTipografíaIconosFormaElevaciónMovimientoEstados de interacciónComportamientoDiseño de páginaAccesibilidadTextosTokens de colorTokens de la escala tipográficaTokens de formaTokens de elevaciónTokens de movimientoTokens de estadoTokens de espacio y de medidaComponentesBotónBotón de acción principalBotón segmentadoGrupo de botonesBotón con menúBarra de herramientasBotón de copiarCasilla de verificaciónBotón de opciónInterruptorChipMenúControl deslizanteCampo de control deslizanteCalendarioDateTimePickerGrupo de camposGrupo de botones de opciónGrupo de casillas de verificaciónSelectCampo de selecciónBarra de menúsSubida de archivosSelector de archivos AvanzadoZona de soltar archivosEntrada de texto AvanzadoCampo de textoCampo de chipsCampoCuadro combinado AvanzadoCampo de cuadro combinadoEntrada numérica AvanzadoCampo numéricoEntrada de color AvanzadoCampo de colorEntrada de código de un solo uso AvanzadoCampo de código de un solo usoEntrada de texto incrustada AvanzadoCuadro de redacción de chatTarjetaDivisorCapa modal AvanzadoDiálogoListaFila de listaHoja inferiorHoja lateralCarruselBloque de códigoTablaPopoverPasosAcordeónLista de descripcionesFiguraEstructura de la aplicaciónCabecera de páginaVista de árbolLínea de tiempoVista previa de imágenesTirador de tamañoMarco redimensionableÁrea de desplazamientoInsigniaIndicador de progresoMedidorContador animadoIndicador de cargaSnackbarTooltipAtajo de tecladoTagCódigo en líneaCalloutAvatarEstado vacíoGrupo de tagsEsqueletoElemento de archivoMuestraTexto en marquesinaPestañasVista de pestañasBarra de la aplicaciónBarra de navegaciónPanel de navegaciónBúsquedaEnlacePaginaciónEnlaces anterior y siguienteIndicador de pasosRuta de navegaciónTabla de contenidoTarjeta de enlaceMenú FABEstadoCómo se construye TanreiCómo contribuirLicencia y atribución
InicioFundamentosSistemas de diseño y personalización

Sistemas de diseño y personalización

Un vocabulario, muchos sistemas de diseño. Cambia un dial, declara una paleta, reorienta un token, y todas las plataformas lo siguen en un solo commit.

content_copyVer en Markdown

Tanrei está construido para poder reproducir cualquier sistema de diseño, con exactitud y no por aproximación, mientras su propio vocabulario se mantiene pequeño. Dos reglas lo hacen posible: los sistemas de diseño se diferencian ante todo en los valores, y lo que un valor no puede expresar, lo añade una capa.

Qué es un token del núcleo y qué no

El vocabulario del núcleo de Tanrei es todo estado que un sistema de diseño puede adoptar dentro de un producto. De ahí sale una prueba para cada token que se propone, la prueba del producto único:

  • Es un token si un solo producto puede necesitar la distinción, por significado o por lugar. focus-ring está separado de primary porque un producto puede querer un color de marca saturado y un anillo de foco tranquilo. Que el sistema predeterminado les dé valores relacionados es una coincidencia, no un motivo para fusionarlos. Button tiene tamaños porque un mismo producto usa varios.
  • No es un token si solo lo tiene un sistema de diseño, o un producto poco común. El tertiary de Material Design 3 es una paleta declarada en el preajuste que lo reproduce, y sus roles primary-fixed son extensiones de ese preajuste, no huecos con los que cargan todos. Veinte tamaños de botón para un producto exótico tampoco son vocabulario del núcleo; ese producto los añade a su propia capa.

Si aun así no se puede reproducir algún rasgo de un sistema de diseño (un material de fondo, una tabla de tamaños de texto propia), eso es un defecto de Tanrei, y se anota en el registro de lagunas de imitación (docs/governance/mimicry-gaps.json en el repositorio).

Diales: un cambio, en todas partes

Un sistema de diseño debería resultar adecuado de entrada y seguirte cuando cambias una sola cosa. Bajo todo lo demás hay ocho diales numéricos:

Dial Valor predeterminado Mueve
dial.type.base 16px todos los tamaños de texto
dial.type.contrast 1 cuánto se separan los títulos del texto de cuerpo
dial.space.unit 8px todas las distancias
dial.size.unit 4px las alturas de los controles, los tamaños de los iconos, las áreas de pulsación
dial.density 1 la altura de los controles y las filas, y el espacio alrededor de su contenido: por debajo de 1 más juntos, por encima de 1 más holgados
dial.shape.unit 2px todas las esquinas
dial.stroke.unit 1px los bordes, los anillos, el indicador de foco
dial.motion.unit 50ms todas las duraciones

Cada token es de uno de tres tipos:

  • derivado: una regla pequeña, normalmente un coeficiente sobre un dial: display es type.base × 3, space175 es space.unit × 1.75. Las escalas tipográficas reales no son geométricas, así que cada token tiene su propio coeficiente;
  • fijo: un literal con un motivo declarado, exento de los diales (corner.full, una curva de aceleración, un tamaño de nota al pie impuesto por ley);
  • alias: una referencia que se puede reorientar (button.container.gap → space100).

En la web, los diales también funcionan en vivo: los tokens derivados se emiten como expresiones calc(), de modo que definir --tanrei-dial-type-base en cualquier elemento reescala todo lo que contiene. Una prueba evalúa cada token derivado en un navegador real con varios ajustes de los diales y exige que sea igual al resultado de la compilación.

Un sistema de diseño es una capa

Un sistema de diseño es un pequeño archivo JSON que extiende otro. Sobre todo cambia valores; donde hace falta, añade al vocabulario.

{
  "id": "acme",
  "extends": "tanrei",
  "palettes": {
    "brand":   { "description": "El rojo de Acme lo fijan las directrices de marca.", "source": { "seed": "#e60012" }, "roles": "intent" },
    "correct": { "description": "Un cuestionario marca las respuestas correctas: ni éxito ni primario.", "source": { "seed": "#0f766e" }, "roles": "intent" }
  },
  "tokens": {
    "tanrei.dial.type.base": { "$value": { "value": 17, "unit": "px" }, "reason": "Nuestro público lee a la distancia de un brazo." },
    "tanrei.comp.button.container.padding-inline-start.medium": { "scale": 2 },
    "tanrei.comp.button.container.gap.medium": { "$value": "{tanrei.sys.space.space50}" },
    "tanrei.sys.typescale.label-small": { "$value": { "fontSize": { "value": 11, "unit": "px" } }, "reason": "El tamaño de la nota legal es contractual." }
  }
}
Quieres Escribes
Todo un poco más grande ajusta un dial
El margen interior de Button al doble del predeterminado, y que siga obedeciendo al dial de espaciado "scale": 2
Que este token use otro paso reorienta el alias
Que este valor concreto no se mueva hagan lo que hagan los diales fija $value con un reason
Un color de marca o de dominio con sus propios roles on- y -container, con el contraste comprobado declara una paleta con "roles": "intent"

Como el código de todas las plataformas se genera a partir del mismo sistema resuelto, un solo pull request cambia a la vez la web, SwiftUI, Compose, Flutter y React Native.

Añadir al vocabulario

Los valores no siempre bastan. Un sistema de referencia tiene tamaños de botón que el núcleo no tiene; tu producto tiene un componente de valoración propio. Por eso una capa también puede extender el vocabulario: bajo vocabulary, siempre con un rationale, y solo añadiendo:

"vocabulary": {
  "tanrei.sys.space.space1200":        { "$type": "dimension", "rationale": "Las secciones de cabecera necesitan un paso de ritmo de 96px.",
                                          "derive": { "fn": "mul", "of": "{tanrei.dial.space.unit}", "by": 12 } },
  "tanrei.comp.button.size.3x":        { "from": "large", "scale": 1.5, "rationale": "La llamada a la acción principal de la pantalla de inicio." },
  "tanrei.comp.button.variant.danger": { "from": "filled", "rationale": "Aquí, la confirmación destructiva es un énfasis distinto." },
  "acme.comp.rating.star.size":        { "$type": "dimension", "rationale": "La estrella del componente de valoración.",
                                          "derive": { "fn": "mul", "of": "{tanrei.dial.size.unit}", "by": 5 } }
}
Añades Qué ocurre
Un paso en una serie: un paso de espaciado, una esquina, un rol de texto, un rol de color con el rol que se dibuja sobre él (onRole) Se sitúa junto a sus vecinos, sigue el mismo dial y su contraste se comprueba como el de un par del núcleo
Un valor en un eje de un componente: un tamaño o una variante de Button Basta una línea. Un tamaño clona de otro tamaño todos los miembros del eje (altura, márgenes interiores, estilo de la etiqueta, icono) y los escala, sin dejar de seguir los diales. Una variante clona sus colores en reposo; las reglas de patrón del sistema dan valor por ti a sus tokens de los estados al pasar el cursor, con foco, pulsado y deshabilitado
Tus propios tokens, en el espacio de nombres de tu capa (acme.…) Para tus propios componentes. Pueden hacer referencia a cualquier token tanrei.*, seguir los diales y los esquemas, y se convierten en --acme-… en la web y en un objeto con tipo en las demás plataformas

vocabulary dice que algo existe; tokens y rules le dan un valor, exactamente igual que a los tokens del núcleo, de modo que una capa hija cambia una extensión como cualquier otra cosa. Las extensiones se heredan, nunca eliminan ni cambian el tipo de nada del núcleo, y no pueden añadir estados de interacción (esos pertenecen a la plataforma). Cada paquete generado las enumera, con quién las declaró y por qué.

Los componentes aceptan lo que añades. <tanrei-button size="3x" variant="danger"> funciona en la web, y los paquetes nativos admiten las mismas claves; un valor que el sistema de diseño activo no tiene se representa como el predeterminado. Un sistema que no añade nada no paga nada: su código generado no menciona las extensiones en absoluto.

Así se sostienen juntos dos ideales: el vocabulario propio de Tanrei se mantiene pequeño y con nombres llanos, y cualquier sistema de diseño se puede igualar con exactitud, porque no hay que añadir nada al núcleo por causa de otro.

El aspecto de un estado también es un valor

Los componentes nunca calculan el aspecto al pasar el cursor ni el del estado pulsado. Cada estado tiene sus propios tokens (button.filled.hovered.container.color, …), y las reglas de patrón de un sistema les dan valor:

{ "match": "tanrei.comp.*.*.hovered.container.color",
  "derive": { "fn": "mix", "base": "{tanrei.comp.$1.$2.container.color}", "with": "{tanrei.comp.$1.$2.label.color}", "amount": "{tanrei.sys.state.hover-state-layer-opacity}" } }

Esa es la capa de estado de Tanrei. Un sistema con colores explícitos por estado escribe alias en su lugar; uno sin estado al pasar el cursor hace del estado un alias de su valor en reposo; uno que se atenúa al pulsar define pressed.opacity. El mismo vocabulario, valores distintos.

Elecciones de tipo

Unas pocas diferencias no son cantidades en absoluto: una pulsación a la que se responde con una onda o atenuando el control, un foco del teclado que se muestra como un anillo o como un resplandor. Son dos tokens bajo sys.behavior (pressed-feedback, focus-style), cada uno con una lista corta y cerrada de nombres, y una capa elige uno como cualquier otro valor. Cómo se curva una esquina es una cantidad, no una elección: sys.shape.corner-smoothing, en Forma. Comportamiento las describe. El conjunto de iconos y los tipos de letra también puede cambiarlos una capa: consulta Iconos y Tipografía.

Los sistemas incluidos

El mismo <tanrei-button>, el mismo marcado. m3-like y cupertino están ahí para probar el mecanismo frente a dos sistemas de caracteres opuestos, y como puntos de partida para tu propia capa.

La exactitud se mide, no se afirma. m3-like lleva un mapa de fidelidad con los tokens publicados de material-web (Apache-2.0): cada rol de color, cada estilo de texto y cada token de Button de la referencia debe ser igual, estar cubierto por otro token o figurar con la laguna de imitación que lo explica. La cifra se muestra más abajo junto al preajuste: cuántos son iguales de entre los tokens que no están cubiertos por otro, leída de tokens/generated/<id>/fidelity.json. Se llama «-like» por motivos de marca registrada, no porque sea una aproximación.

tanrei Tanrei

ClaroFilledTonalElevatedOutlinedTextDisabled
OscuroFilledTonalElevatedOutlinedTextDisabled
Oscuro · Contraste altoFilledTonalElevatedOutlinedTextDisabled
Paletas declaradas
accent
Valores cambiados
152
Reglas de patrón
132
WCAG
Cumple todos los invariantes en todos los esquemas. Una infracción hace fallar la compilación.

The default design system. Near-monochrome surfaces, a monochrome primary and one accent. It takes its values from core and leaves out the components and variants it does not draw.

cupertino Cupertino (Apple HIG-inspired)

ClaroFilledTonalElevatedOutlinedTextDisabled
OscuroFilledTonalElevatedOutlinedTextDisabled
Oscuro · Contraste altoFilledTonalElevatedOutlinedTextDisabled
Extiende
core
Paletas declaradas
Ninguna
Valores cambiados
581
Reglas de patrón
15
Extensiones del vocabulario
3
Fidelidad (medida)
sin medir: la referencia no publica tokens legibles por máquina
WCAG
113 mediciones de contraste por debajo de los umbrales de Tanrei (todas en contraste estándar). Solo a título informativo: los criterios de accesibilidad de Tanrei no se aplican a un preajuste de imitación, en ningún esquema de contraste.

An independent approximation of the look of Apple's platforms, expressed purely as values: a saturated system tint, pure white and black canvases, gray fills with tinted labels, rounded rectangles, no hover state and a dimmed pressed state.

Tanrei is not affiliated with, endorsed by, or sponsored by Apple Inc. Apple is a trademark of Apple Inc. The values here are Tanrei's own approximations, not a transcription of Apple's documentation, and no Apple font or symbol is bundled: the system font is referenced by name only.

m3-expressive-like M3 Expressive-like (Material 3 Expressive-inspired)

ClaroFilledTonalElevatedOutlinedTextDisabled
OscuroFilledTonalElevatedOutlinedTextDisabled
Oscuro · Contraste altoFilledTonalElevatedOutlinedTextDisabled
Extiende
m3-like
Paletas declaradas
Ninguna
Valores cambiados
53
Reglas de patrón
0
Extensiones del vocabulario
2
Fidelidad (medida)
35 / 55 tokens de referencia iguales · 0 diferentes · 20 sin correspondencia, con una brecha registrada — androidx.compose.material3 button size tokensv0_11_0 (Apache-2.0)
WCAG
Cumple todos los invariantes en todos los esquemas. Solo a título informativo: los criterios de accesibilidad de Tanrei no se aplican a un preajuste de imitación, en ningún esquema de contraste.

m3-like with the five button sizes of Material 3 Expressive — extra small to extra large, each with its own paddings, icon size, icon-to-label gap and outline width. Colors, type, shape and states are m3-like's.

Tanrei is not affiliated with, endorsed by, or sponsored by Google. "Material Design" is Google's; this preset is an independent reproduction of published, Apache-2.0 licensed token values.

m3-like M3-like (Material 3-inspired)

ClaroFilledTonalElevatedOutlinedTextDisabled
OscuroFilledTonalElevatedOutlinedTextDisabled
Oscuro · Contraste altoFilledTonalElevatedOutlinedTextDisabled
Extiende
core
Paletas declaradas
tertiary
Valores cambiados
711
Reglas de patrón
19
Extensiones del vocabulario
18
Fidelidad (medida)
273 / 274 tokens de referencia iguales · 0 diferentes · 1 sin correspondencia, con una brecha registrada — material-webv0_192 (Apache-2.0)
WCAG
Cumple todos los invariantes en todos los esquemas. Solo a título informativo: los criterios de accesibilidad de Tanrei no se aplican a un preajuste de imitación, en ningún esquema de contraste.

An independent approximation of Material Design 3's baseline look, expressed purely as values over Tanrei's vocabulary: a chromatic primary, tonal surfaces, pill buttons, shadow elevation without a ring.

Tanrei is not affiliated with, endorsed by, or sponsored by Google. "Material Design" is Google's; this preset is an independent approximation derived from publicly available, Apache-2.0 licensed design tokens.

En la web, un preajuste es una hoja de estilos adicional, limitada a un atributo, así que los sistemas pueden incluso convivir uno al lado del otro. Su entrada registra los componentes que incluye, entre ellos los que el sistema predeterminado deja fuera (@tanrei/web registra solo los del sistema predeterminado):

import '@tanrei/web/tokens.css';
import '@tanrei/web/systems/cupertino.css';
import '@tanrei/web/cupertino'; // los componentes que incluye cupertino
<body data-tanrei-system="cupertino">

Lo que todavía no pueden expresar ni los valores ni las elecciones de tipo (los materiales de fondo, las etiquetas en mayúsculas como cuestión de estilo) figura como defecto de Tanrei en docs/governance/mimicry-gaps.json; no se oculta.

Un componente que tu sistema no tiene

Un sistema puede dejar fuera un componente, o un valor de la variant de un componente: el sistema por defecto no tiene botón segmentado, ni campo de texto filled u outlined. Una capa lo dice en components, con el motivo y lo que se usa en su lugar:

"components": {
  "omit": { "carousel": { "reason": "Acme shows one thing at a time.", "insteadUse": ["list"] } },
  "variants": { "tabs": { "omit": ["tonal"] } }
}

Lo que se deja fuera se sigue dibujando y funciona. En una compilación de desarrollo, cada elemento que lo usa avisa una vez en la consola, con el nombre de tu sistema y lo que usa en su lugar. El aviso nombra el componente que escribiste: <tanrei-text-field variant="outlined"> se informa como el outlined del campo de texto, no como la entrada de texto que dibuja dentro. Un componente que vive en la capa de otro sistema, como el menú FAB en m3-like, se informa igual, con la capa en la que vive.

Para que tus pruebas fallen por ello, haz que se informe como un error y pregunta qué se usó:

import { setAvailabilityMode, unavailableUses } from '@tanrei/web';

setAvailabilityMode('error'); // en la preparación de tus pruebas, antes de que se dibuje nada

test('el producto usa solo lo que tiene su sistema', async () => {
  // …renderiza las pantallas del producto…
  expect(unavailableUses()).toEqual([]);
});

unavailableUses() enumera cada uso una vez, en orden alfabético (segmented-button, text-field:outlined), en cualquiera de los dos modos. Una compilación de producción no comprueba nada, así que allí la lista queda vacía. @tanrei/web/availability contiene las mismas dos funciones sin registrar ningún elemento.

Compartir un sistema

Compilar un sistema produce un paquete DTCG 2025.10 autocontenido (archivos de tokens sencillos, con todos sus valores, y un resolver estándar), de modo que cualquier herramienta que lea tokens de diseño puede consumirlo sin Tanrei. Los archivos de capa que lo produjeron viajan dentro del paquete, para que Tanrei (o un agente de IA) pueda reconstruirlo o modificarlo.

Accesibilidad

Los umbrales de contraste son constantes que nadie puede cambiar. En el sistema predeterminado, un incumplimiento hace fallar la compilación. En una capa de producto, un incumplimiento es una advertencia: se imprime al compilar y se escribe en los artefactos generados, nunca en silencio. A un preajuste que imita otro sistema se le exige, en cambio, fidelidad a ese sistema: los criterios de accesibilidad de Tanrei no se le aplican, ni en el contraste estándar ni en el medio ni en el alto, y las relaciones de contraste que la compilación mide en él quedan registradas como información, no como advertencias. Un fallo funcional (un control que no se puede manejar, un nombre o un estado que falta) es un defecto en cualquier sistema, también en un preajuste de imitación.