You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
8.1 KiB
211 lines
8.1 KiB
<script lang="ts">
|
|
/**
|
|
* SiteFooter 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 SiteFooterSite from './SiteFooterSite.svelte';
|
|
import DocRow from '../_lib/DocRow.svelte';
|
|
|
|
let withSignup = $state(true);
|
|
let columns = $state(4);
|
|
let dir = $state<'ltr' | 'rtl'>('ltr');
|
|
|
|
const previewSrc = $derived(
|
|
`/blocks/site-footer/preview?signup=${withSignup}&columns=${columns}&dir=${dir}`
|
|
);
|
|
</script>
|
|
|
|
<BlockDemo
|
|
name="Site footer"
|
|
slug="$blocks/site-footer"
|
|
{previewSrc}
|
|
previewHeight="780px"
|
|
meta={[
|
|
{ key: 'compone', value: 'Section · Container · Grid · AutoGrid · Separator · Flex · Motion' },
|
|
{ key: 'landmark', value: 'footer (contentinfo)' },
|
|
{ key: 'forma', value: 'compound Column + slots' },
|
|
{ key: 'strings propias', value: '0' }
|
|
]}
|
|
>
|
|
{#snippet preview()}
|
|
<SiteFooterSite {withSignup} {columns} />
|
|
{/snippet}
|
|
|
|
{#snippet lede()}
|
|
El final de la página: la marca, las columnas de enlaces, una banda de alta opcional y la barra
|
|
inferior con lo legal, lo social y lo que el app quiera aparcar ahí. Es el block donde la
|
|
<strong>regla de forma del tier</strong> se ve mejor: <Code>Column</Code> se repite, así que es parte
|
|
compound; lo demás no se repite ni coordina, así que son slots.
|
|
{/snippet}
|
|
|
|
{#snippet controls()}
|
|
<Wrap gap={5}>
|
|
<Group gap={2} align="center" justify="start">
|
|
<Text size="sm" color="muted">columnas</Text>
|
|
<ToggleGroup
|
|
selectionMode="single"
|
|
size="sm"
|
|
attached
|
|
value={[String(columns)]}
|
|
onValueChange={(v) => (columns = Number(v[0] ?? columns))}
|
|
aria-label="número de columnas"
|
|
>
|
|
<ToggleGroup.Item value="2">2</ToggleGroup.Item>
|
|
<ToggleGroup.Item value="3">3</ToggleGroup.Item>
|
|
<ToggleGroup.Item value="4">4</ToggleGroup.Item>
|
|
<ToggleGroup.Item value="5">5</ToggleGroup.Item>
|
|
</ToggleGroup>
|
|
</Group>
|
|
|
|
<Group gap={2} align="center" justify="start">
|
|
<Text size="sm" color="muted">banda de alta</Text>
|
|
<ToggleGroup
|
|
selectionMode="single"
|
|
size="sm"
|
|
attached
|
|
value={[withSignup ? 'sí' : 'no']}
|
|
onValueChange={(v) => (withSignup = (v[0] ?? 'sí') === 'sí')}
|
|
aria-label="banda de alta al boletín"
|
|
>
|
|
<ToggleGroup.Item value="sí">sí</ToggleGroup.Item>
|
|
<ToggleGroup.Item value="no">no</ToggleGroup.Item>
|
|
</ToggleGroup>
|
|
</Group>
|
|
|
|
<Group gap={2} align="center" justify="start">
|
|
<Text size="sm" color="muted">dir</Text>
|
|
<ToggleGroup
|
|
selectionMode="single"
|
|
size="sm"
|
|
attached
|
|
value={[dir]}
|
|
onValueChange={(v) => (dir = (v[0] ?? dir) as typeof dir)}
|
|
aria-label="dirección (solo la vista previa)"
|
|
>
|
|
<ToggleGroup.Item value="ltr">ltr</ToggleGroup.Item>
|
|
<ToggleGroup.Item value="rtl">rtl</ToggleGroup.Item>
|
|
</ToggleGroup>
|
|
</Group>
|
|
</Wrap>
|
|
{/snippet}
|
|
|
|
{#snippet composition()}
|
|
<Stack gap={4}>
|
|
<Text color="muted">El block coloca cinco regiones; el contenido de todas es del app.</Text>
|
|
<Stack gap={3}>
|
|
<DocRow term="región superior">
|
|
<Code>Grid</Code> de dos pistas (<Code>minmax(0, 1fr) minmax(0, 3fr)</Code>): la marca al
|
|
inicio, las columnas ocupando el resto. Apila en estrecho, donde dos pistas dejan las
|
|
columnas inservibles.
|
|
</DocRow>
|
|
<DocRow term="las columnas">
|
|
<Code>AutoGrid minChildWidth</Code> (def. <Code>7.5rem</Code>): tantas columnas como
|
|
caben,
|
|
<strong>sin un solo breakpoint</strong> en el fichero. Dos en un móvil, cinco en escritorio,
|
|
y el mismo código para 2 o para 5 grupos.
|
|
</DocRow>
|
|
<DocRow term="la banda de alta">
|
|
Su propia banda a lo ancho, bajo las columnas. En la columna de la marca (~230px) la fila
|
|
de correo tendría que apilarse; aquí respira. El formulario es del app —
|
|
<Code>Form</Code> + <Code>Field</Code> + <Code>Form.Submit</Code>—: este block
|
|
<strong>no importa el block <Code>newsletter</Code></strong> (B-10) ni inventa validación.
|
|
</DocRow>
|
|
<DocRow term="la barra inferior">
|
|
<Code>Separator</Code> decorativo y un <Code>Flex</Code> que reparte: lo legal al inicio, lo
|
|
social y el <Code>extra</Code> al final. Columna en estrecho.
|
|
</DocRow>
|
|
<DocRow term="la entrada">
|
|
Un <Code>Motion trigger="viewport"</Code> para la región superior y
|
|
<strong>ninguno</strong> para la barra inferior: una línea de copyright que aparece con fundido
|
|
es teatro. Y sin escalonado — nadie lee un pie columna por columna.
|
|
</DocRow>
|
|
</Stack>
|
|
</Stack>
|
|
{/snippet}
|
|
|
|
{#snippet api()}
|
|
<Stack gap={3}>
|
|
<DocRow term="SiteFooter">
|
|
<Code>containerSize</Code> (def. <Code>lg</Code>) · <Code>sectionSize</Code> (def. <Code
|
|
>lg</Code
|
|
>) ·
|
|
<Code>minChildWidth</Code> (def. <Code>7.5rem</Code>).
|
|
</DocRow>
|
|
<DocRow term="brand · signup · social · legal · extra">
|
|
Snippets. <Code>extra</Code> es el slot libre de D-BLK.6: los selectores de idioma / tema / región
|
|
del app. Pasa <Code>undefined</Code> y la región entera desaparece con su hueco.
|
|
</DocRow>
|
|
<DocRow term="SiteFooter.Column · .ColumnTitle">
|
|
Compound porque <strong>se repiten</strong>. <Code>Column</Code> envuelve un
|
|
<Code>Stack</Code> (gap 3) y sus hijos son los <Code>Link</Code> del app;
|
|
<Code>ColumnTitle</Code> es un <Code>Heading level={3} size="xs"</Code>.
|
|
</DocRow>
|
|
</Stack>
|
|
{/snippet}
|
|
|
|
{#snippet a11y()}
|
|
<Stack gap={3}>
|
|
<DocRow term="Landmark">
|
|
Un <Code>footer</Code> de verdad, así que es <Code>contentinfo</Code> sin pedir nada.
|
|
</DocRow>
|
|
<DocRow term="El nivel es estructura, el tamaño es tipografía">
|
|
El título de un grupo es <Code>Heading level={3}</Code> —un grupo del pie TIENE nombre, y las
|
|
referencias lo marcan igual— y se apaga con <Code>size="xs"</Code>, nunca bajando el nivel.
|
|
</DocRow>
|
|
<DocRow term="Iconos con nombre">
|
|
Cada <Code>IconButton</Code> lleva <Code>aria-label</Code> obligatorio (lo exige su tipo): un
|
|
glifo no es una etiqueta.
|
|
</DocRow>
|
|
<DocRow term="Separador decorativo">
|
|
<Code>decorative</Code>, porque el landmark ya dice dónde empieza la región. Un separador
|
|
anunciado sería ruido.
|
|
</DocRow>
|
|
</Stack>
|
|
{/snippet}
|
|
|
|
{#snippet gaps()}
|
|
<Stack gap={3}>
|
|
<DocRow term="Lista de enlaces como `ul`/`li`">
|
|
<strong>Canon</strong> — ningún primitivo de layout puede renderizar un elemento de lista:
|
|
<Code>Box</Code> y todo lo construido sobre él es un <Code>div</Code>, y solo los
|
|
tipográficos (<Code>Text</Code>, <Code>Heading</Code>) aceptan <Code>as</Code>. Una columna
|
|
es una pila de enlaces, no una lista marcada.
|
|
</DocRow>
|
|
<DocRow term="`nav` alrededor de las columnas">
|
|
<strong>Diferido</strong> — un <Code>nav</Code> necesita nombre accesible, y el nombre es una
|
|
cadena: tendría que entrar por el app. Entra cuando una demo lo pida; el
|
|
<Code>contentinfo</Code> ya agrupa.
|
|
</DocRow>
|
|
<DocRow term="Glifos de marca">
|
|
<strong>App-land</strong> — el framework no shippea marcas registradas. La demo usa iconos genéricos
|
|
(RSS, foro, correo).
|
|
</DocRow>
|
|
<DocRow term="Pie a sangre / con fondo propio">
|
|
<strong>App-land</strong> — el app envuelve el block en su <Code>Surface</Code>.
|
|
</DocRow>
|
|
</Stack>
|
|
{/snippet}
|
|
|
|
{#snippet notes()}
|
|
<Stack gap={3}>
|
|
<DocRow term="El selector de idioma está VIVO">
|
|
Es el hueco que nombraba el dossier, y no es una maqueta: escribe en
|
|
<Code>uix.prefs.setIntent('language', …)</Code>, el eje real del ecosistema. El block pone
|
|
el sitio; lo que cambia lo decide el app.
|
|
</DocRow>
|
|
<DocRow term="Los slots se pasan como props">
|
|
La demo declara los snippets arriba y los pasa por prop (<Code
|
|
>signup={'{withSignup ? signupBand : undefined}'}</Code
|
|
>), así que apagar la banda le entrega al block <em>ningún</em> snippet. Renderizar uno vacío
|
|
dejaría el hueco.
|
|
</DocRow>
|
|
</Stack>
|
|
{/snippet}
|
|
</BlockDemo>
|