|
|
<script lang="ts">
|
|
|
/**
|
|
|
* Content section demo. Full-bleed on the page; device widths from `./preview`.
|
|
|
*/
|
|
|
import { Stack } from '$uix/eidos/components/stack';
|
|
|
import { Group } from '$uix/eidos/components/group';
|
|
|
import { Wrap } from '$uix/eidos/components/wrap';
|
|
|
import { Text } from '$uix/eidos/components/text';
|
|
|
import { Code } from '$uix/eidos/components/code';
|
|
|
import { ToggleGroup } from '$uix/eidos/components/toggle-group';
|
|
|
import BlockDemo from '../_lib/BlockDemo.svelte';
|
|
|
import ContentSectionSite from './ContentSectionSite.svelte';
|
|
|
import DocRow from '../_lib/DocRow.svelte';
|
|
|
import type { ContentWidth } from '$blocks/content-section';
|
|
|
import type { ContainerSize } from '$uix/eidos/components/container';
|
|
|
import type { TextMeasure } from '$uix/eidos/lib/types';
|
|
|
|
|
|
let measure = $state<TextMeasure>('normal');
|
|
|
// `responsive` is an axis of the demo, not a value of the prop: it shows what
|
|
|
// only a per-breakpoint value can say — edge to edge on a phone, contained
|
|
|
// from `md` up.
|
|
|
// Starts at the block's DEFAULT: a demo that silently overrides it would be
|
|
|
// showing something the block does not do on its own.
|
|
|
let figure = $state<ContentWidth | 'responsive'>('measure');
|
|
|
const figureProp = $derived(
|
|
|
figure === 'responsive' ? ({ base: 'full', md: 'wide' } as const) : figure
|
|
|
);
|
|
|
let wide = $state<ContainerSize>('lg');
|
|
|
|
|
|
const previewSrc = $derived(
|
|
|
`/blocks/content-section/preview?measure=${measure}&figure=${figure}&wide=${wide}`
|
|
|
);
|
|
|
</script>
|
|
|
|
|
|
<BlockDemo
|
|
|
name="Content section"
|
|
|
slug="$blocks/content-section"
|
|
|
{previewSrc}
|
|
|
previewHeight="1100px"
|
|
|
meta={[
|
|
|
{ key: 'compone', value: 'Section · Grid · Box · Prose · Heading · Text · Motion' },
|
|
|
{ key: 'forma', value: 'compound — Body y Media se repiten' },
|
|
|
{ key: 'anchuras', value: 'measure · wide · full' },
|
|
|
{ key: 'estado', value: 'ninguno — es layout' }
|
|
|
]}
|
|
|
>
|
|
|
{#snippet preview()}
|
|
|
<ContentSectionSite {measure} figure={figureProp} {wide} />
|
|
|
{/snippet}
|
|
|
|
|
|
{#snippet lede()}
|
|
|
Texto largo: una cabecera, la prosa del artículo y figuras que <strong
|
|
|
>se salen de la columna de lectura sin salirse del flujo</strong
|
|
|
>. Es la superficie de lectura de un post, un changelog o una página legal.
|
|
|
{/snippet}
|
|
|
|
|
|
{#snippet controls()}
|
|
|
<Wrap gap={5}>
|
|
|
<Group gap={2} align="center" justify="start">
|
|
|
<Text size="sm" color="muted">medida</Text>
|
|
|
<ToggleGroup
|
|
|
selectionMode="single"
|
|
|
size="sm"
|
|
|
attached
|
|
|
value={[measure]}
|
|
|
onValueChange={(v) => (measure = (v[0] ?? measure) as TextMeasure)}
|
|
|
aria-label="columna de lectura"
|
|
|
>
|
|
|
<ToggleGroup.Item value="narrow">narrow</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="normal">normal</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="wide">wide</ToggleGroup.Item>
|
|
|
</ToggleGroup>
|
|
|
</Group>
|
|
|
|
|
|
<Group gap={2} align="center" justify="start">
|
|
|
<Text size="sm" color="muted">la figura</Text>
|
|
|
<ToggleGroup
|
|
|
selectionMode="single"
|
|
|
size="sm"
|
|
|
attached
|
|
|
value={[figure]}
|
|
|
onValueChange={(v) => (figure = (v[0] ?? figure) as ContentWidth | 'responsive')}
|
|
|
aria-label="alcance de la figura"
|
|
|
>
|
|
|
<ToggleGroup.Item value="measure">measure</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="wide">wide</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="full">full</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="responsive">responsive</ToggleGroup.Item>
|
|
|
</ToggleGroup>
|
|
|
</Group>
|
|
|
|
|
|
<Group gap={2} align="center" justify="start">
|
|
|
<Text size="sm" color="muted">pista ancha</Text>
|
|
|
<ToggleGroup
|
|
|
selectionMode="single"
|
|
|
size="sm"
|
|
|
attached
|
|
|
value={[wide]}
|
|
|
onValueChange={(v) => (wide = (v[0] ?? wide) as ContainerSize)}
|
|
|
aria-label="ancho de la pista wide"
|
|
|
>
|
|
|
<ToggleGroup.Item value="md">md</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="lg">lg</ToggleGroup.Item>
|
|
|
<ToggleGroup.Item value="xl">xl</ToggleGroup.Item>
|
|
|
</ToggleGroup>
|
|
|
</Group>
|
|
|
</Wrap>
|
|
|
{/snippet}
|
|
|
|
|
|
{#snippet composition()}
|
|
|
<Stack gap={4}>
|
|
|
<Text color="muted">
|
|
|
El block pone la rejilla, la medida y la semántica del artículo. El HTML renderizado, la
|
|
|
media y las palabras son del app.
|
|
|
</Text>
|
|
|
<Stack gap={3}>
|
|
|
<DocRow term="por qué es una rejilla y no un Container">
|
|
|
Dentro de un <Code>Container</Code> nada puede ser MÁS ancho que él, así que una figura a sangre
|
|
|
habría que sacarla del artículo y ponerla de hermana — rompiendo el orden de lectura. La sección
|
|
|
es un
|
|
|
<Code>Grid</Code> cuya pista central es la medida, y cada parte dice hasta dónde llega: <Code
|
|
|
>3</Code
|
|
|
> · <Code>2 / 5</Code> · <Code>1 / -1</Code>. Ese escape es lo que separa a este block de
|
|
|
un <Code>Prose</Code> dentro de una caja.
|
|
|
</DocRow>
|
|
|
<DocRow term="la medida de Prose va APAGADA">
|
|
|
<Code>Body</Code> pasa <Code>measure={false}</Code> al <Code>Prose</Code>: la rejilla ya
|
|
|
posee el ancho y dos dueños de la misma medida se pelean — ganaría el más estrecho en
|
|
|
silencio, así que un <Code>Body</Code> ancho no lo sería.
|
|
|
</DocRow>
|
|
|
<DocRow term="todas las longitudes son tokens">
|
|
|
Las medidas son <Code>--measure-*</Code> (54 · 66 · 78ch) y la pista ancha es un <Code
|
|
|
>--container-width-*</Code
|
|
|
>
|
|
|
<strong>menos el padding de ese contenedor</strong>: un token de container es el ancho
|
|
|
EXTERIOR y lo que se ve alineado es la caja de CONTENIDO. En <Code>lg</Code> la pista ancha
|
|
|
son 992, no 1024, y así una figura que se sale cae justo sobre el texto de las secciones de
|
|
|
arriba y de abajo — sin eso sobresalía 16px por cada lado. Los canalones son
|
|
|
<Code>1fr</Code> con suelo en <Code>--container-padding-inline</Code>.
|
|
|
</DocRow>
|
|
|
<DocRow term="el pie de foto se queda estrecho">
|
|
|
Un <Code>figcaption</Code> al ancho de una imagen a sangre no se lee. Se limita a la medida
|
|
|
de lectura y se centra, así que cae justo bajo la prosa aunque su figura no lo esté — varias
|
|
|
referencias no lo hacen. Verificado en 3 viewports × 3 medidas × 3 anchuras: el pie tiene el
|
|
|
mismo ancho y la misma x que la prosa en las 18.
|
|
|
</DocRow>
|
|
|
<DocRow term="los huecos de columna no existen aquí">
|
|
|
<Code>gap</Code> separa también las COLUMNAS, y una parte que las cruza se lleva esos huecos
|
|
|
encima: medido, <Code>wide</Code> salía 1088 en vez de los 1024 del contenedor, y a 420px <Code
|
|
|
>full</Code
|
|
|
> llegaba a 532 dentro de una rejilla de 404 y hacía scrollear la página. Las columnas son un
|
|
|
instrumento de medida, no cosas puestas al lado — van con
|
|
|
<Code>rowGap</Code> y <Code>columnGap={0}</Code>.
|
|
|
</DocRow>
|
|
|
</Stack>
|
|
|
</Stack>
|
|
|
{/snippet}
|
|
|
|
|
|
{#snippet api()}
|
|
|
<Stack gap={3}>
|
|
|
<DocRow term="ContentSection">
|
|
|
<Code>measure</Code> (<Code>narrow · normal · wide</Code>, def.
|
|
|
<Code>normal</Code>) · <Code>wide</Code> (ancho de la pista ancha, def. un
|
|
|
<Code>--container-width-lg</Code>) · <Code>size</Code> · <Code>level</Code>, y los snippets
|
|
|
de cabecera <Code>eyebrow</Code> · <Code>title</Code> ·
|
|
|
<Code>lede</Code> · <Code>meta</Code>.
|
|
|
</DocRow>
|
|
|
<DocRow term="por qué aquí «wide» y «full» se parecen">
|
|
|
<Code>full</Code> llega al borde de <strong>su contenedor</strong>, y en esta galería el
|
|
|
contenedor es la columna de contenido — el raíl se lleva 240px, así que a 1280 quedan 1024 y <Code
|
|
|
>wide</Code
|
|
|
> ya mide eso. Medido en la ruta
|
|
|
<Code>preview</Code>, que es un documento propio: a 1600,
|
|
|
<Code>wide</Code> da 1024 contra <Code>full</Code> 1584. Baja la
|
|
|
<strong>pista ancha</strong> a <Code>md</Code> y la relación se ve aquí mismo; o abre la vista
|
|
|
previa.
|
|
|
</DocRow>
|
|
|
<DocRow term="los tres ejes son responsive">
|
|
|
<Code>measure</Code>, <Code>wide</Code> y el <Code>width</Code> de cada parte aceptan <Code
|
|
|
>ResponsiveProp</Code
|
|
|
>, como los props del propio ecosistema — se resuelven con <Code>eidos.resolve()</Code>,
|
|
|
igual que los de <Code>Box</Code>. Es lo que un valor único no puede decir:
|
|
|
<Code>{'{ base: "full", md: "wide" }'}</Code> = foto a sangre en móvil y contenida de <Code
|
|
|
>md</Code
|
|
|
> en adelante. Verificado contra el breakpoint canónico: a 767 va a sangre, a 768 se contiene.
|
|
|
</DocRow>
|
|
|
<DocRow term="ContentSection.Body">
|
|
|
Una tirada de prosa. Acepta lo de <Code>Prose</Code> (<Code>size</Code>) menos
|
|
|
<Code>measure</Code>, que lo gobierna la rejilla, más <Code>width</Code> por si esa tirada tiene
|
|
|
que salirse.
|
|
|
</DocRow>
|
|
|
<DocRow term="ContentSection.Media">
|
|
|
<Code>width</Code> (def. <Code>measure</Code> — la figura sigue al texto y salirse se pide) y
|
|
|
el snippet <Code>caption</Code>. Renderiza un <Code>figure</Code> de verdad y el pie como
|
|
|
<Code>figcaption</Code>: es la semántica de la cosa, no un párrafo que va debajo.
|
|
|
</DocRow>
|
|
|
</Stack>
|
|
|
{/snippet}
|
|
|
|
|
|
{#snippet a11y()}
|
|
|
<Stack gap={3}>
|
|
|
<DocRow term="La sección tiene nombre">
|
|
|
<Code><section aria-labelledby></Code> apuntando al título, así que el artículo es una región
|
|
|
navegable con nombre. El nivel del encabezado es un prop (<Code>level</Code>): la estructura
|
|
|
del documento la decide la página, no el block.
|
|
|
</DocRow>
|
|
|
<DocRow term="Figura y pie van atados">
|
|
|
<Code>figure</Code> + <Code>figcaption</Code> son elementos reales, no un
|
|
|
<Code>div</Code> con un texto debajo: un lector de pantalla anuncia el pie como lo que es. Es
|
|
|
la única superficie de a11y propia de un block — estructura de documento.
|
|
|
</DocRow>
|
|
|
<DocRow term="El orden de lectura no se rompe">
|
|
|
La figura a sangre sigue estando ENTRE las dos tiradas de prosa en el DOM. Con un contenedor
|
|
|
habría que sacarla fuera, y el orden de tabulación y de lectura dejarían de coincidir con lo
|
|
|
que se ve.
|
|
|
</DocRow>
|
|
|
</Stack>
|
|
|
{/snippet}
|
|
|
|
|
|
{#snippet gaps()}
|
|
|
<Stack gap={3}>
|
|
|
<DocRow term="Un Figure del canon">
|
|
|
<strong>Señalado</strong> — el canon tiene <Code>Image</Code> y
|
|
|
<Code>AspectRatio</Code>, pero no una primitiva de figura, así que el block pone el <Code
|
|
|
>figure</Code
|
|
|
>/<Code>figcaption</Code> él mismo. Con un segundo consumidor, eso es el disparador de promoción.
|
|
|
</DocRow>
|
|
|
<DocRow term="El margen del figure">
|
|
|
<strong><Code><style></Code> justificado</strong> (D-BLK.2) — el navegador da <Code
|
|
|
>margin: 1em 40px</Code
|
|
|
> y la fundación no trae reset de elementos, así que esos 40px se comerían la pista que la rejilla
|
|
|
acaba de asignar. Misma raíz que F18.
|
|
|
</DocRow>
|
|
|
<DocRow term="Notas al margen y citas destacadas">
|
|
|
<strong>Sin construir</strong> — una nota en la pista lateral es un patrón editorial real,
|
|
|
pero no lo ha pedido ningún consumidor;
|
|
|
<Code>Media width="wide"</Code> cubre el caso común.
|
|
|
</DocRow>
|
|
|
</Stack>
|
|
|
{/snippet}
|
|
|
|
|
|
{#snippet notes()}
|
|
|
<Stack gap={3}>
|
|
|
<DocRow term="No posee estado, y está bien">
|
|
|
Aquí no hay nada que coordinar: es layout. Inventarle una máquina a un block que solo coloca
|
|
|
es el error contrario al que arregló <Code>contact</Code>.
|
|
|
</DocRow>
|
|
|
<DocRow term="Un componente del canon dentro de la prosa">
|
|
|
El <Code>Callout</Code> de la segunda tirada conserva su propio estilo sin anotar nada: todas
|
|
|
las reglas de <Code>Prose</Code> se emiten a especificidad CERO, así que el <Code
|
|
|
>[data-callout]</Code
|
|
|
> les gana. Ninguna librería de referencia puede hacer esto — no poseen el markup de sus consumidores.
|
|
|
</DocRow>
|
|
|
</Stack>
|
|
|
{/snippet}
|
|
|
</BlockDemo>
|