Button
Button or button-styled link with variants, sizes, loading state and optional leading/trailing icons.
Component library
Accessible, themeable Astro components with zero JavaScript by default, and React versions for islands where you need them. A generated reference tells your AI agent what each one does, so it reuses them instead of writing its own.
The library
Listed from the same registry the CLI installs from and your agent reads. Pick a category, or open any one in the live demo.
Buttons and native form controls with labels, hints and errors.
Button or button-styled link with variants, sizes, loading state and optional leading/trailing icons.
Text input with built-in label, hint, error message and optional leading/trailing icons.
Multi-line text field with built-in label, hint and error message.
Native <select> built from an options array, with label, placeholder, hint and error.
Native checkbox with label, optional description and error message.
Single native radio input with label and description; give a group the same name. The card variant renders a selectable card.
Accessible on/off toggle (a checkbox with role="switch") with label and description.
Cards, badges, tables and the rest of the content furniture.
Surface container with variants and padding; becomes a block link when given href.
Small inline label for status, tags or counts, with semantic color variants, a pill style and a pulsing dot.
Circular user image that falls back to initials (from fallback or alt) when there is no src.
Overlapping stack of Avatars that collapses extras into a "+N" count.
Responsive data table rendered from columns and rows arrays, with hover, striped and compact options.
Page navigation (<nav>) with previous/next links and a window of numbered pages built from baseUrl.
Progress bar with semantic color variants and optional percentage label; omit value for an indeterminate bar.
Animated placeholder block, circle or text line for content that is still loading.
Consent-aware Google Maps embed by coordinates or address; shows a setup prompt without PUBLIC_GOOGLE_MAPS_API_KEY and a placeholder until consent.
Dialogs, menus, tabs and disclosure, with keyboard support.
Accessible modal with focus trap, Escape/backdrop close, title, description and a footer slot. Open it with window.openDialog(id).
Menu of links or actions (with icons, separators and disabled items) opened from a trigger slot, with keyboard support.
Horizontal tabs with keyboard navigation; panels are children marked data-tab-content="<tab id>".
Side-list tabs with optional descriptions and icons that turn into a dropdown or bottom sheet on mobile; panels use data-tab-content.
Collapsible sections built on native <details>/<summary> (works without JS), with an exclusive single-open mode.
Cookie consent banner and settings dialog with Google Consent Mode v2; renders only when PUBLIC_CONSENT_ENABLED is true.
Alerts, toasts and tooltips.
Inline message box (info, success, warning, error) with an icon, optional title and dismiss button.
React toast notifications: ToastProvider renders the stack and useToast() returns toast()/dismiss(). Toast.astro mounts the provider as an island around its slot.
Text tooltip shown on hover and focus around a trigger passed in the default slot.
Landing page pieces: logo, CTA band, install command.
The site's logo, rendered inline from the SVGs in src/assets/branding/ (logomark or full), with light/dark versions.
Closing call-to-action band centered, with logo, heading, description and actions slots (each rendered only when filled).
Button that shows a shell command and copies it to the clipboard, with "copied" feedback.
Row of overlapping avatar circles (images or placeholders) next to a short trust line.
React island that animates a terminal replaying the create-velocity-astro CLI prompts. Takes no props.
Compositions of the primitives: forms, sections, empty states.
Complete contact form (name, email, subject, message) submitting to the `contact` Astro Action or an external endpoint, with localized labels, validation and status.
Inline email signup form submitting to the `newsletter` Astro Action or an external endpoint, with localized status messages.
Label, hint, error and required marker around a custom control passed in the input slot.
Search field (type="search") built from Input with a leading search icon; used by the Pagefind search page.
Password field built from Input with a show/hide toggle button.
Metric card with label, value, optional icon and an up/down/neutral trend line.
Centered placeholder for empty lists or results: icon, title, description and an optional action button or slot.
Page section with standard spacing and width, an optional eyebrow/title/lead heading block, action slot and tinted tone.
Opening band for inner pages with eyebrow, title and lead; holds the page's only <h1>.
Optimized image from src/assets/photos/<photo> at a fixed aspect ratio, or a labelled placeholder until the photo exists.
Site header and footer, plus the separator.
Horizontal or vertical divider line, optionally with a centered label.
Site header with logo, nav from nav.config.ts, CTA, action buttons, theme toggle, language switcher and mobile menu; bar or floating capsule.
Site footer in simple, columns, minimal or stacked layouts, with nav, link groups, social and legal links, tagline and copyright.
The page-opening section, centered or split.
Page-opening hero section, centered or split, with badge, title, description, actions and aside slots plus optional grid and blob backgrounds.
The icon set and the code block.
Inline SVG icon from a built-in set of Lucide paths (plus a few brand icons), sized by preset.
Static code display with a window header, optional filename, line numbers and a copy button.
New in 2.4
Every project ships .agents/components.md, a reference to
all 46 components: what each is for, props with defaults, CVA variants, slots, an example and what
to use instead.
It is built from the registry and the component files. Velocity’s tests fail when it is stale, so it can’t drift from the code.
“Use instead” notes steer agents to what exists: Button with href instead of a styled <a>, Card with href instead of a Card wrapped in a link.
The add-page and add-component skills open it first, before reading component source or creating anything new.
## Button Button or button-styled link with variants, sizes, loading state and optional leading/trailing icons. - Import: `import Button from '@/components/ui/form/Button/Button.astro';` ### Props | Prop | Type | Default | | `variant` | `'primary' \| 'secondary' \| …` | `'primary'` | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | | `href` | `string` | | … ### Example <Button variant="primary" size="lg" trailingIcon="arrow-right" href="/contact">Get in touch</Button> <Button variant="outline" type="submit" loading>Saving</Button> ### Use instead - Use Button with href for links styled as buttons, not a styled <a>; external URLs get target="_blank" and rel automatically. - Use icon (with size) for square icon-only buttons, and give them an aria-label.
Themes
Default and Midnight ship with the kit, each with light and dark modes. Components only read semantic tokens, so
switching is one line in src/styles/theme.css.
/* src/styles/theme.css */ @import './themes/default.css'; /* themes/default.css, excerpt */ :root { --primary: var(--gray-900); --accent: var(--brand-500); --ring: var(--gray-900); --theme-heading-weight: 700; --theme-radius-md: 0.375rem; }
/* src/styles/theme.css */ @import './themes/midnight.css'; /* themes/midnight.css, excerpt */ :root { --brand-500: oklch(58% 0.25 295); --primary: oklch(16% 0.03 285); --ring: var(--brand-500); --theme-heading-weight: 600; --theme-radius-md: 0.5rem; }
Variants
Each component keeps its styles in a class-variance-authority file. The Astro and React versions share it, and the agent reference lists its options.
<Button>Get started</Button> variant
size
export const buttonVariants = cva( ['inline-flex items-center justify-center gap-2', …], { variants: { variant: { primary: 'bg-primary text-primary-foreground hover:bg-primary/90 …', secondary: 'bg-secondary text-secondary-foreground border …', outline: 'border border-border bg-transparent text-foreground …', ghost: 'text-foreground-secondary hover:text-foreground …', link: 'text-foreground-secondary hover:text-foreground … hover:underline', destructive: 'bg-destructive text-destructive-foreground …', }, size: { sm: 'h-8 px-3 text-xs [&_svg]:h-4 [&_svg]:w-4', md: 'h-10 px-4 text-sm [&_svg]:h-5 [&_svg]:w-5', lg: 'h-12 px-5 text-base [&_svg]:h-5 [&_svg]:w-5', }, fullWidth: { true: 'w-full' }, icon: { true: 'rounded-[var(--theme-radius-button,…)]' }, }, compoundVariants: [{ icon: true, size: 'sm', class: 'h-8 w-8 px-0' }, …], defaultVariants: { variant: 'primary', size: 'md' }, });
Built right
What the starter tests on every change, not what we hope is true.
Velocity’s end-to-end suite runs axe for WCAG 2 A and AA on every page, on desktop and mobile. A serious or critical violation fails the build.
The same suite fails on any Content Security Policy violation, so no component depends on inline scripts or event handlers.
Dialog traps focus and closes on Escape. Dropdown, Tabs and VerticalTabs take arrow keys, Home and End. Accordion is native <details>.
When a visitor prefers reduced motion, a global rule finishes every animation and transition instantly.
Almost every component is an .astro file. JavaScript only ships where a component has to respond to the visitor.