Components (registry)Layout
Prose
Drop long copy in one container; headings, lists, quotes, code, and tables inherit DS type. Articles and legal pages.
Anatomía CSS
<div class="prose"> </div>
| Clase | Propósito |
|---|---|
prose | root |
prose--narrow | modifier |
prose--wide | modifier |
Tokens resueltos
Valores finales tras seguir la cadena de tokens. Derivados del source: si un token cambia, esta tabla cambia sola.
| Variante | Prop | Valor | Token |
|---|---|---|---|
| all | fg | #525252 | muted.foreground |
| all | bg | transparent | (literal) |
| all | border | none | (unparsed) |
Animaciones
| Propiedad | Duración | Easing |
|---|---|---|
text-decoration-color | var(--duration-150) | var(--easing-out) |
CSS mínimo funcional
Autocontenido: sin imports ni tokens. Para previews y prototipos — en producción se consume el CSS del DS (@atom-uikit/css/components.css</code>, <code>@atom-uikit/tokens/tokens.css).
/* -------------------------------------------------------------------------
Prose
Contenido largo estilizado POR ELEMENTO: articulos, documentacion y legales.
El resto del DS tipografia por clase (.h2, .body, .lead), lo cual exige una
clase por nodo — imposible cuando el cuerpo viene de un CMS, de un rich text
de Webflow o de un modelo de bloques. `.prose` cubre ese hueco: se pone UNA
vez en el contenedor y sus hijos semanticos heredan el sistema.
API:
--prose-measure medida de lectura (default 68ch)
--prose-anchor-offset scroll-margin de los headings, para que un salto a
#id no quede debajo de una barra sticky del host
Variantes: --narrow (medida corta, 56ch), --wide (sin limite)
------------------------------------------------------------------------- */
.prose {
max-width: 68ch;
color: #0a0a0a;
font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif;
font-size: 16px;
line-height: 1.5;
font-weight: 400;
text-wrap: pretty;
/* El minimo automatico de un item de grid/flex es su contenido, y una tabla
ancha o una URL larga lo inflan hasta desbordar el viewport arrastrando toda
la pagina. Con min-width:0 el componente se defiende solo, sin depender de que
su contenedor declare minmax(0, ...). */
min-width: 0;
/* Un legal esta lleno de URLs, correos y numeros de registro sin espacios:
sin esto no parten y sobresalen de su columna en pantallas angostas. */
overflow-wrap: break-word;
}
.prose--narrow {
--prose-measure: 56ch;
}
.prose--wide {
--prose-measure: none;
}
/* ---- Headings ----
En sans, no en display. typography.css manda h1/h2 a Grift porque son la voz
de la marca; aqui los headings son ESTRUCTURA densa (un legal puede traer 36)
y aplica el mismo criterio que el DS ya usa para h3-h6: legibilidad sobre
voz. El titulo del documento lo pone el layout, fuera de .prose. */
.prose :is(h2, h3, h4, h5, h6) {
font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif;
color: #0a0a0a;
letter-spacing: -0.01em;
/* em: el ritmo del texto escala con su propio tamano, no con la reticula de
composicion de componente (misma razon que gap/radius en em). */
margin-block: 2em 0.75em;
text-wrap: balance;
/* Los ids los inyecta el behavior toc; sin esto un salto a #id aterriza
debajo del navbar sticky de la pagina anfitriona. */
scroll-margin-block-start: var(--spacing-20);
}
.prose h2 {
font-size: 31.25px;
line-height: 1.25;
font-weight: 600;
}
.prose h3 {
font-size: 25px;
line-height: 1.3;
font-weight: 600;
}
.prose h4 {
font-size: 20px;
line-height: 1.45;
font-weight: 500;
}
.prose :is(h5, h6) {
font-size: 16px;
line-height: 1.5;
font-weight: 600;
}
.prose > :first-child {
margin-block-start: 0;
}
/* ---- Bloques de texto ---- */
.prose p {
margin-block: 0 1.5em;
}
.prose :is(strong, b) {
font-weight: 600;
color: #0a0a0a;
}
.prose small {
font-size: 12.8px;
line-height: 1.45;
color: #525252;
}
/* ---- Links ----
`--link` es el token de texto-sobre-fondo del DS (existe porque sky.500 sobre
claro daba 1.73:1). Subrayado siempre presente: en un legal, distinguir un
link por color es insuficiente (WCAG 1.4.1, uso del color). */
.prose a {
color: #0e7490;
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 0.2em;
transition: text-decoration-color 150ms cubic-bezier(0.22, 1, 0.36, 1);
}
.prose a:hover {
text-decoration-color: transparent;
}
.prose a:focus-visible {
outline: 2px solid #0a0a0a;
outline-offset: 1.5px;
border-radius: 4px;
}
/* ---- Listas ---- */
.prose :is(ul, ol) {
margin-block: 0 1.5em;
padding-inline-start: 1.5em;
}
.prose ul {
list-style: disc;
}
.prose ol {
list-style: decimal;
}
.prose li {
margin-block-end: 0.5em;
}
.prose li::marker {
color: #525252;
}
/* Listas anidadas: sin doble margen ni salto de ritmo. */
.prose :is(ul, ol) :is(ul, ol) {
margin-block: 0.5em 0;
}
/* ---- Cita ---- */
.prose blockquote {
margin-block: 0 1.5em;
margin-inline: 0;
padding-inline-start: 1.25em;
/* Naranja de marca como acento fino, nunca como relleno ni como texto. */
border-inline-start: 2px solid #ff6600;
color: #525252;
font-size: 20px;
line-height: 1.45;
}
.prose blockquote > :last-child {
margin-block-end: 0;
}
/* ---- Codigo ---- */
.prose code {
font-family: 'interval', ui-monospace, 'cascadia code', monospace;
font-size: 0.9em;
background-color: #f5f5f5;
color: #0a0a0a;
border-radius: 4px;
padding: 0.15em 0.4em;
}
.prose pre {
margin-block: 0 1.5em;
padding: 16px;
background-color: #f5f5f5;
border-radius: 8px;
overflow-x: auto;
}
.prose pre code {
background-color: transparent;
padding: 0;
font-size: 12.8px;
line-height: 1.45;
}
/* ---- Tabla ----
Los legales traen tablas (el de suscripcion trae una de suspension por plan).
El componente `table` del DS es para data-tables con su propio markup; aqui
basta la version de lectura, sobre etiquetas desnudas. */
.prose table {
margin-block: 0 1.5em;
border-collapse: collapse;
font-size: 12.8px;
line-height: 1.45;
/* Una tabla ancha no puede desbordar el ancho del host ni forzar scroll
horizontal a la pagina: scrollea ella misma. width max-content + max-width
100% para que no se estire cuando cabe (display:block la saca del algoritmo
de tabla para el ancho, las filas siguen siendo filas).
OJO: max-content hace que el minimo de contenido de la tabla sea su ancho
completo. Si el contenedor no acota ese minimo (min-width:0 en .prose, o
minmax(0,1fr) en la rejilla), la pagina entera se ensancha. */
display: block;
width: max-content;
max-width: 100%;
overflow-x: auto;
}
/* Las celdas parten palabras largas en vez de estirar la tabla; con max-content
arriba, una celda que no parte hace que ninguna columna se ajuste. */
.prose :is(th, td) {
overflow-wrap: break-word;
}
.prose :is(th, td) {
padding: 12px;
border-block-end: 1px solid #e5e5e5;
text-align: start;
vertical-align: top;
}
.prose th {
font-weight: 600;
color: #0a0a0a;
}
.prose td {
color: #525252;
}
/* ---- Otros ---- */
.prose hr {
margin-block: 2em;
border: none;
border-block-start: 1px solid #e5e5e5;
}
.prose :is(img, video) {
max-width: 100%;
height: auto;
border-radius: 12px;
}
.prose dt {
font-weight: 600;
}
.prose dd {
margin-inline-start: 0;
margin-block-end: 1em;
color: #525252;
}
.prose > :last-child {
margin-block-end: 0;
}
@media (prefers-reduced-motion: reduce) {
.prose a {
transition-duration: 0ms;
}
}
Codigo fuente
styles/components/prose.css
/* ------------------------------------------------------------------------- Prose Contenido largo estilizado POR ELEMENTO: articulos, documentacion y legales. El resto del DS tipografia por clase (.h2, .body, .lead), lo cual exige una clase por nodo — imposible cuando el cuerpo viene de un CMS, de un rich text de Webflow o de un modelo de bloques. `.prose` cubre ese hueco: se pone UNA vez en el contenedor y sus hijos semanticos heredan el sistema. API: --prose-measure medida de lectura (default 68ch) --prose-anchor-offset scroll-margin de los headings, para que un salto a #id no quede debajo de una barra sticky del host Variantes: --narrow (medida corta, 56ch), --wide (sin limite) ------------------------------------------------------------------------- */ .prose { max-width: var(--prose-measure, 68ch); color: var(--foreground); font-family: var(--font-family-sans); font-size: var(--font-size-base); line-height: var(--line-height-base); font-weight: var(--font-weight-regular); text-wrap: pretty; /* El minimo automatico de un item de grid/flex es su contenido, y una tabla ancha o una URL larga lo inflan hasta desbordar el viewport arrastrando toda la pagina. Con min-width:0 el componente se defiende solo, sin depender de que su contenedor declare minmax(0, ...). */ min-width: 0; /* Un legal esta lleno de URLs, correos y numeros de registro sin espacios: sin esto no parten y sobresalen de su columna en pantallas angostas. */ overflow-wrap: break-word; } .prose--narrow { --prose-measure: 56ch; } .prose--wide { --prose-measure: none; } /* ---- Headings ---- En sans, no en display. typography.css manda h1/h2 a Grift porque son la voz de la marca; aqui los headings son ESTRUCTURA densa (un legal puede traer 36) y aplica el mismo criterio que el DS ya usa para h3-h6: legibilidad sobre voz. El titulo del documento lo pone el layout, fuera de .prose. */ .prose :is(h2, h3, h4, h5, h6) { font-family: var(--font-family-sans); color: var(--foreground); letter-spacing: var(--letter-spacing-snug); /* em: el ritmo del texto escala con su propio tamano, no con la reticula de composicion de componente (misma razon que gap/radius en em). */ margin-block: 2em 0.75em; text-wrap: balance; /* Los ids los inyecta el behavior toc; sin esto un salto a #id aterriza debajo del navbar sticky de la pagina anfitriona. */ scroll-margin-block-start: var(--prose-anchor-offset, var(--spacing-20)); } .prose h2 { font-size: var(--font-size-2xl); line-height: var(--line-height-2xl); font-weight: var(--font-weight-semibold); } .prose h3 { font-size: var(--font-size-xl); line-height: var(--line-height-xl); font-weight: var(--font-weight-semibold); } .prose h4 { font-size: var(--font-size-lg); line-height: var(--line-height-lg); font-weight: var(--font-weight-medium); } .prose :is(h5, h6) { font-size: var(--font-size-base); line-height: var(--line-height-base); font-weight: var(--font-weight-semibold); } .prose > :first-child { margin-block-start: 0; } /* ---- Bloques de texto ---- */ .prose p { margin-block: 0 1.5em; } .prose :is(strong, b) { font-weight: var(--font-weight-semibold); color: var(--foreground); } .prose small { font-size: var(--font-size-sm); line-height: var(--line-height-sm); color: var(--muted-foreground); } /* ---- Links ---- `--link` es el token de texto-sobre-fondo del DS (existe porque sky.500 sobre claro daba 1.73:1). Subrayado siempre presente: en un legal, distinguir un link por color es insuficiente (WCAG 1.4.1, uso del color). */ .prose a { color: var(--link); text-decoration: underline; text-decoration-thickness: var(--stroke-hairline); text-underline-offset: 0.2em; transition: text-decoration-color var(--duration-150) var(--easing-out); } .prose a:hover { text-decoration-color: transparent; } .prose a:focus-visible { outline: var(--ring-width) solid var(--ring); outline-offset: var(--ring-offset); border-radius: var(--radius-sm); } /* ---- Listas ---- */ .prose :is(ul, ol) { margin-block: 0 1.5em; padding-inline-start: 1.5em; } .prose ul { list-style: disc; } .prose ol { list-style: decimal; } .prose li { margin-block-end: 0.5em; } .prose li::marker { color: var(--muted-foreground); } /* Listas anidadas: sin doble margen ni salto de ritmo. */ .prose :is(ul, ol) :is(ul, ol) { margin-block: 0.5em 0; } /* ---- Cita ---- */ .prose blockquote { margin-block: 0 1.5em; margin-inline: 0; padding-inline-start: 1.25em; /* Naranja de marca como acento fino, nunca como relleno ni como texto. */ border-inline-start: var(--stroke-medium) solid var(--brand); color: var(--muted-foreground); font-size: var(--font-size-lg); line-height: var(--line-height-lg); } .prose blockquote > :last-child { margin-block-end: 0; } /* ---- Codigo ---- */ .prose code { font-family: var(--font-family-mono); font-size: 0.9em; background-color: var(--muted); color: var(--foreground); border-radius: var(--radius-sm); padding: 0.15em 0.4em; } .prose pre { margin-block: 0 1.5em; padding: var(--spacing-4); background-color: var(--muted); border-radius: var(--radius-md); overflow-x: auto; } .prose pre code { background-color: transparent; padding: 0; font-size: var(--font-size-sm); line-height: var(--line-height-sm); } /* ---- Tabla ---- Los legales traen tablas (el de suscripcion trae una de suspension por plan). El componente `table` del DS es para data-tables con su propio markup; aqui basta la version de lectura, sobre etiquetas desnudas. */ .prose table { margin-block: 0 1.5em; border-collapse: collapse; font-size: var(--font-size-sm); line-height: var(--line-height-sm); /* Una tabla ancha no puede desbordar el ancho del host ni forzar scroll horizontal a la pagina: scrollea ella misma. width max-content + max-width 100% para que no se estire cuando cabe (display:block la saca del algoritmo de tabla para el ancho, las filas siguen siendo filas). OJO: max-content hace que el minimo de contenido de la tabla sea su ancho completo. Si el contenedor no acota ese minimo (min-width:0 en .prose, o minmax(0,1fr) en la rejilla), la pagina entera se ensancha. */ display: block; width: max-content; max-width: 100%; overflow-x: auto; } /* Las celdas parten palabras largas en vez de estirar la tabla; con max-content arriba, una celda que no parte hace que ninguna columna se ajuste. */ .prose :is(th, td) { overflow-wrap: break-word; } .prose :is(th, td) { padding: var(--spacing-3); border-block-end: var(--stroke-hairline) solid var(--border); text-align: start; vertical-align: top; } .prose th { font-weight: var(--font-weight-semibold); color: var(--foreground); } .prose td { color: var(--muted-foreground); } /* ---- Otros ---- */ .prose hr { margin-block: 2em; border: none; border-block-start: var(--stroke-hairline) solid var(--border); } .prose :is(img, video) { max-width: 100%; height: auto; border-radius: var(--radius-lg); } .prose dt { font-weight: var(--font-weight-semibold); } .prose dd { margin-inline-start: 0; margin-block-end: 1em; color: var(--muted-foreground); } .prose > :last-child { margin-block-end: 0; } @media (prefers-reduced-motion: reduce) { .prose a { transition-duration: 0ms; } }