Reference
19 Nov 2026
20 min read

SpaceZero Component Library: Complete Reference Guide

Complete reference for all 20+ SpaceZero UI components with props, variants, and usage examples.

SpaceZero Component Library Reference

Complete reference for all SpaceZero UI components.

Design Principles

  1. Constrained: Only design tokens from src/index.css
  2. Composable: Sub-components for flexible composition
  3. Accessible: WCAG 2.1 AA, keyboard navigation, ARIA
  4. Touch-Friendly: Generous padding on interactive elements
  5. Mobile-First: Fluid by default, responsive prefixes used sparingly

Core Components

Button (`@/components/ui/button`)

tsx

import { Button } from '@/components/ui/button';

 

<Button variant="action" href="/path">Link Button</Button>

<Button variant="default" onClick={handler}>Default</Button>

<Button variant="destructive" disabled>Deleting...</Button>

Props:

PropTypeDefaultDescription
variant'default', 'action', 'success', 'warning', 'destructive''default'Visual style
hrefstring-Renders as <a>
disabledbooleanfalseDisables interaction
classNamestring-Extra classes

There is no loading prop — use disabled plus your own label while a request is in flight.


Card (`@/components/ui/card`)

tsx

import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@/components/ui/card';

 

<Card>

<CardHeader>

<CardTitle><Icon name="Server" /><span>Feature</span></CardTitle>

<Badge>New</Badge>

</CardHeader>

<CardDescription>Description text</CardDescription>

<CardContent>Main content</CardContent>

<CardFooter>Footer actions</CardFooter>

</Card>

Sub-components:

  • Card - Container with border/bg
  • CardHeader - Header section
  • CardTitle - Title with icon support
  • CardDescription - Muted description
  • CardContent - Body content
  • CardFooter - Footer area

Hero (`@/components/ui/hero`)

tsx

import { Hero, HeroTitle, HeroDescription } from '@/components/ui/hero';

 

<Hero>

<Badge>Tag</Badge>

<HeroTitle>Hero Title</HeroTitle>

<HeroDescription>Description text</HeroDescription>

</Hero>

Guidance: HeroTitle uses text-5xl with font-hero for all headings.


Input (`@/components/ui/input`)

tsx

import { Input } from '@/components/ui/input';

 

<Input

type="email"

placeholder="Enter email"

value={email}

onChange={e => setEmail(e.target.value)}

togglePassword

required

/>

Props:

PropTypeDescription
togglePasswordbooleanShows eye icon for password (default true)
All standard input props

Padding: py-2 px-3 (0.5rem / 0.75rem).


Textarea (`@/components/ui/textarea`)

tsx

import { Textarea } from '@/components/ui/textarea';

 

<Textarea

placeholder="Enter text..."

value={text}

onChange={e => setText(e.target.value)}

rows={4}

/>

Touch Target: py-3 padding with min-h-16 (64px minimum height)


Select (`@/components/ui/select`)

tsx

import { Select } from '@/components/ui/select';

 

<Select

options={[

{ label: 'Option 1', value: '1' },

{ label: 'Option 2', value: '2', disabled: true },

]}

value={selected}

onValueChange={setSelected}

placeholder="Select..."

searchable

multiple

clearable

/>

Props:

PropTypeDescription
optionsSelectOption[]Required options array
value / defaultValuestringSingle select
values / defaultValuesstring[]Multi select
onValueChange(v: string) => voidSingle callback
onValuesChange(v: string[]) => voidMulti callback
searchablebooleanEnable search filter
multiplebooleanEnable multi-select with chips
clearablebooleanShow clear all button
placeholderstringSingle placeholder
multiplePlaceholderstringMulti placeholder
disabledbooleanDisable interaction

Touch Targets: Trigger uses py-2 px-3; options use py-2


tsx

import { Modal, ModalBackdrop, ModalDialog, ModalClose, ModalHeader, ModalIcon, ModalTitle, ModalDescription, ModalFooter, ModalBody } from '@/components/ui/modal';

import { Notice } from '@/components/ui/notice';

 

<Modal>

<ModalBackdrop />

<ModalDialog>

<ModalClose onClick={close}>

<Icon name="X" />

</ModalClose>

<ModalHeader>

<ModalIcon><Icon name="CheckCircle2" /></ModalIcon>

<ModalTitle>Success</ModalTitle>

</ModalHeader>

<ModalDescription>Operation completed.</ModalDescription>

<ModalBody>

<Notice>Additional info</Notice>

</ModalBody>

<ModalFooter>...</ModalFooter>

</ModalDialog>

</Modal>

Modal is a plain overlay — visibility is controlled by conditionally rendering it (no isOpen/onClose props).

Sub-components:

  • Modal - Fixed overlay container
  • ModalBackdrop - Blurred + tinted backdrop
  • ModalDialog - Dialog box (w-full max-w-full rounded-5 p-5 md:max-w-lg)
  • ModalClose - Close button (p-2)
  • ModalHeader - Centered header
  • ModalIcon - Icon container
  • ModalTitle - Title
  • ModalDescription - Description
  • ModalBody - Scrollable body
  • ModalFooter - Footer actions
  • Notice - Alert box (variant: warning/destructive)

Accordion (`@/components/ui/accordion`)

tsx

import { Accordion, AccordionItem } from '@/components/ui/accordion';

 

<Accordion defaultValue="item-1">

<AccordionItem value="item-1" title="What is this?">

<p>Explanation here</p>

</AccordionItem>

<AccordionItem value="item-2" title="How does it work?">

<p>Details here</p>

</AccordionItem>

</Accordion>


Tabs (`@/components/ui/tabs`)

tsx

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';

 

<Tabs defaultValue="overview">

<TabsList>

<TabsTrigger value="overview">Overview</TabsTrigger>

<TabsTrigger value="settings">Settings</TabsTrigger>

</TabsList>

<TabsContent value="overview"><p>Content</p></TabsContent>

<TabsContent value="settings"><p>Content</p></TabsContent>

</Tabs>


Badge (`@/components/ui/badge`)

tsx

import { Badge } from '@/components/ui/badge';

 

<Badge variant="default">Default</Badge>

<Badge variant="action">Action</Badge>

<Badge variant="success">Success</Badge>

<Badge variant="warning">Warning</Badge>

<Badge variant="destructive">Destructive</Badge>


Progress (`@/components/ui/progress`)

tsx

import { Progress } from '@/components/ui/progress';

 

<Progress value={75} max={100} label="Upload Progress" />


Spinner (`@/components/ui/spinner`)

tsx

import { Spinner } from '@/components/ui/spinner';

 

<Spinner size={24} />

Only allowed animation in SpaceZero.


Toast (`@/components/ui/toast`)

tsx

import { ToastProvider, useToast } from '@/components/ui/toast';

 

// Wrap app

<ToastProvider><App /></ToastProvider>

 

// In component

const { toast } = useToast();

toast.success('Saved!');

toast.destructive('Failed', 'Try again');

toast.warning('Warning');

toast.action('Info');


Icon (`@/components/ui/icon`)

tsx

import { Icon } from '@/components/ui/icon';

 

<Icon name="Server" size={20} color="var(--action)" />

56 icons available (Lucide + a few brand icons from react-icons). Change DEFAULT_ICON_SIZE to resize all globally.


tsx

import { Link } from '@/components/ui/link';

 

<Link href="/path" variant="default">Link</Link>

<Link href="/path" variant="small">Small</Link>

<Link href="/path" variant="muted">Muted</Link>

<Link href="/path" variant="header">Header</Link>

<Link href="mailto:email" variant="email">Email</Link>


Code (`@/components/ui/code`)

tsx

import { Code, CodeBlockTitle, CodeBlockItem, CodeInline } from '@/components/ui/code';

 

<Code>

<CodeBlockTitle><Icon name="Terminal" /><span>Setup</span></CodeBlockTitle>

<CodeBlockItem>Step 1</CodeBlockItem>

<CodeBlockItem>Step 2</CodeBlockItem>

</Code>

 

<CodeInline>npm install</CodeInline>


Article (`@/components/ui/article`)

tsx

import { Article, ArticleSection, ArticleHeading, ArticleParagraph, ArticleList, ArticleListItem, ArticleStrong } from '@/components/ui/article';

 

<Article>

<ArticleSection>

<ArticleHeading>Section</ArticleHeading>

<ArticleParagraph>Text with <ArticleStrong>bold</ArticleStrong>.</ArticleParagraph>

<ArticleList>

<ArticleListItem>Item 1</ArticleListItem>

<ArticleListItem>Item 2</ArticleListItem>

</ArticleList>

</ArticleSection>

</Article>


NewsletterForm (`@/components/ui/newsletter`)

tsx

import { NewsletterForm } from '@/components/ui/newsletter';

 

<NewsletterForm

title="Stay Updated"

description="Get the latest news"

buttonText="Subscribe"

/>


SkeletonLoader (`@/components/infra/skeleton-loader`)

tsx

import { SkeletonLoader, SkeletonPost } from '@/components/infra/skeleton-loader';

 

<SkeletonLoader className="h-5 w-full" />

<SkeletonPost />


Toggle (`@/components/ui/toggle`)

tsx

import { Toggle } from '@/components/ui/toggle';

 

<Toggle

checked={enabled}

onChange={setEnabled}

label="Enable feature"

variant="action"

/>

Props:

PropTypeDescription
checkedbooleanControlled state
onChange(checked: boolean) => voidChange handler
disabledbooleanDisable interaction
labelstringAccessible label
variant'default', 'action', 'success', 'warning', 'destructive'Track color

Uses role="switch".


PricingCard (`@/components/ui/pricing`)

tsx

import { PricingCard } from '@/components/ui/pricing';

 

<PricingCard

gumroadProductId="myapp"

priceDisplay="

0"

originalPriceDisplay="

3"

planName="Lifetime Access"

ctaText="Get Lifetime Access"

/>

All props default to appConfig.monetization. The CTA links to https://{gumroadProductId}.gumroad.com/l/{gumroadProductId}.


Auth (`@/components/ui/auth`)

tsx

import { Auth } from '@/components/ui/auth';

 

<Auth isOpen={open} onClose={close} />

Props:

PropTypeDescription
isOpenbooleanControls visibility
onClose() => voidClose handler
titlestringModal title
descriptionstringModal description

Renders OAuth (Google/GitHub) + email/password forms with password strength validation, backed by useAuth().


Layout Components

PageLayout (`@/components/layout/wireframe`)

tsx

import { PageLayout, PageMain } from '@/components/layout/wireframe';

 

<PageLayout>

<PageMain>Content</PageMain>

</PageLayout>

Section (`@/components/layout/section`)

tsx

import { Section } from '@/components/layout/section';

 

<Section cols={2}> {/* 1 or 2 */}

<Card>...</Card>

<Card>...</Card>

</Section>


Utility Components

ProtectedRoute (`@/components/infra/protected-route`)

tsx

import { ProtectedRoute } from '@/components/infra/protected-route';

 

<ProtectedRoute requireAuth redirect="/">

<PrivatePage />

</ProtectedRoute>

ErrorBoundary (`@/components/infra/error-boundary`)

tsx

import { ErrorBoundary } from '@/components/infra/error-boundary';

 

<ErrorBoundary>

<App />

</ErrorBoundary>


Hooks

useAuth (`@/hooks/useAuth`)

tsx

const {

user, session, loading,

signInWithOAuth, signInWithEmail, signUpWithEmail,

resetPassword, sendConfirmationEmail, checkUserExists,

updatePassword, signOut,

} = useAuth();

useToast (`@/components/ui/toast`)

tsx

const { toast } = useToast();

toast.success('Message');

toast.destructive('Message', 'Description');


Design Tokens Reference

Scaling (Tailwind → CSS Var)

ClassCSS VariableValue
0--scaling-00
1--scaling-10.25rem
2--scaling-20.5rem
3--scaling-30.75rem
5--scaling-51.25rem
9--scaling-92.25rem
12--scaling-123rem
16--scaling-165rem
48--scaling-4812rem
96--scaling-9624rem
lg--scaling-lg32rem
7xl--scaling-7xl5rem
full-100%
auto-auto
px-1px

Applies to every spacing/sizing utility: p-*, m-*, gap-*, w-*, h-*, insets.

Font Sizes

ClassTailwind DefaultValueUse
text-5xl3rem3remHero titles
text-lg1.125rem1.125remHeadings
text-base1rem1remBody text
text-xs0.75rem0.75remCaptions

Border Radius

ClassCSS VariableValue
rounded-0--radius-00
rounded-5--radius-50.5rem
rounded-full--radius-full9999px

Colors

CategoryTokens
Textregular, accent, warning, success, muted
Backgroundsurface, layer, action, codeblock
Borderborder
Inputinput
Destructivedestructive

Font Weight

Only font-semibold (600) allowed.


Usage Rules

  1. Import from `@/components/ui/` — never import Lucide directly
  2. Use sub-components — CardTitle not <h3 className="...">
  3. Responsive prefixes allowed, use sparingly — sm:, md:, lg:, xl:, 2xl: are permitted where a fluid layout cannot work
  4. Generous touch padding — interactive elements use py-2/py-3 scale tokens
  5. Colors via tokens — text-regular, bg-layer, border-border
  6. Font sizes via tokens — text-xs, text-base, text-lg, text-5xl
  7. Icon via `Icon` component — not direct Lucide imports
  8. Link via `Link` component — not raw <a>
  9. Button via `Button` component — supports href
  10. Input via `Input` component — not raw <input>