jixoai-ui
the terminal design language
loading the style sheet…
Skip to content
docs · print medium: screen

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.

install — terminal-footer

$npx jixoai-ui add terminal-footer

https://ui.jixoai.com/r/terminal-footer.json

composition-first free link children text-stroke recipe aria-hidden · unselectable declarative theme

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).

A real footer renders here — composed from columns with free links, staged inline at the canvas width. The ghost scales with the viewport (11vw between clamps), so narrow the window and watch the wordmark breathe.

↓ a live footer, rendered directly — the stage is its viewport

types

Types

One column part, two postures: titled (an uppercase eyebrow over the stack) or untitled (a bare link stack).

titled columntitle + free link children
untitled stackomit title for 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.

TerminalFooter usage
<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.

2xs
project
© 2026 jixoai · MIT
xs
project
© 2026 jixoai · MIT
sm
project
© 2026 jixoai · MIT
default
project
© 2026 jixoai · MIT
lg
project
© 2026 jixoai · MIT
TokenDefaultSource
ghost scaleclamp(3rem, 11vw, 9rem)component
-webkit-text-stroke1px border @ 55%color
@supports fallback35% border-tinted fillcolor
--motion-150 / --motion-ease-out0.15s ease-outstructural
shell measure90rem + auto marginscomponent
--jx-muted-foregroundlight literalcolor

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

PropertyTypeDefaultDescription
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

PropertyTypeDefaultDescription
size'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopethe base scale — root font-size; parts size in em
shape'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto''auto' · ambient scopecorner geometry (CSS corner-shape; §14 degrade table)
radius'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopecorner size; auto = the concentric broadcast (§3)
density'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopespacing/leading scale over the kernel channels (§4)
color'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string'auto' · ambient scopethe hue axis of the fixed oklch primary system (§5)
theme'light' | 'dark' | 'system' | 'auto''auto' · ambient scopelight/dark profile; system = the JS-mutable global (§6)
elevation'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number'auto' · ambient scopeofficial M3 levels over the surface ladder (§7)
motion'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number'auto' · ambient scopeintensity 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.

PropertyTypeDefaultDescription
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.

one real query() case
<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>
project
© 2026

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.

TerminalFooter · universal props

project
© 2026
terminal-footer-query-demo.svelte svelte
<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>

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

KeyAction
TabMoves through the column links in composed order — the free anchors are real links, focusable by default

ARIA

AttributeValueDescription
footer (implicit)contentinfo landmarkThe root is a real <footer> element — zero ARIA authored; screen readers list it as the page contentinfo
aria-hiddentrueOn the ghost wordmark, with user-select: none — decorative by construction: pure sign-off, never information
column titlesspans, not headingsThe uppercase eyebrows carry no heading semantics — the landmark and its links carry the structure
target / relyoursExternal-link attributes are authored by the caller on the free anchors
hovercolor-onlyThe 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)
jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h