registry:ui · Layout
terminal-footer — the ghost wordmark
Composition-first: the root is a real <footer> landmark, the meta row composes TerminalFooterColumn parts (a title plus FREE link children — the closed links[] data prop died with the data-driven form), and the ghost word is decorative by declaration — aria-hidden, unselectable, pure sign-off. The family carries no theme literal: the paint follows the page, and the axes are a first-time all-no-own contract.
$npx jixoai-ui add terminal-footer
https://ui.jixoai.com/r/terminal-footer.json
overview
Overview
The platform gives the landmark; the family gives the ghost recipe, the composed meta row, and a split theme story — all measured.
The semantics are the platform's and the composition law is the family's: the root is a
real <footer> (the contentinfo landmark —
zero ARIA wiring authored), and the meta row is not data. The old closed links[] prop died: columns compose as TerminalFooterColumn children — a title span plus
FREE anchor children the consumer authors, external-link attributes
(target/rel)
included. The component owns the shell (a 90rem measure with auto margins and
three-step padding seams — 16px base, 24px at 40rem, 32px at 64rem, measured), the ghost
recipe, and the column voices; the content is entirely yours.
The ghost is three declarations and an honesty clause. Scale: clamp(3rem, 11vw, 9rem) — measured 140.8px at a
1280 viewport (11vw below the cap) and 144px (the 9rem cap) at 1600. Hollow: transparent
fill with a 1px text-stroke of the border token at 55%. Fallback: @supports not (-webkit-text-stroke) swaps a 35%
border-tinted solid fill for engines without the stroke. Etiquette: aria-hidden + user-select: none — decorative by construction,
never information. And the theme story is a measured split: the stroke rides color-mix over var(--border),
so it re-derives under a .dark scope (black tint → white tint, measured) while the
meta and title inks keep their light --jx-muted-foreground (the --jx-* set is :root-only — frozen). One host lesson worth carrying: inside a
component-canvas stage a data-theme="light" island
re-pins the light profile, so the served demo stays black-stroked under page-dark while
the same rendered footer at body level re-derives white — location decides.
The eight axes are a FIRST-TIME all-no-own contract — zero effective-carrier readers
(grep receipt). Density stamps the rung and moves nothing (paint-invariant shell,
measured at the 2xs and lg rungs); size is an inherited echo (the free text scales with
the root voice, the ghost does not); shape/radius/color/elevation supply unread (flat
square chrome whose only brand paint is the link hover); motion rides one seam (the
hover's 150ms ease-out token — re-timed by injection, measured, and RM-safe since a color
change has no motion path); theme is declarative (above). Kinship: terminal-header and terminal-card (the dark-locked bezel twins — the
footer is the light-following sibling of the family), website-scaffold (the page shell this closes).
types
Types
One column part, two postures: titled (an uppercase eyebrow over the stack) or untitled (a bare link stack).
usage
Usage
Compose the footer from column parts; ghost and copyright are the shell's own strings — everything inside the columns is yours.
<script lang="ts">
import TerminalFooter from '@ui/terminal-footer/terminal-footer.svelte';
import TerminalFooterColumn from '@ui/terminal-footer/terminal-footer-column.svelte';
</script>
<!-- the meta row is composed: columns carry a title + FREE links -->
<TerminalFooter ghost="JIXOAI/UI" copyright="© 2026 jixoai · MIT">
<TerminalFooterColumn title="project">
<a href="https://github.com/jixoai/ui" target="_blank" rel="noreferrer">GitHub</a>
<a href="/r/registry.json">Registry JSON</a>
</TerminalFooterColumn>
<TerminalFooterColumn title="docs">
<a href="/docs.html">Docs</a>
</TerminalFooterColumn>
</TerminalFooter>theming
Density and tokens
Viewport-scaled chrome, not density-scaled: the ghost breathes by 11vw (density paint-invariant, measured at the 2xs and lg rungs), the paint is three text-stroke declarations with an @supports fallback, and the single transition rides a motion token a consumer can re-time.
| Token | Default | Source |
|---|---|---|
| ghost scale | clamp(3rem, 11vw, 9rem) | component |
| -webkit-text-stroke | 1px border @ 55% | color |
| @supports fallback | 35% border-tinted fill | color |
| --motion-150 / --motion-ease-out | 0.15s ease-out | structural |
| shell measure | 90rem + auto margins | component |
| --jx-muted-foreground | light literal | color |
api
API
The declared surface is 12 named props on the shell (ghost, copyright, children, class, style + the eight axes) and 3 on the column (title, children, class). The residual is NOT dropped — both components spread the rest onto their real elements (HTMLAttributes passthrough, 'color' withheld on the shell), so the footer is attribute-transparent: id, data-*, aria-* and handlers land on the landmark you composed.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
| ghost * | string | — | The ghost wordmark (decorative, aria-hidden, unselectable). |
| copyright | string | © {live year} | The © row text. |
| children * | Snippet | — | The meta row — compose TerminalFooterColumn parts. |
| class | string | '' | Class passthrough to the footer root. |
| style | string | '' | Style passthrough, joined after the axis carriers. |
| …rest | HTMLAttributes<HTMLElement> | — | Forwarded onto the <footer> element — the shell is attribute-transparent (id, data-*, aria-*, handlers; the color ATTRIBUTE is withheld, the color AXIS is not). |
| Column: title | string | — | The column heading (an uppercase eyebrow span); omit for an untitled link stack. |
| Column: children * | Snippet | — | FREE link children — anchors are yours to author (target/rel included). |
| Column: …rest | HTMLAttributes<HTMLDivElement> | — | Forwarded onto the column div. |
Universal props
| Property | Type | Default | Description |
|---|---|---|---|
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | the base scale — root font-size; parts size in em |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' · ambient scope | corner geometry (CSS corner-shape; §14 degrade table) |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | corner size; auto = the concentric broadcast (§3) |
| density | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | spacing/leading scale over the kernel channels (§4) |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' · ambient scope | the hue axis of the fixed oklch primary system (§5) |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' · ambient scope | light/dark profile; system = the JS-mutable global (§6) |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' · ambient scope | official M3 levels over the surface ladder (§7) |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' · ambient scope | intensity across the motion kernels (§8) |
axes
The eight axes on terminal-footer
The FIRST-TIME all-no-own contract, measured honestly: density stamps the root and moves nothing (paint-invariant shell — the measure and rhythm live in the family css, media seams and all); size is an inherited echo on the free text (the ghost stays clamped); shape/radius/color/elevation supply unread (flat square chrome, one brand hover); motion rides a single re-timable hover seam; theme is DECLARATIVE — no shell literal, the paint follows the page, and a canvas light-island can pin the demo.
| Property | Type | Default | Description |
|---|---|---|---|
| density | '2xs' | 'xs' | 'sm' | 'default' | 'lg' | 'auto' | number (+ the five legacy spellings) | 'auto' | MANAGED BUT PAINT-INVARIANT — the rung attr stamps the footer root (data-density), and the shell's measure + rhythm is fixed elsewhere: the padding steps are space tokens re-pinned by the sm/lg media seams and the meta gaps are fixed steps (measured an identical shell at the 2xs and lg rungs). Zero --jx-density-effective readers (grep receipt). Number unit: coefficient. |
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' | INHERITED ECHO, FREE TEXT ONLY — the §1 stamp sets the root font-size; NOTHING the family renders follows it: the © line, the column links and the titles all ride the metaRow atom's fixed --jx-text-small step (12.5/12.5/11px, measured CONSTANT under an 18px stamp — the earlier 30px receipt was the root font-size itself, not the © line). Only consumer-authored FREE text outside the family atoms inherits; the ghost ignores it too (clamp(3rem, 11vw, 9rem) viewport/root-relative, measured unmoved). Zero --jx-size-effective readers (grep receipt). Number unit: px. |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' | SUPPLY-ONLY — zero shape-channel readers (grep receipt: no corner-shape or factor consumer in ui/terminal-footer/); the shell is square chrome (measured border-radius 0). Number unit: none. |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' | SUPPLY-ONLY — zero radius-effective readers (grep receipt); the footer carries no border-radius to re-shape (measured 0px — flat chrome, no card surface). Number unit: px. |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' | SUPPLY-ONLY — the family's one brand paint (the free-link hover) reads var(--primary) straight from the legacy chain (measured muted → primary on hover), so the global brand-hue runtime moves it while the hue AXIS never reaches the component: zero --jx-color-effective readers (grep receipt). Quote the ink by lightness/chroma — the hue digits rotate with the runtime. Number unit: hue degrees. |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' | DECLARATIVE, AND THE HOST DECIDES — the family split: unlike the terminal bezel twins (header/card, whose shell-theme literal owns the name), the footer carries NO theme literal; theme="dark" only stamps the .dark class on the root. What re-derives is split and measured: the ghost's stroke rides color-mix over var(--border), so it re-derives under a .dark scope (1px oklab(0 0 0 / 0.55) → oklab(1 0 0 / 0.55)) while the meta/title ink keeps its light --jx-muted-foreground under the scope (the island tier of the alias-theme law — and at root-level dark the UNPINNED ink re-derives, the body-level clone reading the dark 0.8452). And the host lesson, pin site named: the component-canvas stage div[data-theme="light"].jx-light re-pins the light profile, so the served demo stays black-stroked under page-dark — the SAME rendered footer moved to body level re-derives white. Location decides; system/auto = tree inheritance. No number lane. |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' | SUPPLY-ONLY — zero elevation-carrier readers (grep receipt); the footer is flat page chrome (measured box-shadow none). Number unit: dp. |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' | ONE SEAM, RM-SAFE — the family's single transition is the free-link hover: var(--motion-150) var(--motion-ease-out) (measured 0.15s ease-out; re-timing the token re-times the hover — measured 2s under a --motion-150 injection, the seam governs). A color transition has no motion path, so it survives prefers-reduced-motion (measured 0.15s under RM emulation — no kill authored, none needed). Zero --jx-motion-effective readers (grep receipt). Number unit: coefficient. |
Receipts: the shell (90rem cap with auto margins — measured 1440px wide, centered, at a 1600 viewport; padding seams 16/24/32px at base/40rem/64rem) and the ghost (140.8px at 1280, 144px at 1600 — the 9rem cap; transparent fill, 1px stroke at 55%) were measured on this page's served DOM (probe, task 67); the theme split (stroke oklab(0 0 0 / 0.55) → oklab(1 0 0 / 0.55) under a .dark scope while the meta ink stayed oklch(0.3211 0 0); the canvas data-theme="light" island pinning the served demo; the same footer re-derived at body level) and the hover seam (0.15s ease-out muted → primary; 2s under a --motion-150 injection; 0.15s preserved under RM emulation) were measured the same way; the unread rows carry grep receipts over ui/terminal-footer/. The query() seat below rides the md viewport key (48rem) on the size lane.
<script lang="ts">
import TerminalFooter from '@ui/terminal-footer/terminal-footer.svelte';
import TerminalFooterColumn from '@ui/terminal-footer/terminal-footer-column.svelte';
import { query } from '@lib/universal-props-query.svelte';
</script>
<!-- below 48rem the base (13px root) applies; at 48rem+ the md case (18px)
wins — the free text (© line, links) echoes it, the ghost stays clamped -->
<TerminalFooter ghost="JIXOAI/UI" size={query({ md: 18 }, 13)}>
<TerminalFooterColumn title="project"><a href="/docs.html">Docs</a></TerminalFooterColumn>
</TerminalFooter>Media keys are min-width: below 48rem the base (13px root) applies; at 48rem and wider the md case wins (18px) — the free text (© line, links) echoes it while the ghost stays clamped. The number lane goes bare. Resize across 48rem.
a11y
Accessibility
The landmark is the platform's; the ghost is decorative by declaration; everything readable is composed by you — including the external-link attributes.
Keyboard
| Key | Action |
|---|---|
| Tab | Moves through the column links in composed order — the free anchors are real links, focusable by default |
ARIA
| Attribute | Value | Description |
|---|---|---|
| footer (implicit) | contentinfo landmark | The root is a real <footer> element — zero ARIA authored; screen readers list it as the page contentinfo |
| aria-hidden | true | On the ghost wordmark, with user-select: none — decorative by construction: pure sign-off, never information |
| column titles | spans, not headings | The uppercase eyebrows carry no heading semantics — the landmark and its links carry the structure |
| target / rel | yours | External-link attributes are authored by the caller on the free anchors |
| hover | color-only | The link warming is a color transition (measured 0.15s ease-out) — no motion path, so it survives prefers-reduced-motion; never the only affordance (focus and activation are native) |