@renge-ui/react
Components
18 components built on the token system. Proportional. Accessible. Composable. No class names — every style references a Renge CSS variable.
pnpm add @renge-ui/tokens @renge-ui/reactStack
Flexbox container. Composes vertical and horizontal layouts using Fibonacci spacing tokens.
Vertical (default)
Horizontal + justify between
Horizontal + align center
<Stack gap="4">
<div>First</div>
<div>Second</div>
<div>Third</div>
</Stack>
<Stack direction="horizontal" justify="between">
<Text>Left</Text>
<Text>Right</Text>
</Stack>
{/* Flex wrap via style prop */}
<Stack direction="horizontal" gap="3" style={{ flexWrap: "wrap" }}>
<Badge>one</Badge>
<Badge>two</Badge>
<Badge>three</Badge>
</Stack>| Prop | Type | Default | Description |
|---|---|---|---|
| gap | "0" – "10" | "3" | Gap between children — Fibonacci spacing step (e.g. step 4 = 20px). |
| direction | "vertical" | "horizontal" | "vertical" | flex-direction: column or row. |
| align | "start" | "center" | "end" | "stretch" | "stretch" | align-items value. |
| justify | "start" | "center" | "end" | "between" | "around" | "start" | justify-content value. |
| as | ElementType | "div" | Rendered HTML element (div, ul, nav, etc.). |
Grid
CSS grid container with Fibonacci gap tokens. Accepts a number of equal columns or a custom template string.
3-column equal grid
Golden ratio split (1fr 1.618fr)
<Grid columns={3} gap="3">
<div>1</div>
<div>2</div>
<div>3</div>
</Grid>
{/* Custom template — e.g. golden ratio split */}
<Grid columns="1fr 1.618fr" gap="4">
<div>Narrow</div>
<div>Wide (φ)</div>
</Grid>
{/* Per-axis gaps */}
<Grid columns={2} gapX="5" gapY="3">
...
</Grid>| Prop | Type | Default | Description |
|---|---|---|---|
| columns | number | string | 1 | Number of equal columns, or a CSS grid-template-columns string. |
| rows | number | string | — | Row count or grid-template-rows string. |
| gap | "0" – "6" | "3" | Gap between all cells. |
| gapX | "0" – "6" | — | Column gap — overrides gap. |
| gapY | "0" – "6" | — | Row gap — overrides gap. |
| align | "start" | "center" | "end" | "stretch" | "stretch" | align-items value. |
| justify | "start" | "center" | "end" | "stretch" | "stretch" | justify-items value. |
Section
Page section wrapper with max-width constraint and automatic horizontal centering.
Centered with md max-width
<Section maxWidth="lg" paddingY="8" paddingX="5">
<Heading>Page content</Heading>
<Text>Body copy sits inside the max-width container.</Text>
</Section>
{/* Full-bleed with internal constraint */}
<div style={{ background: "var(--renge-color-bg-subtle)" }}>
<Section maxWidth="xl" paddingY="7">
<Heading>Wide section</Heading>
</Section>
</div>| Prop | Type | Default | Description |
|---|---|---|---|
| maxWidth | "sm" | "md" | "lg" | "xl" | "full" | "none" | "lg" | sm=640px · md=768px · lg=1024px · xl=1280px · full=100% · none=unset. |
| paddingX | "0" – "8" | "4" | Horizontal padding. |
| paddingY | "0" – "8" | "6" | Vertical padding. |
| padding | "0" – "8" | — | Sets both axes at once. Overrides paddingX and paddingY. |
| center | boolean | true | Applies margin-inline: auto to center the container. |
| animation | AnimationName | — | Renge animation token. |
| as | ElementType | "section" | Rendered HTML element. |
Heading
Semantic heading using the PHI-derived type scale. Level maps to h1–h6 and sets a proportional default size.
Levels 1–4 with default sizing
Level 1 heading
h1 → 3xlLevel 2 heading
h2 → 2xlLevel 3 heading
h3 → xlLevel 4 heading
h4 → lgColor variants
Default — var(--renge-color-fg)
Subtle — var(--renge-color-fg-subtle)
Accent — var(--renge-color-accent)
<Heading level={1}>The golden ratio.</Heading>
<Heading level={2} color="accent">Section title</Heading>
{/* Override the default size for a level */}
<Heading level={3} size="2xl">Larger h3</Heading>
{/* PHI-derived line heights apply automatically */}
{/* h1/h2 → display (1.236), h3/h4 → heading (1.382) */}| Prop | Type | Default | Description |
|---|---|---|---|
| level | 1 | 2 | 3 | 4 | 5 | 6 | 2 | Semantic HTML level (h1–h6). Also sets the default size. |
| size | "lg" | "xl" | "2xl" | "3xl" | "4xl" | level-mapped | Override the default size. h1→3xl, h2→2xl, h3→xl, h4/5/6→lg. |
| color | "fg" | "fg-subtle" | "accent" | "fg" | Text color semantic token. |
| animation | AnimationName | — | Renge animation token. |
Text
Inline or block text element with full access to the PHI type scale, color tokens, and weight options.
Size scale
Colors + weights
<Text size="lg" color="fg-subtle">Natural proportion.</Text>
<Text as="p" weight="medium" size="base">
Fibonacci spacing.
</Text>
<Text size="xs" color="accent" weight="semibold">
PHI = 1.618033...
</Text>
{/* Block-level text */}
<Text as="p" size="base" color="fg-subtle">
Paragraph text using the base size — 16px, line-height 1.618 (φ).
</Text>| Prop | Type | Default | Description |
|---|---|---|---|
| size | "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "base" | Font size token. Steps derived from PHI: base × φ^n. |
| color | "fg" | "fg-subtle" | "fg-muted" | "accent" | "success" | "warning" | "danger" | "fg" | Text color semantic token. |
| weight | "normal" | "medium" | "semibold" | "bold" | "normal" | Font weight: 400 / 500 / 600 / 700. |
| align | "left" | "center" | "right" | — | Text alignment. |
| animation | AnimationName | — | Renge animation token. |
| as | ElementType | "span" | Rendered HTML element. |
Divider
Horizontal or vertical rule rendered as an <hr>. Uses border color tokens for consistent visual weight.
Horizontal variants
Between sections
Below — stronger border, tighter spacing
Vertical
Right content
<Divider />
<Divider color="border" spacing="2" />
<Divider orientation="vertical" spacing="4" />| Prop | Type | Default | Description |
|---|---|---|---|
| orientation | "horizontal" | "vertical" | "horizontal" | Horizontal: 1px height, full width. Vertical: 1px width, full height. |
| spacing | "0" – "6" | "3" | Margin around the rule (marginBlock or marginInline). |
| color | "border" | "border-subtle" | "border-subtle" | Line color token. |
Input
Text input with Renge sizing and validation state styling. Focus ring is handled with inline event handlers to stay in-system.
States
Sizes
<Input placeholder="Enter value" size="md" />
<Input state="error" placeholder="Invalid input" fullWidth />
<Input state="success" defaultValue="Confirmed" fullWidth />
{/* Controlled */}
<Input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Controlled input"
fullWidth
/>size attribute (a number controlling character width) is excluded to avoid collision with Renge's size prop. Use style={{ width: "..." }} or fullWidth for sizing.| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "md" | Controls padding and font size. |
| state | "default" | "error" | "success" | "default" | Sets border color to the corresponding semantic token. |
| fullWidth | boolean | false | Stretches input to 100% container width. |
FormField
Wraps any input with a label, optional helper text, and error text. Error overrides helper when both are present.
States
<FormField
label="Email"
htmlFor="email"
helperText="We'll never share this."
>
<Input id="email" type="email" fullWidth />
</FormField>
<FormField
label="Username"
htmlFor="username"
required
errorText="Username already taken."
>
<Input id="username" state="error" fullWidth />
</FormField>| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | — | Label text rendered in a <label> element. |
| htmlFor | string | — | Links the label to an input via its id. |
| helperText | string | — | Muted hint text below the input. |
| errorText | string | — | Danger-colored error text. Overrides helperText when both are set. |
| required | boolean | — | Appends a red asterisk to the label. |
Card
Surface container with three visual variants. Padding and radius are token-driven.
Variants
Padding scale
<Card variant="elevated" padding="5" radius="3">
<Heading level={3}>Card title</Heading>
<Text color="fg-subtle">Supporting copy goes here.</Text>
</Card>
<Card variant="outlined" padding="4">
<Stack gap="3">
<Badge variant="success">Active</Badge>
<Heading level={3}>Status card</Heading>
</Stack>
</Card>| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "elevated" | "outlined" | "filled" | "elevated" | elevated: drop shadow · outlined: border · filled: bg-subtle background. |
| padding | "0" – "6" | "4" | Internal padding using Fibonacci spacing tokens. |
| radius | "none" | "1" – "5" | "full" | "3" | Border radius token (radius-3 = 12px by default). |
| animation | AnimationName | — | Renge animation token. |
Badge
Compact inline label for status, category, or count. Six semantic variants map directly to the color token system.
Variants
Sizes
<Badge variant="success">Published</Badge>
<Badge variant="warning" size="sm">Beta</Badge>
<Badge variant="danger">Deprecated</Badge>
<Badge variant="info">New</Badge>
<Badge variant="accent" size="lg">Featured</Badge>| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "neutral" | "accent" | "success" | "warning" | "danger" | "info" | "neutral" | Determines background and text color via semantic token pairs (e.g. success-subtle + success). |
| size | "sm" | "md" | "lg" | "md" | Controls padding and font size. |
Chip
Dismissible tag with the same semantic color variants as Badge. Provide onDismiss to render a close button.
Variants
Dismissible (live demo)
{/* Static */}
<Chip variant="accent">Design system</Chip>
{/* Dismissible */}
<Chip variant="neutral" onDismiss={() => removeTag(id)}>
Removable
</Chip>
{/* Dynamic tag list */}
{tags.map(tag => (
<Chip
key={tag.id}
variant="accent"
onDismiss={() => removeTag(tag.id)}
>
{tag.label}
</Chip>
))}| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "neutral" | "accent" | "success" | "warning" | "danger" | "info" | "neutral" | Color semantic — same token mapping as Badge. |
| onDismiss | () => void | — | When provided, renders a × button. Call your own state removal logic here. |
Avatar
User avatar with image or initials fallback. Sizes follow the Fibonacci sequence × 4px.
Sizes — Fibonacci × 4px
Shapes + image
{/* Initials fallback */}
<Avatar initials="RG" size="3" />
{/* Image with initials fallback */}
<Avatar
src="/user.jpg"
alt="Profile photo"
initials="RG"
size="4"
/>
{/* Square shape */}
<Avatar initials="FB" shape="square" size="3" />| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | — | Image URL. Takes priority over initials if provided. |
| alt | string | — | Image alt text and aria-label for the avatar. |
| initials | string | — | Up to 2 characters, displayed when src is absent or fails to load. |
| size | "1" | "2" | "3" | "4" | "5" | "3" | 20 / 32 / 52 / 84 / 136 px — Fibonacci[3–7] × 4. |
| shape | "circle" | "square" | "circle" | circle: full border-radius · square: radius-3 (12px). |
Stat
Key metric display. Trend badges are color-coded via success/danger tokens. Value renders at 3xl — designed to be read at a glance.
Metrics
<Stat value="φ" label="Golden ratio" />
<Stat
value="89"
label="Fibonacci step"
trend="up"
trendValue="+34"
caption="from step 9"
/>
<Stat
value="0ms"
label="Build time delta"
trend="neutral"
trendValue="no change"
/>| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | number | — | Primary metric — rendered at font-size-3xl. |
| label | string | — | Small label above the value. |
| trend | "up" | "down" | "neutral" | — | up=success · down=danger · neutral=fg-muted. Requires trendValue to render the badge. |
| trendValue | string | — | Change amount shown in the trend badge (e.g. '+34', '-2ms'). |
| caption | string | — | Muted caption rendered below the value row. |
Alert
Contextual banner with left-border accent and semantic status. Renders with role="alert" for screen reader compatibility.
All statuses
Without title
<Alert status="info" title="Note">
Informational context.
</Alert>
<Alert status="success" title="Deployed">
Production build complete.
</Alert>
<Alert status="danger" title="Error">
Something went wrong.
</Alert>
{/* No title — body only */}
<Alert status="warning">
Approaching rate limit.
</Alert>| Prop | Type | Default | Description |
|---|---|---|---|
| status | "info" | "success" | "warning" | "danger" | "info" | Sets left-border color and background using semantic token pairs. |
| title | string | — | Bold heading inside the alert. Optional — body renders alone if omitted. |
Spinner
Animated loading indicator. The CSS keyframe (rengeSpinnerSpin) is injected once at module load — no runtime overhead per instance.
Sizes
Colors
<Spinner size="md" color="accent" label="Loading tokens" />
<Spinner size="sm" color="fg-muted" />
{/* In a button */}
<Button variant="outline" disabled>
<Stack direction="horizontal" gap="2" align="center">
<Spinner size="sm" color="accent" />
<span>Saving...</span>
</Stack>
</Button>| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "md" | 16 / 24 / 32 px diameter. |
| color | "accent" | "fg" | "fg-muted" | "accent" | Spinner ring color token. |
| label | string | "Loading" | aria-label for screen readers. |
Progress
Linear progress bar. Value is clamped 0–100. Track and fill colors reference semantic tokens.
PHI-derived values
<Progress value={61.8} color="accent" />
<Progress value={100} color="success" size="lg" />
<Progress value={30} color="danger" radius="none" />
{/* Controlled */}
<Progress
value={uploadProgress}
color="accent"
size="md"
label="Upload progress"
/>| Prop | Type | Default | Description |
|---|---|---|---|
| value | number | — | 0–100 fill percentage. Values outside range are clamped. |
| color | "accent" | "success" | "warning" | "danger" | "accent" | Fill color semantic token. |
| size | "sm" | "md" | "lg" | "md" | Track height: 4 / 8 / 12 px. |
| radius | "none" | "full" | "full" | Track border radius. |
| label | string | — | aria-label for accessibility. |
Token API
@renge-ui/tokens exports two ways to consume tokens: createRengeTheme() for generating CSS, and rengeVars for typed CSS variable references.
createRengeTheme(config?)
Generates a complete token set from mathematical first principles. Returns CSS ready to inject and a JS vars map.
import { createRengeTheme } from "@renge-ui/tokens";
const theme = createRengeTheme({
profile: "ocean", // 'ocean' | 'earth' | 'twilight' | 'fire' | 'void' | 'leaf'
mode: "light", // 'light' | 'dark'
baseUnit: 4, // Spacing multiplier in px — try 6 for denser UIs
typeBase: 16, // Root font size in px
scaleRatio: 1.618, // Typography scale ratio (φ = golden ratio)
variance: 0, // 0–1 tolerance drift (0 = exact math, deterministic)
selector: ":root", // CSS selector to wrap the variables in
});
// theme.css — full :root { --renge-* ... } string, ready to inject
// theme.vars — Record<string, string> of every --renge-* variable
// theme.config — resolved config with all defaults appliedrengeVars
A statically typed object of CSS variable references. No runtime dependency — use it anywhere you need a var(--renge-*) string with IDE autocomplete.
import { rengeVars } from "@renge-ui/tokens";
// Color (22 semantic tokens, profile-reactive)
rengeVars.color.bg // "var(--renge-color-bg)"
rengeVars.color.bgSubtle // "var(--renge-color-bg-subtle)"
rengeVars.color.fg // "var(--renge-color-fg)"
rengeVars.color.accent // "var(--renge-color-accent)"
rengeVars.color.danger // "var(--renge-color-danger)"
rengeVars.color.borderFocus // "var(--renge-color-border-focus)"
// Spacing (Fibonacci × baseUnit, steps 0–10)
rengeVars.space[0] // "var(--renge-space-0)" → 0px
rengeVars.space[3] // "var(--renge-space-3)" → 12px (Fib[3] × 4)
rengeVars.space[5] // "var(--renge-space-5)" → 32px
// Typography (PHI scale, 8 steps)
rengeVars.fontSize.base // "var(--renge-font-size-base)"
rengeVars.fontSize.lg // "var(--renge-font-size-lg)"
rengeVars.lineHeight.base // "var(--renge-line-height-base)"
// Motion (Fibonacci × 100ms, steps 0–9)
rengeVars.duration[2] // "var(--renge-duration-2)" → 200ms
rengeVars.duration[5] // "var(--renge-duration-5)" → 800ms
rengeVars.easing.out // "var(--renge-easing-ease-out)"
rengeVars.easing.spring // "var(--renge-easing-spring)"
// Radius (Fibonacci × baseUnit, steps none/1–5/full)
rengeVars.radius[2] // "var(--renge-radius-2)" → 8px
rengeVars.radius.full // "var(--renge-radius-full)" → pillIntegrating with another system
Use rengeVars to map Renge tokens to your own CSS variable names. This is the recommended pattern for adopting Renge alongside an existing design system.
import { createRengeTheme, rengeVars } from "@renge-ui/tokens";
const theme = createRengeTheme({ profile: "earth", mode: "light" });
// Map semantic Renge tokens → your system's variable names
const aliases: [string, string][] = [
// Backgrounds
["--color-bg-primary", rengeVars.color.bg],
["--color-bg-secondary", rengeVars.color.bgSubtle],
["--color-surface", rengeVars.color.bgMuted],
// Foreground
["--color-text", rengeVars.color.fg],
["--color-text-muted", rengeVars.color.fgSubtle],
// Interactive
["--color-primary", rengeVars.color.accent],
["--color-primary-hover",rengeVars.color.accentHover],
// Status
["--color-error", rengeVars.color.danger],
["--color-success", rengeVars.color.success],
["--color-warning", rengeVars.color.warning],
// Spacing bridge
["--space-sm", rengeVars.space[2]],
["--space-md", rengeVars.space[4]],
["--space-lg", rengeVars.space[5]],
];
const aliasCSS = `:root {
${aliases.map(([k, v]) => ` ${k}: ${v};`).join("\n")}
}`;
// Inject Renge base vars first, then your aliases on top
document.head.insertAdjacentHTML("beforeend",
`<style>${theme.css}\n${aliasCSS}</style>`
);All 22 semantic color tokens
| rengeVars key | CSS variable | Role |
|---|---|---|
| color.bg | --renge-color-bg | Page background |
| color.bgSubtle | --renge-color-bg-subtle | Slightly elevated surface |
| color.bgMuted | --renge-color-bg-muted | Muted surface |
| color.bgInverse | --renge-color-bg-inverse | Inverted background |
| color.fg | --renge-color-fg | Primary text |
| color.fgSubtle | --renge-color-fg-subtle | Secondary text |
| color.fgMuted | --renge-color-fg-muted | Placeholder / disabled |
| color.fgInverse | --renge-color-fg-inverse | Text on inverse bg |
| color.border | --renge-color-border | Default divider |
| color.borderSubtle | --renge-color-border-subtle | Hairline divider |
| color.borderFocus | --renge-color-border-focus | Keyboard focus ring |
| color.accent | --renge-color-accent | Primary interactive |
| color.accentHover | --renge-color-accent-hover | Hover state |
| color.accentSubtle | --renge-color-accent-subtle | Tinted background |
| color.success | --renge-color-success | Positive outcome |
| color.successSubtle | --renge-color-success-subtle | Success tint bg |
| color.warning | --renge-color-warning | Caution |
| color.warningSubtle | --renge-color-warning-subtle | Warning tint bg |
| color.danger | --renge-color-danger | Error / destructive |
| color.dangerSubtle | --renge-color-danger-subtle | Danger tint bg |
| color.info | --renge-color-info | Informational |
| color.infoSubtle | --renge-color-info-subtle | Info tint bg |
Animations
15 named animations derived from the token system. Apply via the animation prop on any component that accepts it.
Available tokens
Live examples
breathe
floatpulse{/* On any component that accepts animation prop */}
<Heading level={2} animation="breathe">
Living proportion.
</Heading>
<Badge variant="accent" animation="pulse">
New
</Badge>
<Card animation="bloom">
<Text>Enters with bloom effect</Text>
</Card>
{/* Via CSS variable directly */}
<div style={{ animation: "var(--renge-animation-float)" }}>
Floats gently.
</div>createRengeTheme(). All 15 @keyframes blocks are included in the theme output — no separate import needed. Durations reference Fibonacci-derived --renge-duration-* tokens.Patterns
Common composition patterns using @renge-ui/react components.
Status card
Build status
PassingLast run 2 minutes ago
Form with validation
Metric grid
{/* Every component uses only CSS custom properties */}
{/* Switch profiles and all colors update instantly */}
import { createRengeTheme } from "@renge-ui/tokens";
const theme = createRengeTheme({ profile: "twilight" });
// Inject theme.css and all components adapt — no re-render.var(--renge-*) CSS custom properties. Switch color profiles by injecting a new theme block — no component re-renders required.