Components Reference
Forty-five primitives in src/components/ui/, each a folder with an index.ts and a tailwind-variants recipe. Nineteen are used by real pages; the other twenty-six ship for you to reach for. /examples/ui renders every one in every variant.
Basics
Button · Input · Label · Textarea · Badge · Card · Alert · Separator · Skeleton · Avatar
Card is compound — Image, Header, Title, Description, Action, Content and Footer parts, with variant and size.
Disclosure and navigation
Accordion (Item/Trigger/Content) · Tabs (List/Trigger/Content) · Tooltip · Breadcrumb (Item/Link/Page/Separator) · Pagination (Item/Link/Ellipsis) · Progress · Spinner · Nav · MegaMenu (Trigger/Panel/Item) · List (+ Item) · Collapse
Accordion is <details>. Tooltip is CSS-only. Only Tabs ships a script, and it degrades to all-panels-visible. PaginationLink reuses the button config rather than redefining one.
MegaMenu is a Dropdown with a wide multi-column panel, placed by the shared _popover.ts controller. It is click-to-open only — hover triggers are hostile to touch and keyboard. columns (1, 2 or 3) sets width and column count.
Collapse grows a region from 0fr to 1fr so no height is ever hard-coded. Two things make that work and neither is discoverable: the grid item needs min-h-0, and it must carry no padding of its own. See Components.
Overlays
Dialog (Trigger/Close/Header/Title/Description/Footer) · Sheet · Dropdown (Trigger/Menu/Item)
Dialog and Sheet are native modal <dialog> — a Sheet is a Dialog pinned to an edge by a side variant, reusing Dialog’s trigger, close and content parts. They share one delegated controller that binds once and survives view transitions. Entry and exit animations, the backdrop scrim and the modal scroll-lock live in _overlay.css as real @starting-style transitions that honour prefers-reduced-motion.
Dropdown is the native Popover API, so the menu renders in the top layer and is never clipped, with native light-dismiss, Escape and focus return, plus arrow-key roving from the shared controller.
Form controls
Select · Checkbox · Radio · Switch · Slider · InputNumber · ToggleCount (+ Value) · PasswordInput / PasswordStrength · ComboBox (+ Option) · AdvancedSelect · Searchbox (+ Item) · Table (Header/Body/Footer/Row/Head/Cell/Caption)
Select is a native <select> reusing the shared _field look. Checkbox, Radio and Switch are native inputs styled appearance-none with peer and :checked — zero JS. Slider is a native range styled through its pseudo-elements.
PasswordStrength is a four-segment meter driven by a rule-based scorePassword, with a runnable check beside it. ComboBox is a role="combobox" input over a filterable listbox with aria-activedescendant. AdvancedSelect is backed by a real visually-hidden native <select> for form submission and requires JavaScript — the zero-JS alternative is Select. Searchbox is a ⌘K palette reusing the Dialog shell.
Table is static styling in a scroll wrapper; sorting and data-grid behaviour are out of scope.
Motion and design primitives
Reveal · SplitFlip · RollText · RollLink · RollIcon · Marquee · Pill · ChipGroup · ThemeToggle
Reveal is the reveal-on-scroll wrapper, driven by the native scroll timeline (animation-timeline: view()), so it is zero-JS. It follows siteSettings.useAnimations — off, it is a plain pass-through — and carries motion-reduce:animate-none, because the global reduced-motion guard zeroes time durations but cannot stop a scroll-driven animation.
SplitFlip is reveal-on-scroll for text: it splits a string into characters or whole words and turns each in on the Y axis, staggered, as the text scrolls into view. It is the GSAP SplitText recipe with no GSAP — the turn is the catalog’s swivel-in keyframe and the stagger is per-unit animation-range slices of one named view timeline. Text stays real text: units are grouped per word inside a whitespace-nowrap box so a line can only break at a space, and chars mode hides the character spans behind an sr-only copy of the intact string. text is a prop rather than a slot precisely so the server can split it. The maths live in split-flip/stagger.ts with a check beside them.
RollText is the hover label roll — every character exits upward while its duplicate rolls in from below, staggered left to right. Pure CSS on the nearest Tailwind group, split at build time, intact label kept sr-only. RollLink composes Reveal and RollText into the shape three home sections were hand-writing, with an optional rule variant for the header’s retracting-rule motif. The header’s own nav links are deliberately not folded in — different scale, fixed-dark colours, and an aria-current state.
Marquee is a seamless-loop scroll whose speed is a per-instance variable. Each track shifts one full copy width plus one gap, so with identical copies side by side the loop has no seam — which is why --marquee-gap must match the flex gap.
ThemeToggle is the light/dark override. Its sun/moon flip is CSS-only via the dark: variant, so it is correct pre-paint with no flash; only the click ships JavaScript. It is the one primitive that is not purely additive — it pairs with the inline pre-paint script in BaseHead, which must stay inline or the theme flash comes back.
Shared internals
Not primitives, and not exported: _dialog.ts, _popover.ts, _disclosure.ts, _field.ts, _listbox.ts, _client.ts, _Chevron.astro, _overlay.css, _accordion.css, and password/strength.ts.
_client.ts exports onReady, the load-plus-astro:after-swap re-init contract every scripted primitive shares. The three delegated controllers do not need it — they bind once at the document level.
What the site actually uses
Nineteen: accordion, alert, button, card, chip-group, collapse, input, label, nav, pill, reveal, roll-icon, roll-link, roll-text, select, separator, split-flip, textarea and theme-toggle.
The other twenty-six are the reason /examples/ui exists. Once you have chosen, delete the catalog — it costs 18,803 bytes of shared CSS and 64 unused @keyframes. See Layout.
Sections and Cards
Cards — ProjectCard, NoteCard, ServiceCard, PartnerCard, TestimonialCard, GalleryPlate.
Global sections — Header, Footer, PageTransition, PageHero, Pager, Faq, Testimonials, Partners, FounderQuote, NoteGrid, LayoutToggle.
Page sections — Home/ (8), Blog/ (6), Contact/ (6), Project/ (6), About/ (4), Services/ (4), Work/ (2), NotFound/ (2), Legal/ (1), UiCatalog/ (9, dev only).
Where the contracts are
src/components/ui/README.md is the primitive contract and the per-primitive notes. src/components/Sections/README.md and src/components/Cards/README.md are the other two tiers. The repo’s wiki/subsystems/ui-primitives.md carries the longer rationale, including the gotchas each primitive paid for.