@renge-ui/react

Components

44 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/react
forwardRefinline stylesCSS custom propertiesno class namesprofile-reactive

Stack

Flexbox container. Composes vertical and horizontal layouts using Fibonacci spacing tokens.

Vertical (default)

Item 1
Item 2
Item 3

Horizontal + justify between

LeftCenterRight

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>
PropTypeDefaultDescription
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.
asElementType"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

1
2
3
4
5
6

Golden ratio split (1fr 1.618fr)

1
φ
<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>
PropTypeDefaultDescription
columnsnumber | string1Number of equal columns, or a CSS grid-template-columns string.
rowsnumber | stringRow 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

maxWidth="md" — 768px, centered
<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>
PropTypeDefaultDescription
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.
centerbooleantrueApplies margin-inline: auto to center the container.
animationAnimationNameRenge animation token.
asElementType"section"Rendered HTML element.

Container

Centered max-width wrapper. Max-widths follow Fibonacci-proportioned breakpoints: 520, 768, 1024, 1440. Horizontal padding defaults to space-5 (32px = Fibonacci×8×4).

Sizes (shown at reduced scale)

sm — 520px
md — 768px
lg — 1024px (default)
{/* Default: lg (1024px), px=5 */}
<Container>
  <Heading>Page content</Heading>
</Container>

{/* Narrow — article width */}
<Container size="sm" px="4">
  <Text>Prose text with narrow measure.</Text>
</Container>

{/* Custom padding */}
<Container size="xl" px="6">
  <Grid columns={3} gap="5">…</Grid>
</Container>
PropTypeDefaultDescription
size"sm" | "md" | "lg" | "xl" | "full""lg"Max-width: 520 / 768 / 1024 / 1440 / 100%.
px"0" – "6""5"Horizontal padding — Fibonacci spacing token.

AspectRatio

Ratio-maintaining container. Default ratio = φ (1.618…) — the golden ratio. Any content inside fills the proportioned box. Uses padding-bottom technique for universal support.

Common ratios

φ = 1.618
1.618
16/9
1.778
1:1
1.000
import { PHI } from "@renge-ui/tokens";

{/* Default: PHI (golden ratio) */}
<AspectRatio>
  <img src="/photo.jpg" alt="…" style={{ objectFit: "cover" }} />
</AspectRatio>

{/* 16:9 video */}
<AspectRatio ratio={16/9}>
  <video src="/demo.mp4" controls />
</AspectRatio>

{/* Square */}
<AspectRatio ratio={1}>
  <div>…</div>
</AspectRatio>
PropTypeDefaultDescription
rationumberPHI (1.618…)Width-to-height ratio. Pass 16/9 for widescreen, 1 for square, PHI for golden ratio.

Spacer

Explicit whitespace element. All sizes map directly to Fibonacci spacing tokens — the visible grammar of the space between things.

Vertical spacers (rendered with borders for visibility)

space-2
space-4
space-6
<Heading>Section title</Heading>
<Spacer size="5" />
<Text>The proportion between the heading and the body text is deliberate.</Text>

{/* Horizontal spacer in flex layouts */}
<Stack direction="horizontal">
  <Text>Left</Text>
  <Spacer size="4" axis="horizontal" />
  <Text>Right</Text>
</Stack>
PropTypeDefaultDescription
size"1" – "10""4"Fibonacci spacing step. size=4 = space-4 = 20px (Fibonacci 5 × 4px).
axis"vertical" | "horizontal""vertical"vertical=height · horizontal=width (for use in flex rows).

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

h13xl

Level 2 heading

h22xl

Level 3 heading

h3xl

Level 4 heading

h4lg

Color 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) */}
PropTypeDefaultDescription
level1 | 2 | 3 | 4 | 5 | 62Semantic HTML level (h1–h6). Also sets the default size.
size"lg" | "xl" | "2xl" | "3xl" | "4xl"level-mappedOverride the default size. h1→3xl, h2→2xl, h3→xl, h4/5/6→lg.
color"fg" | "fg-subtle" | "accent""fg"Text color semantic token.
animationAnimationNameRenge animation token.

Text

Inline or block text element with full access to the PHI type scale, color tokens, and weight options.

Size scale

The ratio that appears in every living thing.2xl
The ratio that appears in every living thing.xl
The ratio that appears in every living thing.lg
The ratio that appears in every living thing.base
The ratio that appears in every living thing.sm
The ratio that appears in every living thing.xs

Colors + weights

fgfg-subtlefg-mutedaccentsuccesswarningdanger
weight="normal"weight="medium"weight="semibold"weight="bold"
<Text size="lg" color="fg-subtle">Natural proportion.</Text>
<Text as="p" weight="medium" size="base">
  Fibonacci spacing.
</Text>
<Text size="sm" 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>
PropTypeDefaultDescription
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.
animationAnimationNameRenge animation token.
asElementType"span"Rendered HTML element.

Divider

Horizontal or vertical rule rendered as an <hr>. Uses border color tokens for consistent visual weight.

Horizontal variants

Above — default (border-subtle)
Between sections
Below — stronger border, tighter spacing

Vertical

Left content
Right content
<Divider />
<Divider color="border" spacing="2" />
<Divider orientation="vertical" spacing="4" />
PropTypeDefaultDescription
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.

Anchor

Styled anchor element. Three variants for different contexts. Named Anchor (not Link) to avoid collision with Next.js's Link component.

Variants

<Anchor href="/docs">Read the docs</Anchor>
<Anchor href="/github" variant="subtle">GitHub →</Anchor>

{/* Always underlined */}
<Anchor href="/privacy" underline>
  Privacy policy
</Anchor>

{/* In prose — inherits parent text color */}
<p>
  Built on <Anchor href="/phi" variant="plain">natural mathematics</Anchor>.
</p>
PropTypeDefaultDescription
variant"default" | "subtle" | "plain""default"default=accent color · subtle=fg-subtle · plain=inherit.
underlinebooleanfalseShows underline at rest (not just on hover).

Button

Action element. Every variant, size, and color combination resolves to a CSS variable — no hardcoded values.

Variants

Sizes

Color schemes

Full width + disabled

<Button variant="solid" size="md" colorScheme="accent">
  Continue
</Button>

<Button variant="outline" colorScheme="danger" size="sm">
  Delete
</Button>

<Button variant="ghost" colorScheme="success">
  Confirm
</Button>

{/* Disabled state — native HTML attribute */}
<Button disabled style={{ opacity: 0.4, cursor: "not-allowed" }}>
  Unavailable
</Button>

{/* Extends all native button props */}
<Button onClick={handleSubmit} type="submit" fullWidth>
  Submit form
</Button>
PropTypeDefaultDescription
variant"solid" | "outline" | "ghost""solid"Solid fills with colorScheme bg, outline shows border, ghost is text-only.
size"sm" | "md" | "lg""md"sm: space-1/2 · md: space-2/4 · lg: space-3/5 padding.
colorScheme"accent" | "danger" | "success""accent"Which semantic color set to apply across all variants.
fullWidthbooleanfalseStretches button to 100% container width.
animationAnimationNameRenge animation token applied as CSS animation.
disabledbooleanNative HTML attribute — style opacity via style prop.

IconButton

Circular icon-only button. Sizes follow the fractal scale (Fibonacci × baseUnit): 20→32→40→52→84px. Each step is self-similar — a precise mathematical miniature or magnification of the base.

Sizes

xs
sm
md
lg
xl

Variants + color schemes

{/* Basic icon button */}
<IconButton aria-label="Close" onClick={onClose}>
  ✕
</IconButton>

{/* With SVG icon */}
<IconButton
  aria-label="Settings"
  size="md"
  variant="ghost"
>
  <SettingsIcon />
</IconButton>

{/* Danger action */}
<IconButton
  aria-label="Delete item"
  colorScheme="danger"
  variant="outline"
>
  <TrashIcon />
</IconButton>
aria-label is required for icon-only buttons — there is no visible text to describe the action to screen readers.
PropTypeDefaultDescription
aria-labelstringRequired. Accessible label for screen readers.
size"xs" | "sm" | "md" | "lg" | "xl""md"20/32/40/52/84px — fractal scale (Fibonacci × baseUnit).
variant"solid" | "outline" | "ghost""ghost"Same variant model as Button.
colorScheme"accent" | "danger" | "success" | "neutral""neutral"Color channel. neutral uses fg-subtle.
animationAnimationNameRenge animation token.

ButtonGroup

Groups related buttons into a single visual unit. Use ButtonGroupItem to flatten border radii on touching edges — only outermost corners keep their curvature.

Attached group

<ButtonGroup>
  <Button
    variant="outline"
    style={{
      borderRadius: "var(--renge-radius-2) 0 0 var(--renge-radius-2)",
      borderRight: "none",
    }}
  >
    Left
  </Button>
  <Button variant="outline" style={{ borderRadius: 0 }}>
    Middle
  </Button>
  <Button
    variant="outline"
    style={{ borderRadius: "0 var(--renge-radius-2) var(--renge-radius-2) 0" }}
  >
    Right
  </Button>
</ButtonGroup>
PropTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Flex direction of the group.
radius"none" | "1" | "2" | "3" | "full""2"Radius applied to outer edges.

CopyButton

Clipboard copy with visual feedback. Success state persists for 2100ms (duration-7 — a Fibonacci duration, long enough to read, short enough not to linger).

Copy values

In a code block

pnpm add @renge-ui/tokens @renge-ui/react
<CopyButton value="text to copy" />
<CopyButton
  value={cssVar}
  label="Copy"
  successLabel="Copied!"
  timeout={2100}
  onCopy={(value) => console.log("Copied:", value)}
/>
PropTypeDefaultDescription
valuestringText written to the clipboard on click.
labelstring"Copy"Button label at rest.
successLabelstring"Copied!"Label shown after a successful copy.
timeoutnumber2100How long to show success state in ms. Default 2100 = duration-7 (Fibonacci).
onCopy(value: string) => voidCallback fired after a successful copy.

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
/>
The native HTML size attribute (a number controlling character width) is excluded to avoid collision with Renge's size prop. Use style={{ width: "..." }} or fullWidth for sizing.
PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Controls padding and font size.
state"default" | "error" | "success""default"Sets border color to the corresponding semantic token.
fullWidthbooleanfalseStretches input to 100% container width.

Select

Styled native select element. Chevron rendered as a background SVG — no extra DOM nodes. Sizing and state tokens match Input exactly.

Sizes

States + placeholder

<Select placeholder="Choose a profile…" fullWidth>
  <option value="ocean">Ocean</option>
  <option value="earth">Earth</option>
</Select>

<Select state="error" fullWidth>
  <option>Invalid</option>
</Select>

{/* Controlled */}
<Select value={profile} onChange={(e) => setProfile(e.target.value)}>
  <option value="ocean">Ocean</option>
  <option value="twilight">Twilight</option>
</Select>
PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Controls padding and font size — mirrors Input sizing.
state"default" | "error" | "success""default"Border color mapped to semantic state token.
fullWidthbooleanfalseStretches to 100% container width.
placeholderstringRenders a hidden disabled option as the default selection prompt.

Checkbox

Custom-styled checkbox. The box and check geometry derive from Fibonacci spacing. The checkmark animates in with spring easing via a stroke-dashoffset draw.

States

Sizes

Controlled

<Checkbox label="Accept terms" />
<Checkbox label="Remember me" defaultChecked />
<Checkbox label="Partially selected" indeterminate />

{/* Controlled */}
<Checkbox
  checked={agreed}
  onChange={(e) => setAgreed(e.target.checked)}
  label="I agree to the terms"
/>
PropTypeDefaultDescription
labelstringLabel rendered beside the checkbox.
size"sm" | "md" | "lg""md"Box size — 16 / 20 / 24 px (Fibonacci-aligned).
indeterminatebooleanfalseShows a dash instead of a checkmark — used for select-all states.
disabledbooleanfalseReduces opacity and sets cursor:not-allowed.
checkedbooleanControlled checked state.
defaultCheckedbooleanfalseUncontrolled initial value.

Radio + RadioGroup

Radio button group. The inner dot is sized at outer / φ — the golden proportion is visible in every interaction. RadioGroup manages shared name and value state.

Basic group

Horizontal + sizes

Selected: md

Disabled

<RadioGroup
  name="theme"
  defaultValue="ocean"
  onChange={(value) => setTheme(value)}
>
  <Radio value="ocean" label="Ocean" />
  <Radio value="earth" label="Earth" />
  <Radio value="twilight" label="Twilight" />
</RadioGroup>

{/* Horizontal */}
<RadioGroup name="size" direction="horizontal" gap="4">
  <Radio value="sm" label="Small" />
  <Radio value="lg" label="Large" />
</RadioGroup>
PropTypeDefaultDescription
namestring(RadioGroup) Shared input name — required for accessibility.
valuestring(RadioGroup) Controlled selected value.
defaultValuestring""(RadioGroup) Uncontrolled initial value.
onChange(value: string) => void(RadioGroup) Called with the new value when selection changes.
direction"vertical" | "horizontal""vertical"(RadioGroup) Stack direction.
disabledbooleanfalse(RadioGroup) Disables all child radios.
valuestring(Radio) The value this radio represents.
labelstring(Radio) Label text.
size"sm" | "md" | "lg"inherited(Radio) Individual size override.

Switch

Toggle switch. Track width:height ≈ φ:1 — the golden proportion. Thumb slides with spring easing. Works as a styled checkbox under the hood for full accessibility.

Sizes

Label positions + controlled

<Switch label="Dark mode" />
<Switch label="Notifications" defaultChecked />

{/* Controlled */}
<Switch
  label="Feature flag"
  checked={enabled}
  onChange={(e) => setEnabled(e.target.checked)}
/>

{/* Label on left */}
<Switch label="Auto-save" labelPosition="left" />
PropTypeDefaultDescription
size"sm" | "md" | "lg""md"sm: 32×20px · md: 40×24px · lg: 48×28px. Track width≈φ×height.
labelstringVisible label text.
labelPosition"left" | "right""right"Label placement relative to the switch track.
checkedbooleanControlled checked state.
defaultCheckedbooleanfalseUncontrolled initial state.
disabledbooleanfalseDims and disables interaction.

Textarea

Multi-line text input. Min-height steps follow Fibonacci line counts — 3, 5, and 8 lines at sm/md/lg respectively.

Sizes

States + resize

<Textarea placeholder="Describe your system…" fullWidth />

<Textarea
  size="lg"
  state="error"
  placeholder="Too long — 500 characters max"
  fullWidth
/>

{/* Controlled */}
<Textarea
  value={bio}
  onChange={(e) => setBio(e.target.value)}
  resize="none"
  fullWidth
/>
PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Affects padding, font size, and minimum height (Fibonacci line counts: 3/5/8).
state"default" | "error" | "success""default"Border color state — mirrors Input.
fullWidthbooleanfalseStretches to 100% container width.
resize"none" | "vertical" | "horizontal" | "both""vertical"CSS resize property.

Slider

Range slider with optional φ markers. The golden section points (0.382 and 0.618 of the range) are marked as optional visual guides — the phi ratio embedded in every use.

Default + controlled

Scale ratio50
PHI-controlled: 61.861.8

φ markers at 0.382 and 0.618

Golden section markers61.8
φ⁻¹
φ⁻²
<Slider label="Variance" showValue defaultValue={0} />

{/* PHI markers — shows golden section at 0.382 and 0.618 */}
<Slider
  label="Scale ratio"
  showPhiMarkers
  showValue
  min={1}
  max={2}
  step={0.001}
  defaultValue={1.618}
/>

{/* Controlled */}
<Slider
  value={scale}
  onChange={(e) => setScale(parseFloat(e.target.value))}
/>
PropTypeDefaultDescription
showPhiMarkersbooleanfalseRenders tick marks at 0.382 and 0.618 of the range — the golden section points.
labelstringLabel text shown above the track.
showValuebooleanfalseDisplays current value to the right of the label.
min / max / stepnumber0 / 100 / 1Native range input attributes.

FormField

Wraps any input with a label, optional helper text, and error text. Error overrides helper when both are present.

States

We'll never share your email.
Username is already taken.
Read-only. Regenerate from settings.
<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>
PropTypeDefaultDescription
labelstringLabel text rendered in a <label> element.
htmlForstringLinks the label to an input via its id.
helperTextstringMuted hint text below the input.
errorTextstringDanger-colored error text. Overrides helperText when both are set.
requiredbooleanAppends a red asterisk to the label.

Card

Surface container with three visual variants. Padding and radius are token-driven.

Variants

ElevatedShadow on bg
OutlinedBorder visible
Filledbg-subtle surface

Padding scale

padding="2"
padding="4"
padding="6"
<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>
PropTypeDefaultDescription
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).
animationAnimationNameRenge animation token.

Badge

Compact inline label for status, category, or count. Six semantic variants map directly to the color token system.

Variants

neutralaccentsuccesswarningdangerinfo

Sizes

sm — compactmd — defaultlg — prominent
<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>
PropTypeDefaultDescription
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

neutralaccentsuccesswarningdanger

Dismissible (live demo)

Design systemPHIFibonacciProportional
{/* 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>
))}
PropTypeDefaultDescription
variant"neutral" | "accent" | "success" | "warning" | "danger" | "info""neutral"Color semantic — same token mapping as Badge.
onDismiss() => voidWhen 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

RG
20px
RG
32px
RG
52px
RG
84px
RG
136px

Shapes + image

PH
circle
FB
square
Renge
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" />
Avatar sizes are Fibonacci numbers × 4px: 20 · 32 · 52 · 84 · 136. Consecutive ratios converge toward φ — each size is ≈1.618× the previous.
PropTypeDefaultDescription
srcstringImage URL. Takes priority over initials if provided.
altstringImage alt text and aria-label for the avatar.
initialsstringUp 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

Golden ratio
φ
PHI (exact)
1.618
(1 + √5) / 2
Fibonacci step 10
89 +34
from step 9 (55)
Duration 4
13ms -2ms
vs last build
Tests passing
106 +1
<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"
/>
PropTypeDefaultDescription
valuestring | numberPrimary metric — rendered at font-size-3xl.
labelstringSmall label above the value.
trend"up" | "down" | "neutral"up=success · down=danger · neutral=fg-muted. Requires trendValue to render the badge.
trendValuestringChange amount shown in the trend badge (e.g. '+34', '-2ms').
captionstringMuted caption rendered below the value row.

Tooltip

Hover tooltip with spring entry animation. Offset uses space-1 (4px = Fibonacci×1) so the gap is always proportioned. Four placements available.

Placements

top
bottom
left
right

Rich content

<Tooltip content="This is a tooltip" placement="top">
  <Button>Hover me</Button>
</Tooltip>

{/* Rich content */}
<Tooltip
  content={
    <Stack gap="1">
      <Text>PHI = 1.618…</Text>
      <Text size="sm" color="fg-subtle">The golden ratio</Text>
    </Stack>
  }
  placement="right"
>
  <Badge>φ</Badge>
</Tooltip>

{/* With delay */}
<Tooltip content="Appears after 500ms" delay={500}>
  <span>Hover</span>
</Tooltip>
PropTypeDefaultDescription
contentReactNodeTooltip body — can be a string or any React element.
placement"top" | "bottom" | "left" | "right""top"Where the tooltip appears relative to its trigger.
delaynumber0Milliseconds to delay before showing. Useful for reducing tooltip noise on fast mouse movements.

Skeleton

Animated loading placeholder. Shimmer sweeps at duration-6 (1300ms — Fibonacci). Text variant: the last line is 61.8% wide (1/φ), creating a natural paragraph silhouette.

Variants

text (3 lines)
rectangular

Card skeleton pattern

{/* Text — last line at 61.8% (1/φ) */}
<Skeleton variant="text" lines={3} />

{/* Rectangular */}
<Skeleton variant="rectangular" height={80} />

{/* Circular — avatar placeholder */}
<Skeleton variant="circular" width={52} height={52} />

{/* Custom dimensions */}
<Skeleton width="200px" height="120px" />

{/* No animation */}
<Skeleton animated={false} height={40} />
PropTypeDefaultDescription
variant"text" | "circular" | "rectangular""rectangular"text: renders N lines with last at 61.8% width. circular: full border-radius.
linesnumber3Number of lines (variant=text only).
widthstring | number"100%"Width — number treated as px.
heightstring | numberspace-5Height — number treated as px.
animatedbooleantrueEnable shimmer sweep animation.

Table

Data table primitives: Table, TableHead, TableBody, TableFoot, TableRow, TableHeader, TableCell. Row padding follows Fibonacci spacing. Header uses semantic uppercase styling.

Full table

Plantφ RatioFibonacciHabitat
Coast Live Oak1.61889Woodland
Blue Wild Rye1.38255Riparian
California Poppy1.23634Grassland
Sword Fern1.61889Forest
Toyon1.38255Chaparral
<Table bordered>
  <TableHead>
    <TableRow>
      <TableHeader>Name</TableHeader>
      <TableHeader>Value</TableHeader>
    </TableRow>
  </TableHead>
  <TableBody>
    {rows.map((row, i) => (
      <TableRow key={row.id} index={i}>
        <TableCell>{row.name}</TableCell>
        <TableCell muted>{row.value}</TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>
PropTypeDefaultDescription
borderedbooleanfalse(Table) Outer border on the container.
stripedbooleanfalse(Table) Zebra-stripe via data-striped attribute.
indexnumber(TableRow) 0-indexed position — even rows get bg-subtle.
mutedbooleanfalse(TableCell) Renders in fg-muted for secondary data.

Accordion

Expandable sections. Height animates with ease-in-out at duration-4 (500ms). The chevron rotates 180° on open. Accordion manages which items are open; AccordionItem is a single row.

Single (default)

The golden ratio — 1.618033… It appears in nautilus shells, sunflower spirals, galaxy arms. Renge builds its spacing scale from it.
1, 1, 2, 3, 5, 8, 13, 21… Each number is the sum of the two before it. The ratio of consecutive Fibonacci numbers converges toward φ.
The pattern of leaves on a stem. Nature's solution to optimal packing — each leaf grows at the golden angle (137.5°) from the previous. The logic behind our grid.

Multiple open allowed

I can be open simultaneously with others.
Both panels can be open at once when multiple=true.
This item is disabled.
<Accordion defaultOpen="phi">
  <AccordionItem id="phi" title="What is PHI?">
    The golden ratio — 1.618033…
  </AccordionItem>
  <AccordionItem id="fibonacci" title="Fibonacci sequence">
    1, 1, 2, 3, 5, 8, 13…
  </AccordionItem>
</Accordion>

{/* Multiple simultaneously open */}
<Accordion multiple>
  <AccordionItem id="one" title="Section 1">Content</AccordionItem>
  <AccordionItem id="two" title="Section 2">Content</AccordionItem>
</Accordion>
PropTypeDefaultDescription
multiplebooleanfalse(Accordion) Allow multiple items open simultaneously.
defaultOpenstring | string[](Accordion) ID(s) of items open on mount.
idstring(AccordionItem) Unique identifier used for open state tracking.
titleReactNode(AccordionItem) Trigger label.
disabledbooleanfalse(AccordionItem) Prevents expanding.

Timeline

Vertical event list. Dots are sized at space-3 (12px = Fibonacci×3). The connector gradient transitions from the item's status color to border-subtle — events flow forward in time.

Build history

  1. Tokens — v2.2.4Today

    Added animation scale. 15 named keyframes. All Fibonacci-derived durations.

  2. React — v2.3.3Today

    44 components. Added data input, display, navigation, feedback, layout, and action categories.

  3. Tailwind — v2.2.5Today

    v4 plugin now injects animation vars and @keyframes blocks.

  4. @renge-ui/vueTBD

    Vue 3 component port. Not started.

<Timeline>
  <TimelineItem
    title="v1.0 released"
    description="First stable token system."
    timestamp="Jan 2025"
    status="completed"
  />
  <TimelineItem
    title="React components"
    description="21 components added."
    timestamp="Feb 2025"
    status="active"
  />
  <TimelineItem
    title="Vue port"
    description="Coming soon."
    timestamp="TBD"
    status="pending"
  />
</Timeline>
PropTypeDefaultDescription
titleReactNode(TimelineItem) Event label.
descriptionReactNode(TimelineItem) Supporting copy below the title.
timestampReactNode(TimelineItem) Time label — aligned right.
status"completed" | "active" | "pending""pending"(TimelineItem) completed=success · active=accent · pending=border.
iconReactNode(TimelineItem) Optional icon inside the dot (small — use 8–10px icons).

Alert

Contextual banner with left-border accent and semantic status. Renders with role="alert" for screen reader compatibility.

All statuses

PHI is irrationalThe golden ratio cannot be expressed as a simple fraction.
Build succeededAll 106 tests passed. Zero regressions.
High varianceVariance above 0.1 may affect visual consistency across themes.
Token not found--renge-color-unknown is undefined in this profile.

Without title

Informational message without a title.
Variance is set to 0.08 — slight organic drift is active.
<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>
PropTypeDefaultDescription
status"info" | "success" | "warning" | "danger""info"Sets left-border color and background using semantic token pairs.
titlestringBold 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

sm · 16px
md · 24px
lg · 32px

Colors

accent
fg
fg-muted
<Spinner size="md" color="accent" label="Loading tokens" />
<Spinner size="sm" color="fg-subtle" />

{/* In a button */}
<Button variant="outline" disabled>
  <Stack direction="horizontal" gap="2" align="center">
    <Spinner size="sm" color="accent" />
    <span>Saving...</span>
  </Stack>
</Button>
PropTypeDefaultDescription
size"sm" | "md" | "lg""md"16 / 24 / 32 px diameter.
color"accent" | "fg" | "fg-muted""accent"Spinner ring color token.
labelstring"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

61.8% — 1/φ
38.2% — 1/φ²
80% — warning
20% — danger, no radius
<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"
/>
PropTypeDefaultDescription
valuenumber0–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.
labelstringaria-label for accessibility.

Toast

Portal-based notification system. Max 5 toasts visible (Fibonacci). Entries use spring easing; exits use ease-in. Wrap your app in ToastProvider and call useToast() anywhere.

Trigger toasts

// 1. Wrap app (or page) in ToastProvider
import { ToastProvider } from "@renge-ui/react";

export default function Layout({ children }) {
  return (
    <ToastProvider>
      {children}
    </ToastProvider>
  );
}

// 2. Use the hook anywhere inside
import { useToast } from "@renge-ui/react";

function SaveButton() {
  const { toast } = useToast();
  return (
    <Button onClick={() => {
      await save();
      toast({
        title: "Saved",
        description: "Your changes are live.",
        status: "success",
        duration: 3000,  // ms — default 5000
      });
    }}>
      Save
    </Button>
  );
}
Toast uses createPortal to render outside the DOM tree into document.body. Add ToastProvider once at the root — not per-page.
PropTypeDefaultDescription
titlestring(toast()) Required — primary message.
descriptionstring(toast()) Optional body text below the title.
status"default" | "success" | "warning" | "danger" | "info""default"(toast()) Determines left-border color and icon.
durationnumber5000(toast()) Auto-dismiss after N ms. Set to 0 for persistent.
idstringauto(toast()) Custom ID — use to deduplicate or dismiss programmatically.

Tabs

Tab navigation with spring-eased active underline. Tabs manages open state; Tab registers a value; TabPanel renders conditionally.

Basic tabs

φ = 1.618033… The golden ratio. Two quantities are in the golden ratio if their ratio equals the ratio of their sum to the larger quantity.

<Tabs defaultTab="overview">
  <TabList>
    <Tab value="overview">Overview</Tab>
    <Tab value="props">Props</Tab>
    <Tab value="examples">Examples</Tab>
  </TabList>
  <TabPanel value="overview">
    <Text>Overview content</Text>
  </TabPanel>
  <TabPanel value="props">
    <PropsTable>…</PropsTable>
  </TabPanel>
  <TabPanel value="examples">
    <Demo>…</Demo>
  </TabPanel>
</Tabs>

{/* Controlled */}
<Tabs value={activeTab} onChange={setActiveTab}>
  …
</Tabs>
PropTypeDefaultDescription
defaultTabstring""(Tabs) ID of the tab active on mount.
valuestring(Tabs) Controlled active tab.
onChange(id: string) => void(Tabs) Called when active tab changes.
valuestring(Tab / TabPanel) The identifier shared between a Tab and its TabPanel.

Pagination

Page navigation. Shows at most siblings×2+5 pages — default sibling=1 (Fibonacci). Active page uses accent. Ellipsis replaces gaps greater than 2 pages.

Interactive

Page 1 of 20

Siblings = 2

const [page, setPage] = useState(1);

<Pagination
  total={20}
  page={page}
  onChange={setPage}
/>

{/* More siblings visible */}
<Pagination
  total={100}
  page={50}
  onChange={setPage}
  siblings={2}
/>
PropTypeDefaultDescription
totalnumberTotal number of pages.
pagenumberCurrent page (1-indexed).
onChange(page: number) => voidCalled with the new page number when the user navigates.
siblingsnumber1Pages shown on each side of the active page before ellipsis truncation.

EnergyRing

Circular SVG ring showing an energy or progress level. Stroke width and label scale by PHI with the ring size — self-similar at every size.

Sizes

72%
72%

Colors

65%
80%
45%
30%

Pulse rates

50%
rest
50%
active
50%
fire
<EnergyRing value={72} />
<EnergyRing value={72} size="xl" color="success" />
<EnergyRing value={50} size="lg" pulse rate="active" />
<EnergyRing value={88} size="xl" label="88%" color="warning" />
PropTypeDefaultDescription
valuenumberEnergy level 0–100.
size"sm" | "md" | "lg" | "xl""md"Ring diameter — sm:32px → xl:136px, each step ≈ φ growth.
color"accent" | "success" | "warning" | "danger""accent"Color channel — maps to semantic token.
pulsebooleanfalseEnable breathing animation.
rate"rest" | "active" | "fire""active"Pulse speed — rest=2100ms, active=800ms, fire=500ms.
labelstring | null"{value}%"Center label (shown at lg/xl only). Pass null to hide.

Pulse

A breathing dot — the minimal alive indicator. Scales to φ at peak. Rate maps to energy states with Fibonacci-derived durations.

Sizes

Colors + rates

rest
active
fire

With ripple

Inline status indicator

System online
<Pulse />
<Pulse size="lg" color="success" rate="fire" />
<Pulse size="lg" color="accent" ripple />

{/* Status indicator */}
<Stack direction="horizontal" gap="2" align="center">
  <Pulse color="success" size="sm" />
  <Text size="sm">System online</Text>
</Stack>
PropTypeDefaultDescription
rate"rest" | "active" | "fire""active"Breath speed — rest=2100ms (~29bpm), active=800ms, fire=500ms.
color"accent" | "success" | "warning" | "danger" | "fg-muted""accent"Dot color channel.
size"sm" | "md" | "lg""md"Dot diameter — 6px / 10px / 16px (fractal scale).
ripplebooleanfalseShow expanding ripple ring behind the dot.

FlowField

A living phyllotaxis dot field. Each dot is placed at a golden-angle position and pulses with a delay derived from its spiral index — energy flows outward along the golden spiral.

Energy levels

void
rest
active
fire

Colors

<FlowField />
<FlowField energy="fire" size={300} count={144} />
<FlowField energy="rest" color="fg-subtle" size={200} />

{/* As a decorative background element */}
<div style={{ position: "relative" }}>
  <FlowField energy="void" style={{ position: "absolute", inset: 0, opacity: 0.4 }} />
  <YourContent />
</div>
PropTypeDefaultDescription
energy"void" | "rest" | "active" | "fire""active"Controls density, opacity, and pulse rate. void=sparse/slow, fire=dense/fast.
countnumber144Maximum dot count at full energy. Actual count scales with energy density.
sizenumber400Container width and height in px.
color"accent" | "fg-muted" | "fg-subtle""accent"Dot color channel.

Token API

@renge-ui/tokens exports two ways to consume tokens: createRengeTheme() for generating CSS, and rengeVars for typed CSS variable references.

Every component in this library references CSS custom properties — never hardcoded values. Switch profiles by swapping which theme block is injected. No component re-renders required.

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 applied

rengeVars

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)" → pill

Integrating 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 keyCSS variableRole
color.bg--renge-color-bgPage background
color.bgSubtle--renge-color-bg-subtleSlightly elevated surface
color.bgMuted--renge-color-bg-mutedMuted surface
color.bgInverse--renge-color-bg-inverseInverted background
color.fg--renge-color-fgPrimary text
color.fgSubtle--renge-color-fg-subtleSecondary text
color.fgMuted--renge-color-fg-mutedPlaceholder / disabled
color.fgInverse--renge-color-fg-inverseText on inverse bg
color.border--renge-color-borderDefault divider
color.borderSubtle--renge-color-border-subtleHairline divider
color.borderFocus--renge-color-border-focusKeyboard focus ring
color.accent--renge-color-accentPrimary interactive
color.accentHover--renge-color-accent-hoverHover state
color.accentSubtle--renge-color-accent-subtleTinted background
color.success--renge-color-successPositive outcome
color.successSubtle--renge-color-success-subtleSuccess tint bg
color.warning--renge-color-warningCaution
color.warningSubtle--renge-color-warning-subtleWarning tint bg
color.danger--renge-color-dangerError / destructive
color.dangerSubtle--renge-color-danger-subtleDanger tint bg
color.info--renge-color-infoInformational
color.infoSubtle--renge-color-info-subtleInfo tint bg

Animations

15 named animations derived from the token system. Apply via the animation prop on any component that accepts it.

Available tokens

vortex-reveal
helix-rise
sacred-fade
spiral-in
morph-fade-in
bloom
pulse
vibrate
wave
breathe
fall
float
float-wave
pulse-color-shift
swelling

Live examples

breathe

floatpulse
bloom
{/* 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>
Animation CSS variables are injected into the document by 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

Passing
Tests106 / 106

Last run 2 minutes ago

Form with validation

Used for login and notifications.
Must be at least 12 characters.

Metric grid

Scale ratio
φ
Fibonacci 10
89 +34
Tests
106 +1
{/* 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.
All 44 components use only var(--renge-*) CSS custom properties. Switch color profiles by injecting a new theme block — no component re-renders required.
Scale6.0