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.
svelte-kit-vice/web/routes/blocks/site-footer/+page.svelte

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>

Powered by TurnKey Linux.