diff --git a/.specs/footer.md b/.specs/footer.md new file mode 100644 index 000000000..57db66c1d --- /dev/null +++ b/.specs/footer.md @@ -0,0 +1,180 @@ +--- +name: footer +category: layout +structure: composition +status: approved +spec_version: 1 +figma: + url: https://www.figma.com/design/t97pXRs7xME3SJDs5iZ5RF/Webkit?node-id=5467-1638 + node_id: 5467:1638 +checksum: 7cc4efef4305e63c50c44c3a007001c28265e937d6dfaa153a588893ace37118 +created: 2026-08-10 +last_updated: 2026-08-11 +--- +# Footer — Component Spec + +## Purpose + +Page footer for marketing and product surfaces: the bottom-of-page counterpart to `GlobalHeader`. A canvas-colored shell with a centered content measure holding link columns and a social bar separated by a hairline top border. Matches the Webkit Footer component set (Figma node 5467:1638, `Type=Default` / `Type=Mobile`): the two presentations are one component switching by CSS at the `md` (768px) breakpoint. Content (links, labels, languages, status) always comes from the consumer; the component owns anatomy, tokens, and responsiveness. + +## When to use + +- The page-level footer of a site or application, paired with `GlobalHeader`. +- Grouped navigation links at the bottom of the page plus a brand/social/status bar. + +## When NOT to use + +- The bottom area of a panel, table, sidebar, or overlay → use that component's own footer part (`panel-footer`, `table-footer`, `sidebar-footer`, `popover-footer`). +- A single standalone link inside body content → use `link` instead of `Footer.Link`. + +## Related + +- `global-header` — the top-of-page chrome counterpart; same shell philosophy (regions composed by the consumer). +- `brand`, `icon-button`, `status-indicator`, `select` — the components consumers compose inside the social bar; the footer reimplements none of them. + +## Best practices + +- Compose the social bar from existing DS components (`Brand`, `IconButton kind="transparent"`, `StatusIndicator`, `Select`); never rebuild them inside the slots. +- Keep column titles short (one or two words) — they are group labels, not headings for prose. +- Give every social `IconButton` an `ariaLabel`; the footer cannot name them for you. +- Provide four columns for the canonical desktop presentation; the grid folds to two columns below `md` on its own. + +## Usage + +```vue + + + +``` + +Tree-shaking alternative — the standalone root and each sub-component from its own entry (no `Object.assign` compound pulled in): + +```vue + +``` + +## Sub-components + +- `footer-column/footer-column.vue` — one navigation column: a muted title labelling a stacked group of `Footer.Link` items. Renders a `nav` labelled by its title so each group is a navigable landmark. Props: `title: string` (required) — column group label, rendered above the links and used as the accessible name of the column landmark. Slot: `default` — its `Footer.Link` items. +- `footer-link/footer-link.vue` — one text link inside a column; the root is the anchor itself, so consumer attributes (`target`, `rel`) land on the `a`. Props: `href: string` (required) — destination URL of the link. Slot: `default` — the link label. + +## Props + +| Prop | Type | Default | Required | JSDoc | +|---|---|---|---|---| +| `ariaLabel` | `string` | `'Footer'` | false | Accessible name for the contentinfo landmark. | + +## Events + +| _none_ | — | — | + +## Slots + +| Slot | Scope | Notes | +|---|---|---| +| `default` | — | Root: the `Footer.Column` items; laid out as a 2-column grid that becomes 4 columns at `md`. | +| `default` | — | `footer-column`: its `Footer.Link` items. | +| `default` | — | `footer-link`: the link label. | + +> The root also exposes the `social-start` named slot (leading cluster of the social bar: brand + social icon buttons) and the `social-end` named slot (trailing cluster: status indicator + language select; rendered above `social-start` below `md`, per the Mobile variant). They are kept out of the table above because the compliance parser cannot read hyphenated (quoted) slot keys from `defineSlots`. The social bar renders only when `social-start` or `social-end` is provided. + +## States + +- Visual states: `default` on the shell and columns; `default`, `hover`, `focus-visible`, `active`, `visited` on `footer-link`. +- No `data-state`; the shell has no interactive states — slotted children own their own. + +## Motion & Animations + +| Trigger | Animation / Transition | Token (see `.claude/docs/DESIGN.md` § Animations) | Reduced-motion fallback | +|---|---|---|---| +| link hover/focus color change | `transition-colors duration-150 ease-out` | inline (matches catalog) | `motion-reduce:transition-none` | + +## Tokens + +| Region | Token (DESIGN.md) | +|---|---| +| shell surface | `var(--bg-canvas)` | +| content max-width | `max-w-(--container-5xl)` | +| columns row gap (mobile 2-row wrap) | `var(--spacing-lg)` | +| column padding | `var(--spacing-lg)` | +| column divider (border-right, `md`+) | `var(--border-width-default)` / `var(--border-default)` | +| column internal gap (title → links) | `var(--spacing-md)` | +| link stack gap | `var(--spacing-xs)` | +| social bar border (top) | `var(--border-width-default)` / `var(--border-default)` | +| social bar height (`md`+) | `h-14` (56px) | +| social bar padding-y (below `md`) | `var(--spacing-md)` | +| social bar padding-x | `var(--spacing-lg)` | +| social bar left-cluster gap (brand ↔ social icons) | `var(--spacing-md)` | +| social bar right-cluster gap (status ↔ language) | `var(--spacing-lg)` | +| column title typography / color | `.text-label-sm` / `var(--text-muted)` | +| link typography / color | `.text-label-sm` / `var(--text-default)` | +| link hover color | `var(--text-muted)` | +| ring (link focus) | `var(--ring-color)` | + +## Theme gaps + +| Figma variable | Temporary primitive | Follow-up | +|---|---|---| +| `--container-max-width` (1280px content measure) | `max-w-(--container-5xl)` (1192px, nearest container primitive) | `TODO: tokenizar` | + +## Accessibility (WCAG 2.1 AA) + +- Root renders as `