Tipografía
Dieciséis estilos de texto nombrados por nivel (un display, seis títulos, tres tamaños de cuerpo, etiquetas, código y un antetítulo), compuestos en Inter, con el énfasis como un paso de peso y no como un rol.
Los estilos de texto de Tanrei se nombran por el nivel que un texto tiene en una página, no por una clase de tamaño. Hay dieciséis roles: un display, de heading-1 a heading-6, tres tamaños de texto corrido, tres roles para el texto que va dentro de los controles y junto a ellos, dos para código y un eyebrow. Un h2 es heading-2; un párrafo es body; el texto de un botón es label. Nadie tiene que recordar cuál de tres roles de catorce píxeles corresponde al encabezado de una tabla.
Un rol es un estilo tipográfico completo: familia, tamaño, peso, interlineado y espaciado entre letras. Los componentes se vinculan a roles, nunca a tamaños en bruto, y una capa da nuevos valores a un rol sin cambiarle el nombre.
| Rol | Valor predeterminado | Uso |
|---|---|---|
display |
48px · 450 · tipo de marca | El único tamaño de cabecera: el mensaje de una pantalla de inicio, una pantalla de bienvenida. No es un nivel de título. |
heading-1 |
32px · 500 · tipo de marca | h1, el título de la página. |
heading-2 |
24px · 500 · tipo de marca | h2, una sección. |
heading-3 |
20px · 500 · tipo de marca | h3, una subsección. |
heading-4 |
18px · 500 | h4, un grupo dentro de una sección. |
heading-5 |
16px · 500 | h5, y el título de una tarjeta, de un diálogo o de un elemento de lista. |
heading-6 |
14px · 500 | h6, y el encabezado de una tabla o de una sección de formulario. |
body-large |
16px · 400 | Texto de entrada y lectura larga. |
body |
14px · 400 | p: el texto predeterminado de una interfaz. |
body-small |
13px · 400 | small: texto secundario, un paso por debajo. |
label |
14px · 500 | Texto dentro de los controles y junto a ellos: botones, campos de entrada, navegación. |
label-small |
12px · 500 | Chips, insignias, controles densos. |
caption |
11px · 500 | Texto de ayuda, metadatos, marcas de tiempo. |
code |
13px · 400 · mono | code y pre. |
code-small |
12px · 400 · mono | Código dentro de una interfaz densa. |
eyebrow |
12px · 500 · con espaciado entre letras | El antetítulo de una sección, encima de un título; el componente lo pone en mayúsculas. |
Cada familia es estrictamente monótona en tamaño: ningún título es más pequeño que el título del nivel siguiente, y ningún rol de cuerpo es más pequeño que el rol de cuerpo siguiente. Donde un título y un rol de cuerpo comparten tamaño (heading-6 y body miden ambos 14px; heading-5 y body-large, ambos 16px), la regla del sistema predeterminado cabe en una frase: los títulos pesan 500, el cuerpo pesa 400. Un preajuste define sus propios pesos (el heading-1 de m3-like es el 400 de Material).
Elige el nivel que tiene el texto, no el tamaño que quieres. Un h2 que debe verse pequeño sigue siendo heading-2; un producto que quiere títulos de sección más pequeños da un nuevo valor a heading-2 en su capa, y todos los h2 lo siguen.
| Elemento | Rol |
|---|---|
h1 … h6 |
heading-1 … heading-6 |
p, li, td |
body (body-large en la prosa con su tamaño de lectura) |
| Un párrafo de entrada | body-large |
small |
body-small |
code, pre |
code |
strong, b |
el rol en el que se encuentra, con el peso de énfasis |
button, input, label |
label |
| Un antetítulo encima de un título | eyebrow |
En la web, @tanrei/web/text.css da a cada rol una clase de utilidad, .tanrei-text-<role>, que define los cinco campos del rol; .tanrei-text-emphasized, en el mismo elemento o en un descendiente, cambia al peso de énfasis. Lee las propiedades personalizadas del sistema activo, así que sigue a data-tanrei-system, a los diales y a un rol con nuevos valores.
<h2 class="tanrei-text-heading-2">Sección</h2>
<p class="tanrei-text-body">Texto corrido con <strong class="tanrei-text-emphasized">énfasis</strong>.</p>Un artículo de ayuda, unas notas de versión, unas condiciones, un mensaje escrito en Markdown, una página de un CMS: el producto no coloca ese texto pieza a pieza, y aun así tiene que leerse como un mismo texto con el resto de la interfaz. La prosa es la parte del sistema que se ocupa de ello: la tabla anterior aplicada a lo que produzca un renderizador, con un único ritmo vertical y un límite para la longitud de línea. Da estilo y espacio; no analiza. Tanrei no incluye un analizador de Markdown en ninguna plataforma.
En la web es una clase de @tanrei/web/prose.css. Esta página está dibujada con ella.
<article class="tanrei-prose">…h1, p, ul, blockquote, hr…</article>
<article class="tanrei-prose" data-size="large">…</article>- Dos tamaños.
mediumcompone el texto corrido enbody, para la prosa entre controles: un panel, un diálogo, una tarjeta.largelo compone enbody-large, para una página cuyo propósito es el texto. Los títulos son los mismos en ambos. - Un solo espacio entre dos vecinos. Los bloques están separados por un espacio; un título tiene más espacio antes que después, de modo que pertenece a lo que le sigue; un título que va justo debajo de otro toma el espacio del de arriba. Los espacios nunca se suman, y el primer bloque y el último no tienen ninguno.
- Una medida. Los bloques de texto miden como máximo 36 em del texto corrido de ancho: entre unos 72 y 80 caracteres latinos, o 36 japoneses. Una tabla, un bloque de código, una figura o cualquier componente colocado en el flujo ocupa todo el ancho.
data-measure="false"levanta el límite donde el contenedor ya es estrecho. - Sin color de texto. La prosa toma el color de contenido de la superficie sobre la que está, así que el mismo texto se lee en una página, en una tarjeta y en un aviso.
- Los componentes siguen siendo ellos mismos. Lo que está dentro de un elemento de Tanrei, o dentro de una parte marcada con
tanrei-not-prose, no recibe estilo de la prosa; la prosa solo lo espacia. - Los enlaces van subrayados en reposo, para que el color por sí solo nunca sea lo que distingue un enlace del texto que lo rodea.
- Idiomas. Las líneas en japonés y en chino se dividen según sus propias reglas; el énfasis en chino, japonés y coreano es recto y con el peso de énfasis, porque esas escrituras no tienen cursiva; sus títulos, y el título de cada componente, se componen sin el espaciado entre letras negativo de los títulos latinos (uno positivo se mantiene), y una escritura que une sus letras o apila marcas sobre ellas no lleva ningún espaciado entre letras. El texto corrido en japonés se compone más suelto, con un interlineado de 1,85, de modo que el espacio entre dos líneas se acerca a un carácter, como recomiendan los requisitos de composición japonesa (JLREQ) para líneas tan largas como la medida. La dirección es la del lugar: un diseño de derecha a izquierda pone a la derecha las sangrías de las listas, la línea de una cita y el borde del texto.
Un título puede llevar un ancla: un enlace a sí mismo, después del final de su texto, que se muestra cuando el cursor pasa por el título o el enlace tiene el foco, y que se muestra siempre donde no hay cursor:
<div class="tanrei-prose-heading">
<h2 id="install">Instalación</h2>
<a class="tanrei-prose-anchor" href="#install" aria-label="Enlace a: Instalación">…</a>
</div>Cada valor es un token comp.prose.*, así que una capa puede reorientar el ritmo sin tocar un margen. El comportamiento está escrito en specs/prose.spec.json; la web lo implementa primero, y las demás plataformas la siguen.
Para destacar un texto sin cambiar su nivel (la pestaña seleccionada, una cifra clave, un strong dentro de un párrafo), una plataforma dibuja el rol con su peso más un paso propio de su familia. Los cuatro pasos son tokens:
| Sistema de diseño | emphasis.display | emphasis.heading | emphasis.body | emphasis.label |
|---|---|---|---|---|
tanrei | 100 | 100 | 100 | 100 |
cupertino | 100 | 200 | 200 | 200 |
m3-expressive-like | 100 | 100 | 100 | 200 |
m3-like | 100 | 100 | 100 | 200 |
Generado a partir de tokens/systems/*.system.json, design/tokens/<system>/tanrei.light.tokens.json
Tanrei suma 100 a todas las familias, así que un heading-1 con énfasis es 600 y un body con énfasis es 500. Un sistema de diseño que destaca de forma distinta según la familia define los cuatro números: m3-like suma 200 a las etiquetas, como hacen los estilos de etiqueta con énfasis de Material; cupertino suma 200 a los títulos, al cuerpo y a las etiquetas. La familia de un rol es el primer segmento de su nombre; caption, code, code-small y eyebrow toman el paso de las etiquetas.
Todas las plataformas exponen el mismo modificador: emphasized en la API de estilos de texto (.tanreiTextStyle(system.typescale.heading1, emphasized: true) en SwiftUI; typescale.heading1.emphasized() en Compose, o emphasized(system.typescale.emphasis) fuera de la composición; theme.textStyle(system.typescale.heading1, emphasized: true) en Flutter; text('heading1', { emphasized: true }) de useTanrei() en React Native; --tanrei-sys-typescale-<role>-font-weight-emphasized y .tanrei-text-emphasized en la web). No hay roles con énfasis que nombrar ni una segunda escala que mantener al día.
Las familias predeterminadas son Inter para el texto latino, Noto Sans JP para el japonés y Roboto Mono para el código, todas con licencia abierta (SIL OFL 1.1 y Apache-2.0). La pila continúa con Noto Sans para otras escrituras y después con la fuente de interfaz del sistema, así que el texto se degrada con elegancia donde las fuentes no están cargadas. Un producto que se publica en más escrituras añade a la pila, en su capa, la familia Noto de cada una (Noto Sans KR, SC, TC, Arabic, Devanagari, Thai); para el chino, el japonés y el coreano, define también lang en la página, porque los tres comparten puntos de código y el orden de las fuentes decide qué formas de glifo aparecen. Tanrei no incluye archivos de fuentes; los aporta el producto.
ref.typeface.brand- Display and headline text. Inter for Latin, Noto Sans JP for Japanese, Noto Sans for other scripts; a product adds the Noto family of each further script it ships (Noto Sans KR, SC, TC, Arabic, Devanagari, Thai…).
ref.typeface.plain- Running text and labels. Inter for Latin, Noto Sans JP for Japanese, Noto Sans for other scripts.
ref.typeface.mono- Code. Roboto Mono, with Noto Sans JP for Japanese inside code.
Generado a partir de tokens/ref/typeface.tokens.json
Los tipos de letra pertenecen al sistema de diseño. Los roles se construyen sobre tres pilas (ref.typeface.brand para display y de heading-1 a heading-3; plain para los títulos más pequeños, el texto corrido, las etiquetas y el antetítulo; mono para el código), y una capa cambia el tipo de letra en todas partes dando un nuevo valor a una pila. Esto es lo que resuelven los sistemas incluidos:
| Sistema de diseño | brand | plain | mono |
|---|---|---|---|
tanrei | Inter, Noto Sans JP, Noto Sans, system-ui, sans-serif | Inter, Noto Sans JP, Noto Sans, system-ui, sans-serif | Roboto Mono, Noto Sans JP, ui-monospace, monospace |
cupertino | -apple-system, BlinkMacSystemFont, system-ui, Inter, sans-serif | -apple-system, BlinkMacSystemFont, system-ui, Inter, sans-serif | ui-monospace, SF Mono, Menlo, monospace |
m3-expressive-like | Roboto, system-ui, sans-serif | Roboto, system-ui, sans-serif | Roboto Mono, ui-monospace, monospace |
m3-like | Roboto, system-ui, sans-serif | Roboto, system-ui, sans-serif | Roboto Mono, ui-monospace, monospace |
Generado a partir de tokens/systems/*.system.json, design/tokens/<system>/tanrei.light.tokens.json
Así es como m3-like compone su texto en Roboto:
"tokens": {
"tanrei.ref.typeface.brand": { "$value": ["Roboto", "system-ui", "sans-serif"], "reason": "El tipo de letra predeterminado de Material Design 3; Roboto tiene licencia OFL." },
"tanrei.ref.typeface.plain": { "$value": ["Roboto", "system-ui", "sans-serif"], "reason": "El tipo de letra predeterminado de Material Design 3; Roboto tiene licencia OFL." },
"tanrei.ref.typeface.mono": { "$value": ["Roboto Mono", "ui-monospace", "monospace"], "reason": "El compañero monoespaciado de Roboto (OFL)." }
}Los componentes no se tocan: se vinculan a roles, y los roles hacen referencia a las pilas.
Una pila se escribe como la escribe CSS, porque esa es la forma que entiende toda herramienta de diseño. Solo la web puede usarla tal como está escrita, así que la compilación lee cada pila una vez y entrega a todas las plataformas los mismos cuatro datos:
| Campo | Significado |
|---|---|
stack |
la lista tal como está escrita |
families |
las familias con nombre, en orden: las que un producto puede instalar o registrar |
generic |
la clase de fuente a la que se recurre: sans-serif, serif o monospace |
system |
true cuando la pila pide la fuente propia de la plataforma antes que cualquier familia con nombre |
La pila brand predeterminada, Inter, Noto Sans JP, Noto Sans, system-ui, sans-serif, se lee como las familias Inter, Noto Sans JP y Noto Sans, generic sans-serif y system false: system-ui viene después de las familias con nombre, así que es solo el recurso final. La pila de cupertino empieza por -apple-system, así que se lee como system true con Inter como única familia con nombre: primero la fuente propia de la plataforma, e Inter donde una plataforma no tiene tal noción.
Una plataforma usa las familias con nombre en orden y, después, su propia fuente de la clase generic; cuando system es true, su propia fuente va primero. Las plataformas no interpretan por sí mismas system-ui, ui-monospace ni las palabras clave genéricas.
Tanrei no distribuye archivos de fuentes, para ningún sistema de diseño. Una pila es una lista de nombres, y el producto aporta las fuentes que hay detrás de los nombres que quiere. Una familia que no está se omite, así que el texto se sigue representando con la familia siguiente o con la fuente de la plataforma, sin cambios en el tamaño, el peso, el interlineado ni el espaciado entre letras del rol.
| Plataforma | Cómo aporta un producto una familia con nombre |
|---|---|
| Web | Cárgala con exactamente ese nombre de familia: tu propio @font-face, Google Fonts o un paquete de fuentes. |
| SwiftUI | Añade los archivos de fuentes a la aplicación y regístralos (UIAppFonts / ATSApplicationFontsPath, o CTFontManagerRegisterFontsForURL). |
| Jetpack Compose | Coloca las fuentes en los recursos de fuentes de la aplicación y asócialas a los nombres con TanreiFontFamilies, que se pasa a TanreiTheme. |
| Flutter | Declara los archivos de fuentes en el pubspec.yaml de la aplicación con los mismos nombres de familia. |
| React Native | Enlaza los archivos de fuentes (npx react-native-asset, o expo-font). React Native no tiene lista de fuentes alternativas y elige un archivo por peso, así que pasa resolveFont a TanreiProvider cuando tus fuentes estén registradas con un nombre por peso. |
Las guías de primeros pasos y el README de cada paquete tienen los detalles. Comprueba la licencia de un tipo de letra antes de distribuirlo: los predeterminados tienen licencia abierta, y cupertino se refiere a las fuentes de Apple solo por palabra clave o por nombre, porque no pueden incluirse ni usarse en otras plataformas; en las demás recurre a un tipo de letra con licencia abierta.
Un preajuste que imita otro sistema de diseño conserva los dieciséis nombres y les da los valores de ese sistema; donde su origen tiene un estilo que el núcleo no nombra, extiende el vocabulario con un rol propio. Los nombres nunca se mueven, así que un producto construido sobre tanrei pasa a m3-like sin tocar un solo título.
m3-likeda nuevos valores a todos los roles a partir de la escala tipográfica de Material Design 3 (heading-1toma el headline large de Material,bodysu body medium,labelsu label large) y añadedisplay-large(57px) ydisplay-small(36px), porque Material tiene tres tamaños de display y un producto necesita uno. Sus pasos de énfasis son los de Material: 100 en display, títulos y cuerpo, y 200 en las etiquetas. El arnés de fidelidad mide todos los estilos de texto que publica Material, los treinta, incluidos los de énfasis, junto con los roles de color y Button: de 288 tokens medidos, 273 son iguales, 14 están cubiertos por otro token, 1 no tiene correspondencia y está amparado por una laguna de imitación registrada, y ninguno es distinto. En la página de sistemas de diseño se muestra como 273 / 274, porque no cuenta los cubiertos. Las cifras se leen detokens/generated/m3-like/fidelity.json, que la compilación reescribe.cupertinoda nuevos valores a todos los roles a partir de los once estilos de texto de iOS de Apple, de Large Title a Caption 2, y añade los tres que el núcleo no nombra:body-callout,body-subheadycaption-small. Sus pasos de énfasis son 100 en display y 200 en lo demás, que es la distancia entre los estilos normal y con énfasis de Apple.
Un rol que añade un preajuste es un token corriente en todas las plataformas: el display-large de m3-like es --tanrei-sys-typescale-display-large-font en la web y un miembro de la escala tipográfica en cada paquete nativo. La vista previa de Theme Studio lo dibuja en la escala de tipos junto a los dieciséis.
- El display y los títulos van apretados. El texto grande se compone con espaciado entre letras negativo (−0.04em en
display, −0.03em enheading-1, que se reduce hasta −0.01em enheading-5), lo que mantiene serenos los títulos grandes;display, con peso 450, se mantiene ligero. - Los tamaños pequeños se abren.
body-small,label-small,captionyeyebrowllevan un ligero espaciado entre letras positivo para que el texto de 11–13px siga siendo legible. - Los pesos son números. Como Inter es una fuente variable, pesos como 450 son válidos y se conservan en todas las plataformas que admiten ejes variables. Los pasos de énfasis son números por la misma razón: 450 + 100 es un peso real.
- El tipo mono es para el código.
codeycode-smallusan Roboto Mono. Eleyebrowse compone en el tipo plain: pequeño, con espaciado entre letras amplio y puesto en mayúsculas por el componente, nunca por el token.
Los interlineados se eligen para que el texto que mezcla escritura latina y CJK no se recorte, y el japonés hereda los mismos roles. El espaciado entre letras negativo es un recurso de la escritura latina: dondequiera que el lang del texto sea chino, japonés o coreano (en prose, en las clases de texto y en el título de cada componente), Tanrei compone el título sin él. Define lang y el título no necesita nada más.
Los tamaños de texto se expresan en unidades relativas en la web (rem) y se escalan con el ajuste de tamaño de texto del usuario en todas las plataformas. Los diseños deben tolerar el texto al 200%: los contenedores crecen, y las etiquetas se dividen en varias líneas antes de truncarse. Cada tamaño es un coeficiente sobre dial.type.base con dial.type.contrast como exponente, de modo que un dial mueve toda la escala y el otro separa los títulos del cuerpo.
Consulta todos los roles con muestras en vivo en la referencia de la escala tipográfica.