An open source collection of accessible, contrast-gated React components on a warm neutral palette and a two-tier glass material — the VeloBits design system, authored once and shipped both as an npm package and as this registry.
The one sanctioned height animation: Radix measures --radix-accordion-content-height, so it does not thrash layout the way an unbounded height:auto transition would.
Status panel. Defaults to polite role="status"; escalate to "alert" deliberately. Tier-S glass on the neutral variant only , the four soft washes would replace the tint.
The authenticated chrome. `sidebar` is a prop rather than a child because it is rendered TWICE , as a rail at md and up, and inside a drawer below it , so a nav item cannot exist in one and not the other. Ships the skip link, which is the most-skipped WCAG requirement in an admin UI.
(No description)
Soft washes pair with the matching text token, never with the solid fill.
A named nav landmark around an ordered list. The leaf is a plain span with aria-current="page" , NOT shadcn’s role="link" aria-disabled, which announces static text as a broken link.
Controls are radius-md. No variant paints --primary as text; `link` uses --primary-text.
Tier-S glass by default; surface="panel" for the opaque original, which writes border-border explicitly so it survives an app that skipped the base layer.
Includes the indeterminate state bulk-selection headers need.
Tailwind-aware class merger. Signature must stay twMerge(clsx(...)) , the dashboard app points components.json `utils` at it.
Preformatted code, with a copy button that survives an insecure origin (navigator.clipboard is absent over plain http). The `terminal` variant is the theme-invariant --code/--on-code pair, which is what a one-time secret is revealed on.
cmdk inside a Radix Dialog. The ⌘K listener is opt-in via `shortcut` , a design system must not bind a global key by merely being imported.
Sorting, selection and row activation over a column registry , deliberately not TanStack, whose grouping and pivoting nothing here uses. aria-sort goes on the th, not the sort button, and an activatable row needs tabIndex + Enter/Space + a target check or it is mouse-only.
The centred form modal. `focusFirstField` exists because autoFocus is silently swallowed by Radix FocusScope.
A unified line diff. The +/− gutter is the primary channel and the green/red wash the secondary , colour alone fails 1.4.1, and the gutter is also the only channel that survives greyscale. Ships `diffLines`, with a guard: LCS is O(n·m) in MEMORY.
Highlight is data-[highlighted], never :hover , Radix drives keyboard focus through it. Cannot host a text input; use Dialog or Popover.
First-run, filtered-to-nothing and failed-to-load are three different states; this covers the first two and an Alert covers the third. The title is a <p> unless headingLevel is set, because an empty state usually sits inside a container that already has the heading.
Label/description/error wiring. Keeps BOTH ids in aria-describedby when a field has an error and a hint.
react-hook-form bound to Field’s ARIA wiring. label and description are PROPS, not children, because aria-describedby is assembled before children render , a description left out while its id is still listed is a dangling reference. NOT exported from the npm barrel: react-hook-form is an optional peer.
All three glass tiers behind one prop: surface (Tier S, no blur by default) · overlay · elevated. Never for page backgrounds, table rows, or nested glass.
border-input maps to --field-border, the half WCAG 1.4.11 gates.
(No description)
A real <label>, so htmlFor resolves. Non-control roots need aria-labelledby instead.
Page transitions and capped list staggers on framer-motion, which was already a required peer for VelobitsProvider’s MotionConfig and until now bought nothing else. Subpath-only in the npm package (@velobitsio/ui/motion), like form, because the barrel’s own-code budget has ~4 kB left and nobody should pay for Framer to import a Button. Reduced motion is handled by MotionConfig and the token layer , do NOT add a third path. Stagger caps at STAGGER_LIMIT items so a 200-row list does not take eight seconds to arrive.
A cva-styled native <select>. Deliberately NOT Radix Select, which is undriveable under happy-dom.
Numbers are links, previous/next are buttons: an <a> cannot express "unavailable" without ceasing to be focusable. Unavailability is aria-disabled plus a guard, so focus is not lost at the ends of the range. `paginationRange` returns a CONSTANT number of slots so the control never reflows as you page.
The elevated tier, for glass stacked on glass. PopoverTitle wires aria-labelledby , Radix content is role="dialog" with no Title part, so it is otherwise unnamed.
A scrollable region with a styled scrollbar. The thumb is --field-border, not --border: it is an interactive control, so 1.4.11 applies to it.
Radix ToggleGroup with a REAL disabled and a working accessible name , a div root makes an external htmlFor dangle, and pointer-events-none leaves the control focusable and unannounced.
Decorative, so it uses --border and is exempt from 1.4.11.
The anchored reading sheet. Deliberately NOT Dialog and deliberately does NOT redirect focus , the two focus policies are opposites, so do not merge them behind a `side` variant. The bottom variant needs a definite 75dvh.
aria-hidden; pair with ONE sr-only live region rather than announcing each block.
The name goes on the THUMB , Radix puts role="slider" there, so htmlFor and a root aria-label both dangle.
(No description)
On/off/partial/pending/archived as one chip. Every status ships a DISTINCT glyph, because colour alone fails 1.4.1 and on-versus-off is the distinction a control plane exists to make unambiguous. Composes Badge rather than re-deriving the soft-wash pairs.
Applies immediately. Thumb translation is mirrored explicitly for RTL.
Rows are never glass , one backdrop-filter per row repaints its own backdrop region on every scroll. Surface treatment belongs on the container.
(No description)
(No description)
React-free light/dark resolution, storage and the blocking init script. Storage key is required, never defaulted.
Variants use a logical border stripe, not a soft wash: bg-*-soft is a utility and beats --glass-bg from the components layer, flattening the tier.
TooltipProvider is required , Radix throws without it, and under Module Federation it must be a singleton.
Media query subscription plus usePrefersReducedMotion. Import breakpoints from @velobitsio/tokens rather than re-typing the pixel value, so the JS and CSS breakpoints cannot drift apart.
Row selection for DataTable. The selection is DERIVED , the stored set intersected with the rows on screen , so a bulk action can never point at a row the filter has hidden. Returns a stable object identity, which is what keeps DataTable’s memoised rows from re-rendering per keystroke.
ThemeProvider + useTheme. Honours the existing fmx_theme_mode and tf.theme keys.
The base style: tokens, the cn util, the provider stack, and every Tier-1 primitive, Tier-2 overlay and Tier-3 composite. Start here.
Mount once at the shell root: ThemeProvider + TooltipProvider + MotionConfig reducedMotion="user".
The full token layer as CSS variables. Every colour pair here is contrast-verified in both themes by the @velobitsio/tokens test suite.