Skip to main content

Component examples

Existing UI APIs stay available through the compatibility layer. Use the primitives entry point for native Astryx APIs, and site components for page structure.

Design tokens

Choose colours by purpose. These swatches use the live shared theme and change with light and dark mode.

Surfaces

  • Canvas

    --color-background-body
  • Surface

    --color-background-surface
  • Muted surface

    --color-background-muted
  • Popover

    --color-background-popover

Text and borders

  • Primary text

    --color-text-primary
  • Secondary text

    --color-text-secondary
  • Disabled text

    --color-text-disabled
  • Border

    --color-border
  • Emphasized border

    --color-border-emphasized

Status

  • Success

    --color-success
  • Warning

    --color-warning
  • Error

    --color-error

Use semantic tokens

.example {
background: var(--color-background-surface);
color: var(--color-text-primary);
border: 1px solid var(--color-border);
}

Existing aliases such as --color-bg and --glass-border remain available for compatibility. Use semantic names in new code.

Typography

Suisse Intl gives headings their shape and keeps reading and controls clear; Geist Mono is reserved for code.

Headings · Suisse Intl

A useful idea

How it works

The details

Page, section and item roles. Choose the heading level for the document structure, independently of its visual size.

Reading and controls · Suisse Intl

Good documentation makes the next step clear. Use body text for explanations and supporting text for context that can stay quieter.

Supporting text uses the same family, with less visual emphasis.

Code · Geist Mono

<SiteHeading variant="section">How it works</SiteHeading>
<SiteText>Your content goes here.</SiteText>

Controls

Interactive examples of the existing UI compatibility API. Native Astryx props are available through @n3wth/ui/primitives.

Button

Variant:Size:
<Button variant="primary" size="md">
Button
</Button>

Badge

Variant:Size:
DefaultStatusv1.0
<Badge variant="default" size="sm">
Default
</Badge>

Input

Variant:Size:
<Input
variant="default"
inputSize="md"
placeholder="Type something..."
/>

Icon (Iconoir)

Size:Color:
arrow-right
arrow-left
arrow-up
arrow-down
chevron-right
chevron-left
chevron-up
chevron-down
check
x
copy
search
menu
sun
moon
external
github
terminal
code
plus
minus
settings
user
heart
star
mail
calendar
clock
bell
home
folder
file
trash
edit
eye
eye-off
lock
unlock
link
external-link
download
upload
refresh
filter
sort
grid
list
more-horizontal
more-vertical
info
warning
success
error
<Icon name="search" size="md" />
<Icon name="github" size="md" />

CodeBlock

import { Nav, Hero, Button, useTheme } from '@n3wth/ui'
import '@n3wth/ui/styles'
function App() {
const { theme, toggleTheme } = useTheme()
return (
<Nav
logo="myapp"
items={[
{ label: 'Home', href: '/' },
{ label: 'About', href: '/about' },
]}
theme={theme}
onThemeToggle={toggleTheme}
fixed
hideOnScroll
/>
)
}
// Quick start
npm install @n3wth/ui

Compositions

Interactive examples of the existing UI compatibility API. Native Astryx props are available through @n3wth/ui/primitives.

Card

Variant:
Padding:

Default Card

Basic border card

Cards contain content and actions about a single subject.

<Card variant="default">
<CardHeader>
<CardTitle>Default Card</CardTitle>
<CardDescription>
Basic border card
</CardDescription>
</CardHeader>
<CardContent>
<p>
Cards contain content and actions about a single subject.
</p>
</CardContent>
<CardFooter>
<Button size="sm" variant="secondary">
Action
</Button>
</CardFooter>
</Card>

Tabs

Variant:

Overview content with animated indicator that follows the active tab.

<Tabs value={tab} onChange={setTab} variant="underline">
<TabsList>
<TabsTab value="tab1">Overview</TabsTab>
<TabsTab value="tab2">Features</TabsTab>
</TabsList>
<TabsPanel value="tab1">Overview content</TabsPanel>
<TabsPanel value="tab2">Features content</TabsPanel>
</Tabs>

Modal

Size:
<Modal isOpen={open} onClose={() => setOpen(false)} size="md">
<ModalHeader>
<div>
<ModalTitle>Modal Title</ModalTitle>
<ModalDescription>Description text</ModalDescription>
</div>
<ModalCloseButton onClick={() => setOpen(false)} />
</ModalHeader>
<ModalBody>Content here</ModalBody>
<ModalFooter>
<Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
<Button onClick={() => setOpen(false)}>Confirm</Button>
</ModalFooter>
</Modal>

Toast

Variant:
// Standalone
<Toast
variant="success"
title="Toast Title"
description="Toast description"
duration={5000}
onDismiss={() => setShow(false)}
/>
// With useToast hook + Provider
const { toast } = useToast()
toast.success({ title: 'Done!', description: 'Action completed' })

NavLink

Variant:
<NavLink href="/about" variant="underline" isActive>
About
</NavLink>

CommandBox

npm install @n3wth/ui
<CommandBox command="npm install @n3wth/ui" />

ThemeToggle

Current: dark
const { theme, toggleTheme } = useTheme()
<ThemeToggle theme={theme} onToggle={toggleTheme} size="md" />

Site patterns

Page structure belongs to the shared UI layer. The live shell uses these components.

Navigation and footer

import { SiteNavigation, SiteFooter } from '@n3wth/ui/site'
<SiteNavigation brand={<a href="/">My site</a>}
links={<a href="/work">Work</a>} />
<SiteFooter sourceHref="https://github.com/n3wth/n3wth" />

Hero and sections

import { PageHeader, SiteSection, SiteHeading, SiteText } from '@n3wth/ui/site'
<PageHeader title="Work" description="Selected projects."
actions={<a href="/resume.pdf">Resume (PDF)</a>} />
<SiteSection>
<SiteHeading>Projects</SiteHeading>
<SiteText>What each project helps people do.</SiteText>
</SiteSection>

Decorative visual bands

import { AssembleField, VisualBand } from '@n3wth/ui/visuals'
import '@n3wth/ui/site.css'
<VisualBand height="clamp(190px, 34svh, 340px)">
<AssembleField clusters={[[530, 132], [645, 284], [762, 158]]}
width={900} height={400} />
</VisualBand>

Hooks

React hooks for theme, media queries, accessibility, and animations.

useTheme

Current theme:dark
import { useTheme } from '@n3wth/ui'
function ThemeSwitch() {
const { theme, setTheme, toggleTheme } = useTheme()
return (
<button onClick={toggleTheme}>
{theme === 'dark' ? 'Switch to Light' : 'Switch to Dark'}
</button>
)
}

useMediaQuery / useIsMobile / useBreakpoint

Mobile: YesTablet: NoDesktop: No

Current breakpoint: xs

import { useIsMobile, useBreakpoint, useMediaQuery } from '@n3wth/ui'
function ResponsiveLayout() {
const isMobile = useIsMobile() // < 768px
const isTablet = useIsTablet() // 768-1023px
const isDesktop = useIsDesktop() // >= 1024px
const breakpoint = useBreakpoint() // 'sm' | 'md' | 'lg' | 'xl' | '2xl'
const isWide = useMediaQuery('(min-width: 1440px)')
return (
<div className={isMobile ? 'stack' : 'grid-cols-3'}>
{isWide && <Sidebar />}
<Main />
</div>
)
}

useReducedMotion

prefers-reduced-motion:Reduced
import { useReducedMotion } from '@n3wth/ui'
function AnimatedCard() {
const prefersReducedMotion = useReducedMotion()
return (
<div
className={prefersReducedMotion ? 'opacity-100' : 'animate-fade-in'}
style={{ transition: prefersReducedMotion ? 'none' : 'all 0.3s ease' }}
>
Accessible animation
</div>
)
}

useCountUp

0
import { useCountUp } from '@n3wth/ui'
function StatsCounter() {
const { value, ref } = useCountUp(1000, {
duration: 2,
onScroll: false,
})
return <span ref={ref}>{value}</span>
}

useKeyboardShortcuts

Register keyboard shortcuts with modifier keys. Handles platform differences (Cmd vs Ctrl) automatically.

import { useKeyboardShortcuts, getModifierKey } from '@n3wth/ui'
function SearchDialog() {
const [open, setOpen] = useState(false)
const modKey = getModifierKey() // 'Cmd' | 'Ctrl'
useKeyboardShortcuts([
{
key: 'k',
modifiers: ['meta'],
handler: () => setOpen(true),
description: 'Open search',
},
])
return <span>Press {modKey}+K to search</span>
}

Motion policy

Keep route content visible immediately. Existing animation hooks are compatibility tools for deliberate demonstrations and functional feedback, not a default page treatment.