Skip to content
New guideAgent Cookbook 12 copy-ready prompts for AI coding agents.See recipes

Dwellir design system

Build the site from parts, not paint

The components, tokens and rules behind every Dwellir page. Pick a part, set its variant, arrange it. The look comes with it.

Tailwind v4shadcn/uiRadixnpm run lint:ds

Foundations

Tokens before components

Every component reads the same small set of tokens. Learn these and you can read any screen.

Colour

Name colours by role, never by value. The classes read the identity's tokens, so a sheet that swaps its grounds restyles everything inside it.

backgroundThe page ground. Inside an alt sheet it takes the card colour.
cardCards. Inside an alt sheet it takes the page ground.
skyThe deep sheet of heroes and closing calls to action.
titleTitles and headings: a step above the body text at night.
foregroundBody text and icons.
muted-foregroundSecondary text. AA on ground, card and sky, and on the muted fill over each.
primaryThe filled green of the main action.
accent-inkGreen text and links. AA on every ground.
accentA green tint for selected items.
borderHairlines and card edges. White at 10% at night, so it shows on every surface.
inputForm field edges: a calm 1.5:1. The placeholder, the label and the focus ring identify the field.
input-hoverA field edge under the pointer: about 2.3:1 on the ground.
input-strongThe edge of a control whose edge is its only cue (checkbox, radio, switch track): 3:1 on every ground.
fieldForm field fills: white by day, a step below the card at night.
ringThe keyboard focus ring.
destructiveErrors and destructive actions.
warning-inkWarnings, such as a fallback status: text-warning-ink. AA on every ground.
aurora-1Green
aurora-2Violet
aurora-3Rose
aurora-4Gold
status-operationalOperational: -ink
status-minorMinor: -ink
status-majorMajor: -ink
status-downDown: -ink
status-no-dataNo data: -ink

The status scale paints uptime and incidents: a fill per tier for bars and tints, at 3:1 on the ground and the card, and an ink for text, at 4.5:1. The uptime bar and the status badges use it.

Type

Ubuntu for display and headings, Roboto for body text, JetBrains Mono for code, at 400 and 700 only. Sizes match stock Tailwind in pixels: text-sm is 14px.

text-displayRPC for 150+ networks
text-headingInfrastructure for every use case
text-sectionA section on an app screen
text-card-titleA card title in an app
text-2xlA card title, large
text-xlA card title
text-lgA lede: one or two sentences that say why the section matters.
text-baseBody text in Roboto. 1 response is 1 credit, with no compute units.
text-smControls, table cells and secondary copy.
text-eyebrowAn eyebrow label
font-uiChain ID · Sign up · Hyperliquid: buttons, navbar items, field labels
font-monocurl https://api-ethereum-mainnet.n.dwellir.com/<key>

Shape and depth

Controls are pills. Nested corners are concentric: inner radius = outer radius − inset. By day, depth comes from a top light and a long, soft drop. At night a surface rises by lightness and the shadows go: its 1px edge, white at 10%, marks it.

rounded-lgMenu items
rounded-2xlMenus, popovers
rounded-cardCards
rounded-3xlDialogs, sheets
rounded-sheetPage sheets
rounded-fullEvery control (the Radius section)
shadow-xs
shadow-card
shadow-popover
shadow-glow

Logo

DwellirLogo draws the wordmark or, with mark="shield", the shield alone. One file serves both themes: on dark grounds the green rim carries the shape, on light ones the fill. Size it with a height class; the link around it names it.

Units

The site's root font size is 62.5%, so 1rem is 10px. The theme scales spacing, type, radii and widths to match: p-4 is 16px and max-w-md is 448px, as in stock Tailwind. Under 900px the root drops to 60% and everything scales with it.

Do

  • Use role tokens: text-muted-foreground, bg-card.
  • Use text-accent-ink for green text; it meets AA on every ground.
  • Pick from the scales. Add a token to the theme when a design needs a new one.

Don't

  • Write raw colours: bg-[#2e2e2e], text-emerald-500.
  • Use text-primary for text: the filled green is under AA on white.
  • Fake weights: only 400 and 700 load, so 500 and 600 render as one of them.

Radius

One corner for every control

Every control reads one theme token, --radius-control. This site sets it to a pill. An app sets it once in its theme, such as 8px, and its buttons, fields, tabs and key caps all follow.

The radius scale

Pick a corner by the role of the surface. Multiline fields and key caps cap the control token at their role's limit.

rounded-control-max-smKey cap · Control, capped at 4pxKbd. A key cap stays a cap, never a pill.
rounded-lgItem · --radius-lg, 8pxMenu, select and sidebar items: rows inside a floating layer.
rounded-control-max-xlControl, capped item · Control, capped at 12pxChoice tiles. Stretch tab items on phones keep the same 12px corner inside their capped track.
rounded-control-max-2xlControl, multiline · Control, capped at 16pxTextarea and an InputGroup with rows. The large composer caps at 24px (max-3xl).
rounded-2xlFloating layer · --radius-2xl, 16pxMenus, popovers and select panels. Dialogs and sheets take rounded-3xl (24px).
rounded-cardCard · --radius-card, 18pxCards, on every surface.
rounded-sheetSheet · --radius-sheet, 32pxThe page sheets a Section draws.
rounded-controlControl · --radius-control, a pill (999px) hereButton, Input, the Select trigger, InputGroup, Toggle, ToggleGroup, ChoiceGroup pills and the Tabs track.

Set it once per app

The second panel sets --radius-control to 8px on one wrapper. No part changes: the same controls take the new corner. The two pinned buttons keep the legacy shape they pass.

this site: --radius-control: 999px
⌘K

Requests this period.

an app: --radius-control: 8px
⌘K

Requests this period.

Do

  • Set --radius-control once in the app theme to square every control.
  • Pick card, floating-layer and item corners from the scale above.

Don't

  • Pass shape to a Button in new code. It stays only so existing callers keep their corners.
  • Add a rounded-* class to a control at a call site. The part owns its corner.
  • Give a multiline field the pill corner. It caps at 16px instead.

Surfaces

Which surface?

Where a card sits picks its surface. Grain is the default; the other surfaces each have one job. Glass only earns its cost where the aurora moves behind it.

Pick the surface from where the card sits and set it with surface="…". The default is grain. Inside a menu, add flat on top of the surface; for a row inside a card, add inset.
Where the card sitsSurface
A standalone card on the page ground or the alt sheetFeature cards, pricing cards, FAQ, stat cardsgrain
A card directly over the aurora, or a panel that floats over the pageThe hero window, cards on sky sheets, the footer islands, navbar panelsglass
Glass over moving content where every frame countsAn app pane over the live aurora or a scrolling list: an 8px blur, no saturateglass-lite
The one call to action among glass panesThe footer enquiry card, the lead column of a navbar panelsky
A card inside a card, or dense contentTables, forms, code, ranked rows, lists of linksplain
A card on an app panel's groundThe dashboard mock's tool, rail and request cardsraised
A read-out inside a cardCalculator sums, the volume summarywell
A row in a list inside a card's contentEndpoint and status rows: plain or well, with a corner that follows the card+ inset
Any card inside a menu, a popover or a dropdownKeeps its surface and drops the shadow: it belongs to the panel+ flat

On the page ground

Grain lifts the card off the ground. Glass has nothing to frost and looks flat. Plain is quiet.

Grain

The default. The card colour under a still film grain. By day a top light and a soft drop; at night the card is lighter than the ground.

Glass

Frosted: the aurora glows through it. On a flat ground there is nothing to frost, and it reads as a dull card.

Glass, lite

Glass with an 8px blur and no saturate, for a pane over moving content where every frame counts.

Plain

No texture. For tables, forms and cards inside cards, where grain would be noise.

On the alt sheet

The sheet takes the card colour and its cards take the page ground, so they still stand out. Use the alt sheet to alternate bands down a long page. This band uses space="roomy", the landing page's padding (80px, 128px from lg); pages use the default (56px, 80px) and demos compact.

Grain

The default. The card colour under a still film grain. By day a top light and a soft drop; at night the card is lighter than the ground.

Glass

Frosted: the aurora glows through it. On a flat ground there is nothing to frost, and it reads as a dull card.

Glass, lite

Glass with an 8px blur and no saturate, for a pane over moving content where every frame counts.

Plain

No texture. For tables, forms and cards inside cards, where grain would be noise.

Over the aurora

Here glass belongs: the curtain glows through it. Grain stays readable and solid. At lite motion quality, glass drops its blur and turns almost solid.

Grain

The default. The card colour under a still film grain. By day a top light and a soft drop; at night the card is lighter than the ground.

Glass

Frosted: the aurora glows through it. On a flat ground there is nothing to frost, and it reads as a dull card.

Glass, lite

Glass with an 8px blur and no saturate, for a pane over moving content where every frame counts.

Plain

No texture. For tables, forms and cards inside cards, where grain would be noise.

Cards in the solar wind

On the sky and alt sheets the solar wind blows past the cards and strikes them. The collider prop says when: hover, always or off. Lite motion quality draws fewer lines; still quality and reduced motion draw none. Other marketing apps install the wind with npx shadcn add @dwellir/solar-wind; dense app screens keep the still parts.

collider="hover"

The default. At rest the wind passes behind the card; it strikes the card while the pointer or the focus is on it.

collider="always"

The wind strikes the card at all times, brighter under the pointer. For the one centrepiece of a sheet, such as the hero window.

collider="off"

The wind passes through. For cards inside cards, rows, overlays and panels that do nothing on hover.

The aurora rim

A 1px ring in green and violet. rim="lit" marks the one featured card in a set. rim="hover" marks clickable cards; it also lights on keyboard focus, which the solar wind cannot show.

Developer

For a first production app.

25M

API responses a month

Growth

The featured plan: rim="lit".

150M

API responses a month

Scale

A clickable card: rim="hover".

500M

API responses a month

A piece of the sky

surface="sky" takes the deep ground and grain of the hero and the close. It isolates, so an AuroraCurtain inside it shows over the ground. Use it for the one call to act in a group of glass panes.

Need a dedicated cluster?

surface="sky" rim="lit", with an AuroraCurtain.

Latest guides

surface="sky" rim="hover": the lead column of a navbar panel. In the panel it is also flat.

Flat inside a panel

A card in a menu, a popover or a dropdown is part of the panel. Set flat: it keeps its surface and drops its shadow, so nothing hangs past the panel's edge.

Latest guides

surface="sky" flat: no shadow past the panel.

  • Docs
  • Blog
  • Gas tracker

Raised cards on an app panel

An app screen, such as the dashboard mock, sets its cards on the panel's ground. surface="raised" is plain with upstream's small shadow.

Request console

surface="raised": plain, with a small shadow.

Usage this month

surface="raised": plain, with a small shadow.

Wells and rows

Inside a card, surface="well" sets a read-out apart with a muted fill. size="xs" makes compact rows for a ranked list.

Unlimited Node · 100 RPS

A card with a well for the sums.

Same traffic billed per response$520/month

You save$41/month

  1. 1DwellirGrowth plan$299
  2. 2Provider BBusiness plan$499
  3. 3Provider CPremium plan + overage$772

Inset rows

A bordered row inside a card's content sets inset on a plain or well card. Its corner (rounded-inset) is the outer corner less the 1px edge and 16px, the padding of a size="xs" card, and never under 12px. This example's card pads 24px, and at this site's 18px card the 12px floor sets the corner.

Endpoints

Each row is a plain inset card. Its corner follows the outer card's.

  • Europe38 ms
  • Asia112 ms
  • Americas96 ms

Hairlines

--border-hairline is 0.5px on a screen with 2 or more device pixels per CSS pixel, and 1px below that. Separator and the PageHeader border take it. In an app, draw your own line with border-b-(length:--border-hairline) or gap-(--border-hairline).

Latency by region

Europe38 ms

Asia112 ms

Americas96 ms

Clickable cards

asChild renders the card as its child: a link to a post, or a button. The focus outline and the rim show on keyboard focus. For one choice from a set, use ChoiceGroup tiles.

A bento frame

size="flush" drops the card's own padding, for one grain frame whose cells carry theirs and share hairline edges, as the homepage's numbers do. It clips its cells, so its rim sits just inside the border: lit, and on hover.

A wide cell carries its own padding.

A narrow cell shares its edge.

A wide cell carries its own padding.

A narrow cell shares its edge.

A wide cell carries its own padding.

A narrow cell shares its edge.

Do

  • Use grain for every standalone card on the ground and the alt sheet, the FAQ too.
  • Use glass only over the aurora: the hero, the sky sheets, the footer islands.
  • Use plain for dense content and for a card inside a card.
  • Set flat on every card inside a menu, a popover or a dropdown.
  • Light the rim on one card per set at most.

Don't

  • Put glass on a flat ground: the blur costs frame time and shows nothing.
  • Stack grain on grain: nest a plain card inside a grain card.
  • Let a card in a menu cast a shadow past the panel.
  • Add borders, shadows or radii to a Card with className. Pick a surface.
  • Light every rim: when everything is featured, nothing is.

Actions and labels

One loud action, many quiet ones

Every control is a pill. The primary green is for the one action that moves the reader forward.

Button

Eight variants, five sizes, four icon sizes and a row size; shape rounded for app UI. One primary per view; the rest step down. Link-ink is for the links beside a view's one green action. Destructive is for an action that deletes. Visual is for a control its children draw, such as a video's thumbnail. A link or an action is a Button, never a Badge.

variant="primary" | "secondary" | "outline" | "ghost" | "link" | "link-ink" | "destructive" | "warning"
size="xs" | "sm" | "default" | "md" | "lg"
size="icon-lg" | "icon" | "icon-sm" | "icon-xs", with aria-label
variant="outline" with aria-pressed: a toggle, such as a filter
EndpointsUsage
shape="rounded", the legacy pin (new code sets --radius-control: see Radius) · aria-current="page" on ghost and outline: the current page
size="row" on a ghost link, shape="rounded": a link row in a list
Pick a network first.
aria-invalid: a trigger whose value fails, with a written error
How to build with it
asChild: a link with the button's look · disabled
variant="visual": the children draw it, such as a chart that replays

Toggle

One on/off control. On, it takes the accent tint, as a pressed outline Button does. Visual leaves the look to its children, such as a chart's bar. Several that belong together are a ToggleGroup (see Forms).

variant="default" | "outline"
size="sm" | "default" | "lg" · an icon, with aria-label
disabled · aria-invalid
variant="visual": the children draw it and read its data-state, such as a map pin

Badge

Identity metadata uses neutral: ecosystem, network, plan tier and role. Keep colour for state. The status tones describe the uptime scale. The text meets AA on its own tint. A badge never starts with a status dot: the word and the variant give the state. A badge that names a chain starts with the chain's icon.

EVMMainnetGrowth planAdmin
Identity metadata: variant="neutral"
HTTPSNew: pay in cryptoPaused
variant="neutral" | "accent" | "outline"
StreamingSyncingDegradedReconnecting
variant="green" | "violet" | "rose" | "gold"
StreamingReconnecting
A state: the word and the hue, no dot · variant="green" | "gold"
OperationalMinor outageMajor outageDownNo data
A status, on the uptime scale · variant="operational" | "minor" | "major" | "down" | "neutral"
FailedRate limitedActive
variant="red" | "orange" | "solid"
ArchiveDeprecatedBetav2 API
variant="secondary" | "destructive" | "ghost" | "link", as upstream
EthereumHyperEVM
A chain: ChainIcon before the name, alt="", 14 px

Do

  • Write the action as a verb: “Create an account”, not “Submit”.
  • Give icon buttons an aria-label and hide the icon.
  • Use asChild to give a link the button's look; keep its href.
  • Let a press answer a finger: on a touch screen a held button shrinks to 97%, as a finger never hovers. Reduced motion keeps it still.
  • Put the chain's icon before its name wherever a label names a chain: a chip, badge, option, list, table or menu. Use ChainIcon with the catalog's icon, at a fixed size; the icon is decorative.

Don't

  • Put two primary buttons side by side.
  • Change a button's padding or height with className: pick a size.
  • Use a badge as a button. If it does something, it is a Button.
  • Start a chip, badge or pill with a status dot. Say the state in the label, or pick a variant.

Forms

Fields that explain themselves

Pill fields with a 3:1 edge, labels above, errors in words. Shown on the alt sheet, where the cards take the page ground.

Fields

Input, Label and Select. Every field has a visible label. A search field takes icon="start".

Enter a number, such as 500.

Choices

ChoiceGroup for one of a few options (segmented, pills, or tiles with a line each): native radios, so the arrow keys move the choice; disabled locks every radio. ToggleGroup for on/off filters, in the same looks. variant="bare" leaves the look to each item, such as a badge (see Overlays, Popover, bare). default and outline are Toggle's looks: they take a size, and join into one bar unless spacing parts them. visual leaves the whole look to each item's children, such as a chart's bars, which read the item's data-state. Checkbox for an on/off choice with a label, md or lg in a settings row. Slider for a range, also orientation="vertical".

Billing
Candle interval, locked while a stream runs
Throughput tier
Chain families
Protocols, variant="outline", joined
Views, size="sm" spacing={2}
Ecosystems, variant="visual" spacing={2}
Usage profile
Optional purposes
Throughput500 RPS
Price range
Alert threshold, vertical

Field

The frame round a control: label, description and error. orientation is vertical, horizontal or responsive (a row from the FieldGroup's md width). data-invalid turns the label red; FieldError says why, from children or react-hook-form errors.

Endpoint alerts

Where and when we tell you about errors.

We send a POST request for every alert.

or

One message per incident, to the account owner.

Region, FieldLabel tiles

Textarea, Switch and RadioGroup

Textarea has the Input's look: sizing="content" grows with the text, fixed keeps its rows. Switch for a setting that applies at once, size default or sm. RadioGroup for one choice from a list, orientation vertical or horizontal, items md or lg; ChoiceGroup covers pills and tiles. Fields take the calm input edge. A checkbox, a radio and an off switch track have no text inside, so their edge is their only cue: they take input-strong (3:1).

Switches
Plan
Protocol, horizontal, lg

Form, with react-hook-form and zod

useForm({ resolver: zodResolver(schema) }). FormField, FormItem, FormControl and FormMessage wire the ids and errors for you. Field with a Controller does the same by hand: pass fieldState.error to FieldError. Submit it empty to see the errors.

Shown in your usage reports.

Email at 80% of the plan.

Submitted values

Nothing yet: submit the form.

Input group

Addons round one field. align is inline-start, inline-end, block-start or block-end. The group draws the Input's fill, edge and focus ring; aria-invalid and disabled on the control reach the whole group. A group with rows or a text area takes the Textarea's corners.

⌘K
https://

Enter a domain, such as app.example.com.

JSON
58 characters

When to use which

Input with icon="start" for a plain search field. InputGroup when the field needs a prefix, a key hint, a button or a row of filters. Give the control a Label or an aria-label; an addon's text does not name it.

Composer, size="lg"

A 56px field with 18px text and rounded-3xl corners; addons, buttons and text step up with it, and no class overrides it. Its focus ring lies on the edge, not 2px outside it, so a field this large shows one frame. Filters are outline buttons with aria-pressed; the hint is a Kbd with size="lg". surface="glass" frosts the field for a composer over the aurora, as the dashboard's home search.

What do you want to query?

Enterto search

Markdown works

Kbd and Spinner

Kbd names a key; KbdGroup holds keys pressed together. Spinner shows work in progress: size is sm, md or lg, and tone is current, muted or accent. A Button with pending shows a Spinner, turns disabled and sets aria-busy; an icon button swaps its icon for the Spinner. A pending link (asChild) ignores a click and Enter.

EscEnterCtrl+B
<Kbd>, <Kbd size="lg">, <KbdGroup>
size="sm" | "md" | "lg" · tone="current" | "muted" | "accent"
Opening the docs
<Button pending>, in every variant, an icon size and a link
pending while the work runs

Do

  • Pair every field with a Label (htmlFor = the field's id).
  • Mark errors with aria-invalid and say how to fix them.
  • Use ChoiceGroup for one of two to five options; Select for more.

Don't

  • Use a placeholder as the label: it disappears as the reader types.
  • Use colour alone for an error: write the message.
  • Shrink field text below 16px on a touch screen, an iPad too: iOS zooms in on focus. The fields here drop to 14px only for a mouse or trackpad.

App controls

Clear choices, reachable actions

Vertical native radios show selection with a dot. Under a finger every control takes a 44 px tap; hit areas meet halfway in a gap.

Touch targets

Under a coarse pointer, phones and tablets, the controls below take a tap of at least 44 by 44 px; REGISTRY.md lists the few that do not. Hit areas meet halfway in a gap and never reach over a neighbour. A mouse sees every control at its base size. Lay out with gap-*: a part reads its room from the nearest gap.

Button size="xs" | "sm" | "icon-xs" | "icon-sm", 12px apart
A header row, 8px apart: Button size="sm" grows to 36px under a finger
ToggleGroup variant="outline" size="sm", joined; ChoiceGroup segmented
Input, InputGroup and Select: 40px under a mouse, 44px under a finger
Input, InputGroup, Select
A field is wide, so its box grows to 44px.
Button, Toggle, ToggleGroup, ChoiceGroup, Select sm
The box keeps its size and an invisible hit area reaches into half the gap on each side. Where the box and the gap make less than 44px, the box grows by the difference.
ToggleGroup, segmented ChoiceGroup
One line of items: the gap is gap-x, so the room above and below is the parent’s. A segmented item takes its room from the track’s padding.
Joined ToggleGroup (spacing 0)
Each hit area spans its own box: neighbours meet on the seam.
Calendar
The days touch, so each column asks for 44px and gives way to 36px, its mouse size, in a narrow container.
Text links, visual variants
A link keeps its line and a visual control takes its children’s size; neither grows.

Vertical ChoiceGroup

Set orientation="vertical" for a list. Each item accepts description and disabled. Group disabled locks every item. Arrow keys skip disabled choices.

variant="segmented" orientation="vertical"
variant="pills" orientation="vertical"
variant="tiles" orientation="vertical"
variant="bare" orientation="vertical"

Shared select trigger

Use selectTriggerVariants on a native button inside DropdownMenuTrigger or PopoverTrigger. The recipe adds SelectTrigger's touch target; pass touch: false to opt out. Both sizes share SelectTrigger's fill, edge, focus, disabled and invalid states. Add a decorative chevron as a child.

SelectTrigger and selectTriggerVariants({ size: "default" })
SelectTrigger and selectTriggerVariants({ size: "sm" })

Copy fields

One copy control for short values, URLs, keys, commands and prompts. Click anywhere on the field or use its copy button.

Copy field inside an input

A read-only input with an inline secondary copy button. The whole field is the button: hover fills it, press fills it further, Tab rings it, and Enter or Space copies. A drag selects text and does not copy. Copy success keeps the button name and announces Copied.

Inline chip in context

The chip centres its one line on its copy and reveal buttons, in a table cell, in 14px and 16px running text and in a stacked row.

NameAPI key
Demo keyfdc22b86-3e38-4eb1-8395
Masked key••••••••••••••••

In your terminal, run dwellir auth login

In your terminal, run dwellir auth login

Endpoint
https://api.example.com
Key
••••••••••••••••

Inline chip

variant=inline. Fits beside running text and truncates within its container.

Install withnpm install @dwellir/sdk

Single-line field

variant=field. A read-only input with an associated label and an inline copy button.

Masked value

masked. Reveal and copy work independently. Copy always uses the full value.

Without a label

Use name to give the button a useful accessible name.

Multi-line read-out

variant=multiline. Fixed height with internal scrolling. Tab into the text to scroll with the keyboard.

Connection promptConnect this project to a Dwellir RPC endpoint. Read the endpoint URL from the environment. Keep API keys on the server. Report connection errors in the interface. Before sending a request: 1. Check the selected network. 2. Check the method parameters. 3. Set a request timeout. After receiving a response: 1. Check the response status. 2. Parse the result. 3. Show the latest block number.

Disabled

disabled. Neither the field nor its buttons can copy or reveal.

Action lists

Full-width action rows for tool and integration cards. Icons stay neutral. The trailing mark explains each action.

Integration actions

Compose ActionListItem with a link, copy action or button. Long labels wrap on phones.

Pending, done and disabled

pending blocks activation and announces progress. done adds a check. Leading icons are slots.

  • In progress
  • Done

Navigation and disclosure

Show one thing at a time

Tabs switch between views of the same kind. The accordion folds answers away until the reader asks; a collapsible folds away one block.

Tabs

A pill track; the active tab takes the ground colour under the aurora wash, with the aurora rim for its border (aurora-current, as on the sidebar's current row). With long labels, variant="stretch" shares a phone's width between the tabs, icon over label (narrow the window to see it). variant="stack" is a column of rich rows beside a preview, as in the homepage's Hyperliquid products. It has no track: place it in a card, and set orientation="vertical" on Tabs, so Up and Down move between the rows. variant="line" underlines the active tab on a rule, as in an app's page header. It stays one row and scrolls sideways when narrow, and it scrolls the active tab into view. Give the triggers flex-1 justify-center and they share the full row. When the tabs stand for states, a tone colours the active one.

Pay per response

1 response = 1 credit, with no compute units. From 20 RPS free to 5,000 RPS.

A flat monthly price for 25 to 1,000 RPS, from $119 a month. No credits and no overages.

Dedicated node clusters for high volume.

1 response = 1 credit, with no compute units. From 20 RPS free to 5,000 RPS.

1 response = 1 credit, with no compute units. From 20 RPS free to 5,000 RPS.

Dedicated node clusters for high volume.

Successful responses this session.

Accordion

For questions and answers. The first item may start open. With forceMount on AccordionContent, closed answers stay in the HTML for search engines.

1 RPC response = 1 API credit. There are no compute units: debug_traceTransaction costs one credit too, on the paid plans. Links and code in an answer take the theme from AccordionContent.

Yes, on every plan.

From 20 RPS on the free plan to 5,000 RPS, with bursts to 10,000.

Collapsible

One block that opens and closes. The plain collapsible has no look: its trigger is a Button through asChild. variant="card" is a card whose header row opens it, for reference entries. With forceMount on CollapsibleContent, closed text stays in the HTML and shows when script is off.

From 20 RPS on the free plan to 5,000 RPS, with bursts to 10,000.

An app shows WTIOIL; the Info and trading APIs take the canonical coin, xyz:CL.

Spot markets go by an @index or a pair label; HIP-3 perpetuals go by dex and symbol.

Do

  • Keep tab labels to one or two words.
  • Write accordion triggers as the reader's question.
  • Put forceMount on content that search engines and readers without script need.

Don't

  • Use tabs for steps in a sequence: the reader cannot see what comes next.
  • Hide the one fact a reader needs to decide in a closed accordion.

App shell

A sidebar for app screens

The navigation of the API platform dashboard: it collapses to icons on a desktop and opens as a sheet on a phone.

Sidebar

shadcn's Sidebar family on the registry sheet, tooltip, separator, input, skeleton and button. SidebarProvider holds the state; SidebarTrigger, the rail and Cmd+B or Ctrl+B toggle it. Collapsed to icons, each row's tooltip names it. A hovered row takes the muted fill; the current row takes the aurora wash and rim (aurora-current, as on a selected tab) and a heavier icon stroke. Here the sidebar is position="contained" in a frame; the app shell keeps the default, fixed. Hover a row for its More action. On a phone, mobileSurface="plain" gives the sheet the card fill without grain, for app screens.

variant
collapsible
side
mobileSurface

Do

  • Give every row an icon and a tooltip, so it reads when collapsed.
  • Mark the current page with isActive and aria-current="page".

Don't

  • Put a status dot on a row or its badge: say the state in words.
  • Use the sidebar on marketing pages: they keep the site navbar.

Overlays

Layers over the page

Open each one. They sit over the navbar and the cookie notice, trap focus where they should, and close on Escape.

Dialog

A short task that blocks the page: confirm, name, create. Placed in the corner, a compact one, such as the cookie preferences. DialogFooter showCloseButton closes a dialog that only informs. surface="plain" drops the grain, for app screens.

Sheet

Longer settings beside the page. Slides in from an edge: right by default, or left, top or bottom. surface="plain" drops the grain, for app screens.

Long content

Content taller than the screen goes in DialogBody, SheetBody or AlertDialogBody. The body scrolls with a quiet bar inside the panel's straight edge; the title and the footer's action stay in view, also on a short phone.

Dropdown menu

Actions on one thing: an account, a key, a row. Checkbox items toggle; radio items pick one of a set. A sub trigger opens a submenu; inset lines an item up with the icons.

Dropdown menu, compact

The sm size, as in an app's header: a tighter menu. The chosen item is tinted and checked on the right.

Popover

Extra detail or a small form, anchored to its trigger. PopoverHeader holds its title and description.

Popover, bare

A row of chips under a field, with no surface of its own. The arrow keys move between the chips; a choice or Escape closes the row.

Tooltip

A short hint for an icon or a term. Never the only copy. Primary is the brand green; sm fits a one-word hint. A disabled button takes no pointer or focus, so its tooltip goes on a focusable span around it (the last one).

Do

  • Give every dialog and sheet a title; it names the layer for screen readers.
  • Put the safe action on the left and the main action on the right.
  • Wrap content that can outgrow a phone in the body part, so the footer's action stays in view.
  • Open one on a phone: the panel takes the focus, not its first field, so the keyboard waits for a tap. Its edges clear the status bar, the notch and the home indicator, and its close button takes a 44px tap.

Don't

  • Open a dialog on page load.
  • Put anything in a tooltip that a touch reader also needs: there is no hover.

Command and dates

Find anything, pick a period

The app parts: a command palette for the dashboard's Cmd+K, and calendars for usage periods and log windows.

Command

A search field over a list that filters as you type. The arrow keys move the highlight and Enter runs the item. surface picks the panel: plain on the page or in a card, bare inside a dialog or a popover.

<CommandDialog> (size="lg"), Cmd+K
<Command surface="bare"> in a Popover
<Command surface="plain">

Calendar

react-day-picker 10 in the theme. It draws no surface: a plain card on the page, a popover in a form. The month buttons take a Button variant through buttonVariant. Today is green and bold; a pick is the primary green; a range fills with the accent tint.

October 2026
mode="single"
October 2026
40
41
42
43
44
captionLayout="dropdown" buttonVariant="secondary" showWeekNumber
September 2026
October 2026
mode="range" numberOfMonths={2} buttonVariant="outline"

Date picker

A Button that opens a Calendar in a Popover. DatePicker closes on a pick; DateRangePicker shows two months and stays open until Escape or a click outside. The trigger takes the Button's variant, size and shape.

<DatePicker>
<DateRangePicker> (usage period, align="end")
<DateRangePicker variant="secondary" size="sm" shape="rounded">

Do

  • Give a palette item a CommandShortcut only when the shortcut works outside the palette.
  • Disable the days a reader cannot pick, such as future days for usage.

Don't

  • Use a Command for two to five options: a ChoiceGroup or a Select shows them all.
  • Ask for a date a reader knows by heart, such as a birthday, with a calendar.

App controls

Option picker

Search a grouped list. Choose one value, or keep several as removable chips inside the field.

No selection
Large catalog: 5,000 options, 150 visible rows
1 selected
Networks
Single selection, default size
4 selected
NetworksEndpoints
Networks, Endpoints, API keys, Team members
Multiple selection, maxChips={2}, controlled
No selection
size="sm", summary={false}, maxVisibleOptions={2}
1 selected
API keys
API keys
Disabled
No selection
Empty list
No selection

Choose a resource.

Invalid field
1 selected
Networks
No resource saved.
Native form: required selection, submitted IDs, and reset
Inside a Dialog, hierarchical options, logo slots, limit, and a field action

The field is the search box. ArrowDown opens the list. Each edit highlights the first match. Enter selects. In single mode, typing starts a fresh query; Escape restores the chosen label. The first Escape closes the list; the next closes its Dialog. In multiple mode, Backspace removes the last selection when search is empty. Each chip has a named remove button. Hidden chips remain selected and can be found through search. Clear removes every selection. Options accept an icon slot for a logo or icon, a group, search keywords, and disabled. Use defaultValue for local state, or value and onValueChange for controlled state. Set level for chain, network, or endpoint indentation. Set maxSelections for a live count and limit. Set summary={false} to hide the count and Clear row. The action slot receives open and onOpenChange. The logo slot accepts any node, including the registry ChainIcon. Set maxVisibleOptions to cap results; the default is 150.

Data

Dense, calm, scannable

Tables carry the facts; separators and skeletons keep the rhythm while data loads, and an empty view says what will be there.

Table

On a plain card. The wrapper scrolls sideways, so a wide table never widens the page.

Sample rows for the layout. Chain families take their aurora hue from ECOSYSTEM_HUE; the protocols column shows highlight, as on the recommended plan.
NetworkFamilyProtocolsNetwork type
EthereumEVMHTTPS, WSSMainnet
PolkadotSubstrateHTTPS, WSSMainnet
CelestiaCosmosHTTPSMainnet
SuiMoveHTTPS, gRPCMainnet

Uptime bar

A green bar per period, with a slice at its foot as tall as the downtime (at least 5%), in the tier's colour: clean from 99.99%, minor from 95%, major from 80%, down below. A period without data is grey. The bars are decorative; the strip is one slider, so the pointer, the arrow keys, Home and End read a period, and screen readers hear its date and uptime. Up and Down step as Right and Left do. A period may carry detail lines: one tooltip follows the active period, and the slider reads the lines too. A lag marker caps the bar.

size="default": 32 px bars, as on the status page
size="fill" start end: the bars fill a positioned parent, as on the homepage
thresholds={{ clean: 100 }}: any downtime slices · readout={false}
details · marker='lag': a tooltip per period, a cap on lagging bars · readout={false}
details · marker="lag" · size="fill": the tooltip follows the bar, inside the viewport
periods={[]}: no slider, the readout says no data

Empty

In place of data the reader cannot see yet. The default is plain and md, as upstream. card stands in for content, dashed outlines where it will appear, xs with align="start" is a note in a list of results. EmptyMedia holds an icon, bare or on a tile (icon); EmptyTitle asChild makes the title a heading. Give a live result role="status".

No API keys yet
Create a key to send your first request.
No requests in this session yet.
No chain matches “zeta”
Dwellir adds chains on request.

No API keys yet

A key lets your app send requests. You can make as many as you need.
No logs yet
Failed requests show here, per key, chain and method.

Scroll area

Content that scrolls inside a fixed height; it takes keyboard focus, so the arrow keys scroll it. The bar shows on hover; type="auto" shows it while the content overflows. overscroll="contain" keeps the wheel in a panel at its end, as in the hero dashboard.

  • eth_blockNumber
  • eth_call
  • eth_chainId
  • eth_estimateGas
  • eth_gasPrice
  • eth_getBalance
  • eth_getBlockByNumber
  • eth_getCode
  • eth_getLogs
  • eth_getTransactionByHash
  • eth_getTransactionReceipt
  • eth_sendRawTransaction

Selectable rows, compact, quiet headers

density="compact" sets 10px cell padding and 36px header rows, for app UI. head="quiet" gives sentence-case, medium-weight, muted headers with normal tracking, for app UI; the default head="eyebrow" is the website look. A Checkbox column takes no right padding and sits on the text line. A row whose disclosure is open (aria-expanded) keeps the hover tint.

KeyCreatedRequests, 30 daysDetails
indexer-prod2 Oct 20261.2M
Allowed origins: Any origin
wallet-staging14 Sep 202686k

Row headers

Key facts, one per row. TableHead scope="row" labels the row; long values wrap with whitespace-normal.

Founded2021, in Uppsala, Sweden
Core offeringHTTP and WebSocket RPC access to 150+ networks through one API key
Uptime SLA99.99% on production endpoints

Code block

Mono on the ground colour, with a copy button. Long lines scroll sideways.

grpcurl -proto v3.proto \
  -H "x-api-key: $DWELLIR_API_KEY" \
  "$HYPERLIQUID_GRPC_ENDPOINT" list

Code block, wrapped

lines="wrap" for one-line payloads, which wrap instead of scrolling.

{"coin": "BTC", "blockNumber": "1140007989", "bids": [{"px": "78497", "sz": "2.59056", "n": 16}]}

Code block, one-line command

copy="block" makes the whole block the copy control: hover tints it, a click, Enter or Space copies, and the icon shows the copied state. A drag still selects text. Use it for one-line commands; add lines="wrap" for a long one.

npx skills add dwellir-public/hyperliquid-skills

Code block with a title

title puts a row over the code: its language or file name. size="lg" keeps code in running text readable on a phone. tokens="shiki" colours Shiki output with two themes (defaultColor: false) by the site theme, as in the blog articles.

TypeScript
const block = await client.getBlockNumber();
console.log(block);

Skeleton

The shape of the content that is on its way. SkeletonRows holds a list or table that has no layout yet: it takes a label for screen readers, the row count, and the row height (sm, default, lg). LoadingSplash covers a whole screen or region when even rows presume too much: it waits out its delay first, so a fast load never flashes it.

Loading your keys

Separator

A hairline between groups. Decorative by default.

Shared nodes
HTTPSWSSgRPC

Do

  • Align numbers to the right and use the same unit in a column.
  • Put tables on a plain card: grain under small text is noise.

Don't

  • Use a table for layout.
  • Show a spinner where a skeleton can show the shape.

App data

Charts and tables for app screens

Usage over time and lists of records, as on the API platform dashboard. Both sit on plain cards and take their colours from the theme.

Chart

shadcn's Recharts wrapper, on plain cards. A ChartConfig names each series and picks its colour, chart-1 to chart-5; the chart sets --color-<key> for the series to read. Hover or focus a chart for the tooltip: indicator is dot, line or dashed. Give the container a height, as here, or keep its 16:9 frame. Name each chart with aria-labelledby on its title and summarise its data in description; a chain series sets its icon. chartConfigForSeries assigns distinct series colours for a set of names. missing defaults to gap, so a bucket with no reading breaks the line; pass missing="zero" only for a range you measured. chartDateAxisProps steps date labels to the width useChartWidth measures on the container, so they never collide, and the quiet preset (QUIET_GRID_PROPS, QUIET_SERIES_PROPS) keeps horizontal guides only and dots on isolated or active points. Under a chart, ChartLegendList aligns its rows on phones, highlights on hover and focus, and folds a long list into +N more.

Requests per day

Area, stacked. Tooltip indicator="line".

From 1 to 8 Oct, HTTPS requests ran from 149,000 to 248,000 a day and WSS requests from 52,000 to 81,000. Both were lowest on 6 Oct and highest on 8 Oct.

Latency

Line. Tooltip indicator="dashed".

Over one day, the median latency ran from 35 to 47 ms and the 95th percentile from 98 to 156 ms. Both were lowest at 04:00 and highest at 12:00.

Requests by chain (thousands)

Bar, stacked, with a legend: all five series colours.

From week 37 to week 40, in thousands of requests: Ethereum rose from 420 to 510, Base from 310 to 400, Arbitrum from 220 to 260, Polygon from 140 to 160 and Sui from 90 to 170.

Requests by chain, measured

missing="zero", the quiet preset, and a stepped date axis.

From 1 to 14 Oct, in thousands of requests a day. Every day was measured, so an idle day reads zero: Base was idle on 4 and 5 Oct, Sui on 3 Oct. Ethereum ran highest, from 198 to 271.

Requests by chain, with no reading

The default, missing="gap": Base breaks on 4 and 5 Oct.

From 1 to 8 Oct, in thousands of requests a day. Base has no reading on 4 and 5 Oct, so its line breaks there instead of reading zero. Ethereum ran from 198 to 252.
  • EthereumMainnet1.8M
  • BaseMainnet, no reading 4 to 5 Oct882K

Data table

A TanStack Table v9 table on the Table part, on a plain card. A string header gets a sort button; enableSorting: false turns it off. selectable adds the checkbox column, columnToggle the columns menu, toolbar the controls before it, and pageSize the pages (0 shows every row). options passes any other TanStack option, such as manualPagination for server data. head="quiet" passes the Table's quiet headers, sort buttons included.

Created
Allowed origins
indexer-prod2 Oct 2026
1,204,000
Any originActive
wallet-staging14 Sep 2026
86,000
app.example.comActive
bot-arbitrage9 Sep 2026
3,410,000
Any originActive
explorer-backfill28 Aug 2026
912,000
Any originRevoked
dashboard-demo21 Aug 2026
4,100
demo.example.comActive

0 of 8 rows selected

Data table, loading

loading shows skeleton rows in the table's shape and hides the pages. density="compact" is the Table's app density.

Created
Allowed origins

Data table, empty

With no rows, the table shows an Empty note. empty sets its content.

Created
Allowed origins
No API keys yet
Create a key to start sending requests.

Do

  • Pick series colours from chart-1 to chart-5, in order.
  • Name every series in the config, so the tooltip and legend say what a colour means.

Don't

  • Paint a series with a raw colour or an aurora utility.
  • Show more than five series in one chart: split it or group the rest.

Responsive tables

Keep each record together

Opt in to stacked records in a narrow container, or pin a comparison table’s first column. Existing tables keep their current layout.

Data table, stacked below 3xl

Set stack="auto". The default stackBelow="3xl" means a container narrower than 768px, even inside a desktop rail or dialog. stack="never" is the default. Sorting, selection, pages, hidden columns and expansion share one row model. Name each navigation landmark with paginationLabel when a page has several tables.

Details
Production indexer
1,204,000
Operations
indexer
Wallet staging
86,000
Product
wallet

0 of 3 rows selected

A narrow container on a wide screen

This container uses stackBelow="xl" (576px). Supported container tokens: lg (512px), xl (576px), 2xl (672px), 3xl (768px), and 4xl (896px). The wide layout starts at the chosen size.

Details
Production indexer
1,204,000
Operations
indexer
Wallet staging
86,000
Product
wallet

0 of 3 rows selected

Column roles and plain tables

Use column meta.stackRole: title for the identity, action for row controls, and hidden for desktop-only fields. Set meta.stackLabel for functional headers. Other fields use the header string or column ID. Keep the identity column visible with enableHiding: false. Plain TableCell takes label and stackRole directly.

ProjectOwnerActions
Production indexer
Operations

Sticky first column, four grounds

Set stickyFirstColumn and match surface to the ground: card (default), background, muted inside a card, or glass over the aurora. The edge appears only after horizontal scrolling. Row hover and selection keep an opaque fill.

IncludedFreeDeveloperGrowthScale
Monthly responses1 million25 million150 million500 million
Requests per second251005001,000
Scroll sideways. The feature name stays visible.
IncludedFreeDeveloperGrowthScale
Monthly responses1 million25 million150 million500 million
Requests per second251005001,000
Scroll sideways. The feature name stays visible.
IncludedFreeDeveloperGrowthScale
Monthly responses1 million25 million150 million500 million
Requests per second251005001,000
Scroll sideways. The feature name stays visible.
IncludedFreeDeveloperGrowthScale
Monthly responses1 million25 million150 million500 million
Requests per second251005001,000
Scroll sideways. The feature name stays visible.

Loading outside the scroller

Stacked and sticky data tables place loading and empty states outside the scroller. statePlacement="outside" opts in without changing layout. Skeletons match the visible column count, including selection.

Loading rows

Empty content stays on screen

The empty slot can include a recovery action. Plain Table accepts state="loading" or state="empty", visibleColumns, loadingRows, and empty. With no new props, existing states stay unchanged.

No projects yet
Create a project to start tracking requests.
Create project

Stats and page header

Figures and the title row of a page

One read-out for every metric, and one header for every app page. The dashboard's stat cards, usage rows and top bar, and the status page's uptime figures, all read from these two parts.

Stat

A label, a figure in tabular numbers, and quiet meta. StatGroup answers to its own width: a compact list in a narrow card, a row in a wide one. Colour marks a state only: limited requests take the warning tone, the rest stay neutral. A stat carries no surface; it sits on a plain card here, as in the dashboard.

Responses this cycle4,281,903
+12.4%vs last cycle
Average RPS48.2req/s
Limited requests1,204
+310past 24 hours

Inline stats, states, and the count-up

layout="inline" reads as a list: label left, figure right. The same part shows a skeleton while it loads and a dash when a figure is not available. animate counts a numeric value up as it changes, and skips the count under reduced motion; screen readers hear the final figure once. A ready-made string takes a unit, as an uptime figure does.

Peak RPS312
Rate limit500req/s
Uptime, 30 days99.98%
ResponsesLoading Responses
Burst limitNot available
Error rate0.4%
Responses today128,400
Requests92,140
past 7 days
Errors18
past 7 days

Uptime status tones

Each figure uses its status tier's ink, so it matches the uptime bar.

Operational100%
Minor downtime99.98%
Major downtime98%
Down90%
No dataNot available

Page header

A muted icon slot, the title with a quiet description, secondary readings beside them, and the page's actions; a tabs or toolbar row goes under it all as children. sticky keeps it at the top of its scroll container: clear at rest, frosted glass with a hairline once the page scrolls under it. The title truncates on a phone and the actions shrink before they cover it. size="lg" is the same part as a page lead, below the frame.

Usage

Requests across every API key

Scroll the overview frame: the header stays at the top and frosts over the rows under it.

Scroll the overview frame: the header stays at the top and frosts over the rows under it.

Scroll the overview frame: the header stays at the top and frosts over the rows under it.

Scroll the overview frame: the header stays at the top and frosts over the rows under it.

Scroll the overview frame: the header stays at the top and frosts over the rows under it.

Scroll the overview frame: the header stays at the top and frosts over the rows under it.

Scroll the overview frame: the header stays at the top and frosts over the rows under it.

Scroll the overview frame: the header stays at the top and frosts over the rows under it.

Ethereum mainnet

Archive node in the eu-north region, with debug and trace methods.

Do

  • Let StatGroup answer to the card's width, not the viewport's.
  • Pick a tone only when the figure itself is the news: a limit hit, an error rate.
  • Give a delta its tone apart from its arrow: up can be bad news.
  • Head a group outside the part, or name it with aria-label.

Don't

  • Paint every figure green: neutral is the default, colour is the state.
  • Put the only copy of important text in a hint tooltip.
  • Wrap a stat in its own card inside a group: one card holds the group.
  • Build a page's title row by hand next to a PageHeader.

Feedback

Tell the reader what happened

Messages in the page, questions that block it, progress and toasts. The tones reuse the theme's inks: green for done, orange for a warning, red for a failure.

Alert

An inline message about the content around it. It stays until the cause goes away. The theme has no info colour: an info message is default with an info icon.

Alert dialog

A question the reader must answer before going on. It has no close button and does not close on a click outside. Cancel takes the focus. AlertDialogMedia puts an icon over the title. surface="plain" drops the grain, for app screens.

Progress

How far a task or a quota has come. size sets the height; variant warns near a limit or shows a failure. A value past the maximum shows a full bar.

size="lg"
size="lg" variant="warning" value={112}
md (default)
variant="destructive"
size="sm"

Avatar

A person: their photo, or their initials while it loads or when there is none. Give the image the person's name as alt. AvatarBadge marks a corner; say what it means in text nearby. AvatarGroup overlaps a team and ends with AvatarGroupCount.

GNBC
size="sm"
GNBC
size="md"
GNBC
size="lg"
GNEFBC
+3
AvatarGroup, size="sm"
GNEFBC
+3
AvatarGroup, size="md"
GNEFBC
+3
AvatarGroup, size="lg"

Toast (sonner)

The result of an action, in the corner for a few seconds. Mount <Toaster /> once, then call toast() from sonner anywhere. The promise toast fails every second time.

Do

  • Say what happened and what to do next, in the title and one sentence.
  • Use a toast for the result of an action; use an alert for a state that stays.

Don't

  • Put the only copy of an error in a toast: it closes on its own.
  • Ask with an alert dialog when Undo would do.

Brand blocks

The parts that make it Dwellir

Pages are built from these. They carry the sheets, the grain and the aurora, so a new page looks like the homepage without new CSS.

Section

components/brand/section.tsx

One band of a page: tone="ground", "alt" or "sky", plus aurora for a still curtain. Keeps data-au-band, so the solar wind shows inside the sheets.

SectionHeading

components/brand/section-heading.tsx

Eyebrow, title and lede. size="display" with as="h1" once per page; the default is a section h2.

PageHero

components/brand/page-hero.tsx

The opening of a page: the h1 on a sky sheet, with a lede, actions, a visual and the aurora. layout is center, split or lead. See the samples.

Container

components/brand/container.tsx

The centred column: page (160rem), narrow (137.5rem) or prose.

Prose

components/brand/prose.tsx

Long text from plain HTML: headings, paragraphs, lists, links, code. In a max-w-3xl column. size="lg" for text read from start to end. Tables go in the Table part.

AuroraCurtain

components/brand/aurora-curtain.tsx

A still CSS curtain for places without the hero canvas. No frame cost. tint takes a CSS colour for the lower rays, such as a chain's icon colour.

IconChip

components/brand/icon-chip.tsx

A tinted square for a card's icon. tone is one of the four aurora hues; a grid of cards cycles iconChipTone(index), a lone chip stays green. tone="neutral" is a grey chip for icons that carry no meaning, as on app screens. nested takes the smallest radius, for a chip inside a link's hover fill, so its corners stay concentric with the fill's.

grain · grain-sky

styles/design-system.css

Utilities: the card grain (overlay blend) and the sky grain.

rim · rim-hover · glass

styles/design-system.css

Utilities behind Card's rim and surface="glass". Reach for the Card props first. Every pane shares one blur, --au-glass-blur: glass is 80% solid, the navbar pill and the hero window 70%.

Monthly billing, no lock-in

Every plan bills monthly with no long-term commitment. See pricing for the plans, and call eth_chainId to check an endpoint.

What you get

  • One API key for 150+ networks
  • Trace and debug APIs on every paid plan
<Prose>

About the role

You will build the software that runs our nodes, from monitoring to the tools that deploy new chains.

  1. Talk to the team
  2. Solve a short task
<Prose size="lg">
<IconChip tone="neutral | green | violet | rose | gold">
<IconChip size="sm | md | lg | xl">
A link's hover fill
<IconChip nested>
  • No tint: the palette
  • tint="var(--au-dot-4)"
  • fade="left", on the right
  • fade="right", on the left
  • fade="both", in the middle

Do

  • Alternate ground and alt bands; open and close a page with a sky sheet.
  • Keep wrappers around a Section transparent, so the wind shows through.

Don't

  • Put two alt sheets next to each other.
  • Paint a background on a wrapper above a sheet: it hides the wind.

A sky sheet

Ship UI without the clean-up

Section tone="sky" with aurora, a centred SectionHeading and one primary action: the shape of a closing call to action.

Aurora canvas

The moving curtain, for any app

AuroraCanvas draws the hero's curtain on one canvas, in a worker, at 24 frames a second. It needs no solar wind. Until the canvas draws, a CSS still of the same curtain holds its place.

Live and still

The canvas fills its nearest positioned parent. It pauses off screen and in a hidden tab, and draws one still frame for reduced motion. The still on the right is what shows before JavaScript runs.

<AuroraCanvas />
<AuroraCanvas placement="footer" />
The CSS still (data-au-still)

Do

  • Use AuroraCanvas for one moving curtain: a dashboard header or a page backdrop.
  • Use the still AuroraCurtain where many curtains share a screen. It costs no frame time.
  • Pass quiet where text sits on the curtain, so the light thins out there.

Don't

  • Put a moving curtain behind dense tables or forms: it pulls the eye from the data.
  • Install solar-wind for the curtain alone. The wind is for marketing pages.

Page hero

One hero for every page

PageHero opens a page: the h1 on a sky sheet, with a lede, the actions, a visual and the aurora. Pick a layout; the hero keeps the title cap and stacks the actions on phones, so nothing moves when the fonts load.

The samples below use as="h2", as this page has its own h1. On a page, pass the live aurora as backdrop.

layout: center

Centred: a content page

The default without a visual: about, contact, the legal pages, the blog archive.

layout: split · width: narrow · space: tall

Split: a product page.
The accent, a second line.

The default with a visual: copy and visual in equal columns from lg, the visual under the copy below.

A visual

Beside the copy from lg.

100 RPS

layout: lead

Lead: the Hyperliquid pagesThe accent, a smaller line

The copy leads in 7 of 12 columns at the top; the visual takes 5 and centres on the copy.

A visual

In 5 of 12 columns.

100 RPS

Do

  • Use one PageHero per page, with the h1 as its title.
  • Put the promise in accent: it reads as the second line of the h1.
  • Give it two actions at most: the main one first.

Don't

  • Build a hero from a Section and a SectionHeading by hand.
  • Put a visual in a centred hero: use split or lead.

How to build with it

Four rules

For engineers and their agents. The full guide is in CLAUDE.md under “Design system”.

  1. 01

    Build from the parts

    Compose pages from components/ui and components/brand. Arrange them with layout classes on plain elements: grid, gap-6, md:grid-cols-3.

  2. 02

    Never restyle a part

    Give a part layout classes only (w-full, mt-6). Its colour, type, padding and shape come from its variants. No new styled-components, no raw colours, no p-[13px].

  3. 03

    Run the linter

    npm run lint:ds checks every design-system file. Its errors say what to use instead; fix them before you commit. CI runs it on every pull request.

  4. 04

    Change a part and its base styles

    Add variants to the part's cva. Shared bases live in styles/design-system-parts.css. Keep overrides visible to cn(). Update the registry theme, then run npm run registry:build.

Rune icons

Dwellir's icon set: 176 icons cut as runes. They replace Tabler, lucide and react-icons. Install them with @dwellir/rune-icons. They are decorative by default.

Navigation and layout

37 icons, in components/brand/rune-icons/navigation.tsx.

  • RuneHomeHome: Othala, the homestead.
  • RuneArrowLeftGo back, or move left: Tiwaz, the arrow rune, laid down.
  • RuneArrowRightGo on, or move right: Tiwaz, the arrow rune, laid down.
  • RuneArrowUpMove up, or sort ascending: Tiwaz, the arrow rune.
  • RuneArrowDownMove down, or sort descending: Tiwaz, turned over.
  • RuneArrowUpDownSortable, not sorted yet: one stave with both Tiwaz heads.
  • RuneArrowUpRightOpen or go to a destination: an arrow cut up and out.
  • RuneArrowDownRightFall or go down and out: an arrow cut down and out.
  • RuneChevronUpOpen upward, or collapse: half a Gebo.
  • RuneChevronDownOpen downward, or expand: half a Gebo.
  • RuneChevronLeftPrevious: half a Gebo, pointing back.
  • RuneChevronRightNext, or a link onward: half a Gebo, pointing on.
  • RuneChevronsUpScroll or jump up: two chevrons.
  • RuneChevronsDownScroll or jump down: two chevrons.
  • RuneChevronsLeftJump back several steps: two chevrons.
  • RuneChevronsRightJump on several steps: two chevrons.
  • RuneChevronFirstThe first page: a chevron against a stave.
  • RuneChevronLastThe last page: a chevron against a stave.
  • RuneSelectorA select or combobox that opens a list: a chevron up over a chevron down.
  • RuneSidebarShow or hide the sidebar: a frame with a side panel.
  • RuneSidebarCollapseCollapse the sidebar: the frame, with a chevron pointing in.
  • RuneSidebarExpandExpand the sidebar: the frame, with a chevron pointing out.
  • RuneGripDrag to reorder: two rows of cut studs.
  • RuneMoreMore actions: three cut studs in a row.
  • RuneMoreVerticalMore actions, in a narrow column: three cut studs, stacked.
  • RuneMenuOpen the menu: three staves.
  • RuneMaximizeFull screen, or open larger: two arrows cut out to the corners.
  • RuneMinimizeLeave full screen, or open smaller: two arrows cut in from the corners.
  • RuneExternalLinkA link that leaves the site: an arrow out of a frame.
  • RuneLinkA link or a URL: two cut links on a diagonal, interlocked as RuneChains' links are.
  • RuneColumnsShow or choose columns: a frame cut in three.
  • RuneLayoutGridA grid view: four cut tiles.
  • RuneDashboardThe dashboard, or an overview: four tiles of two heights.
  • RuneWindowA window, an app or a transport: a frame with a title bar.
  • RuneEnterEnter, return or insert: an arrow that turns down and back.
  • RuneLoginSign in: Tiwaz, the arrow, into the door.
  • RuneLogoutSign out: Tiwaz, the arrow, through the door.

Actions

33 icons, in components/brand/rune-icons/actions.tsx.

  • RuneSearchSearch: a cut lens on a straight handle.
  • RunePlusAdd: two crossed staves.
  • RuneMinusRemove one, or reduce: one stave, laid down.
  • RuneCheckDone, chosen or true: a check cut in two strokes.
  • RuneCloseClose or clear: Gebo, the crossing.
  • RuneCopyCopy: one tablet over another.
  • RuneDownloadDownload: an arrow down into a tray.
  • RuneUploadUpload: an arrow up out of a tray.
  • RuneCloudDownloadDownload from the cloud, or a bulk export: an arrow down out of a cut cloud.
  • RuneFileDownloadDownload a file, such as a CSV: an arrow down inside a page.
  • RuneFileExportExport to a file: an arrow out of a page.
  • RuneShareShare: three Ingwaz nodes joined.
  • RuneRefreshRefresh or reload: two arrows round a cut ring.
  • RuneResetReset or undo: one arrow turning back round a cut ring.
  • RuneHistoryHistory, or recent items: the reset arrow round a clock face.
  • RuneRepeatRepeat, or a recurring job: two arrows round a loop.
  • RuneTrashDelete: a bin with a lid.
  • RuneSaveSave: a floppy disk with its shutter and label.
  • RuneFilterFilter: a cut funnel.
  • RuneSortAscendingSort ascending: staves that grow, beside an arrow up.
  • RuneSortDescendingSort descending: staves that shrink, beside an arrow down.
  • RunePlayPlay or start: a cut wedge.
  • RunePausePause: two staves.
  • RuneStopStop: a square block.
  • RuneStopSolidStop, as a filled button: the stop block, solid.
  • RuneRewindRewind or replay: two wedges turned back.
  • RuneEyeShow, or a preview: a cut eye with an Ingwaz pupil.
  • RuneEyeOffHide: the cut eye, struck through.
  • RuneEditEdit or write: a cut pencil.
  • RuneSendSend: a paper dart, cut.
  • RunePinPin to the top: a cut pushpin.
  • RunePowerTurn on, or activate: the power mark, a stave through a cut ring.
  • RunePowerOffTurn off, or deactivate: the power mark, struck through.

Status and feedback

28 icons, in components/brand/rune-icons/status.tsx.

  • RuneInfoInformation, or a note: an i in a cut ring.
  • RuneHelpHelp, or a question: a cut ? in a cut ring.
  • RuneAlertAn alert or an error message: a ! in a cut ring.
  • RuneWarningA warning: a ! in a cut triangle.
  • RuneErrorFailed, or an error toast: Gebo, the crossing, in a cut ring.
  • RuneSuccessSuccess, or done: a check in a cut ring.
  • RuneSuccessSolidSuccess, as a filled mark: the success ring, solid, with the check cut out.
  • RuneLoaderLoading: three quarters of a cut ring. Spin it with `animate-spin`.
  • RuneCircleA radio choice, or an empty mark: a cut ring.
  • RuneDotA chosen radio, or a bullet: a solid cut stud.
  • RuneHandStopStop, or rate limited: a raised hand.
  • RuneBellNotifications: a cut bell.
  • RuneBellOffNotifications off, or unsubscribe: the cut bell, struck through.
  • RuneClockTime, or a duration: hands on a cut clock face.
  • RuneExpiringExpiring, or timed out: a cut clock face with Gebo at its edge.
  • RuneTimerA timer, or a trial period: a cut stopwatch.
  • RuneFlameA streak, or hot: a cut flame.
  • RuneBoltFast, unmetered, or power: Sowilo, the lightning rune.
  • RuneSparklesAI, or something new: a cut four-point star and two sparks.
  • RuneStarA favourite, or a top pick: a cut five-point star.
  • RuneHeartLiked, or a favourite: a cut heart.
  • RuneCrownThe owner, or a top tier: a cut crown.
  • RuneTrophyA win, an award or a ranking: a cut cup with handles.
  • RuneShieldSecurity, or protected: a cut shield.
  • RuneShieldCheckVerified, or secure: a check on the cut shield.
  • RuneShieldLockLocked down, or private: a keyhole on the cut shield.
  • RuneShieldAlertNot authorised: a ! on the cut shield.
  • RuneNotFoundPage not found: 404, cut in straight strokes.

Data and charts

23 icons, in components/brand/rune-icons/data.tsx.

  • RuneUsageUsage: staves rising, each with a twig.
  • RuneLogsLogs: the lines of a runestone.
  • RuneChartBarA bar chart: three cut bars on a base line.
  • RuneChartLineA line chart: a cut line over a base line.
  • RuneChartAreaAn area chart: the cut line, closed down to the base.
  • RuneChartCandleA candle chart: two candles with wicks.
  • RuneChartStackedStacked bars: two bars, each cut in two.
  • RuneChartGroupedBars side by side: two pairs of staves.
  • RuneActivityActivity, or a live stream: a cut pulse line.
  • RuneGaugeSpeed, rate or load: a needle on a cut dial.
  • RuneCalculatorCalculate, or a cost estimate: a cut calculator.
  • RuneNumberA number field, or a count: 123, cut in straight strokes.
  • RuneHashAn ID, a hash or a chain ID: a cut #.
  • RunePercentA percentage, or uptime: two cut studs and a slash.
  • RuneDollarMoney in dollars: a cut $.
  • RuneInfinityUnlimited: two Ingwaz loops, crossed.
  • RuneListA list: three studs and three staves.
  • RuneListOrderedAn ordered list: a cut 1 and 2 beside three staves.
  • RuneListCheckA checklist: three checks beside three staves.
  • RuneReportA report, or analytics: bars inside a page.
  • RuneCalendarA date: a cut calendar.
  • RuneCalendarEventAn event on a date: the calendar, with one day marked.
  • RuneCalendarStatsStats over time, or a year in review: the calendar, with bars.

Product nouns

55 icons, in components/brand/rune-icons/nouns.tsx.

  • RuneChainsChains, or networks: two Ingwaz, linked.
  • RuneHyperliquidHyperliquid: the Hyperliquid mark, cut as Chains is: a small link and a larger one, centred on one line as the mark's two lobes are.
  • RuneKeyAn API key: a carved key with an Ingwaz head.
  • RuneAgentsAgents, or the in-app assistant: Mannaz, the one who acts.
  • RuneBotA bot, or a prompt for an agent: a cut robot head.
  • RuneDocsDocs: an open book with a straight spine.
  • RuneBookA guide, or a closed book: a cut book with its pages.
  • RuneSettingsSettings: a six-tooth cog cut from straight lines, with an Ingwaz hub.
  • RuneProfileThe user's own profile: a cut head over shoulders.
  • RuneUserPlusInvite a member: a cut head over shoulders, with a plus.
  • RuneAccountThe organisation's account: a hall.
  • RuneTeamThe team, or users: two figures.
  • RunePlanA plan, and moving up one: Tiwaz, the arrow rune.
  • RuneBillingBilling, or a price: a hacked-silver coin with a cut $.
  • RuneCoinsCredits, or a balance: two hacked-silver coins.
  • RuneCreditCardA card payment: a cut card with its stripe.
  • RuneReceiptA receipt, or an invoice: a slip with a torn edge.
  • RuneDiscountA discount: a % in a cut ring.
  • RuneServerA server, or a node: two cut units with their lights.
  • RuneServerCogA managed server, or sign-in to one: a cut unit and a small cog of two squares.
  • RuneDatabaseA database, or archive data: a cut drum.
  • RuneCloudThe cloud, or hosting: a cut cloud.
  • RuneNetworkA network: three cut boxes, joined.
  • RuneWorldThe world, a region, or the public web: a cut globe.
  • RuneApiAn API: the letters API, cut.
  • RuneChipA chip, or compute: a cut die with pins.
  • RuneComponentA component: four Ingwaz, set as a cross.
  • RunePlugAn integration, or connected: a cut plug.
  • RuneWebhookA webhook: three cut rings, each hooked to the next.
  • RuneTerminalA terminal, or a CLI: a prompt and a cursor.
  • RuneCodeCode: two chevrons and a slash.
  • RuneBracesJSON, or a config object: a pair of cut braces.
  • RuneFileA page or a document: a page with lines.
  • RuneFileCodeA code file, or a schema: a page with two chevrons.
  • RunePuzzleAn add-on, or an extension: a puzzle piece with cut knobs.
  • RuneRocketA launch, or getting started: a cut rocket.
  • RuneLayersLayers, a stack, or an order book: three cut sheets.
  • RuneCompassGuides, or explore: a needle in a cut ring.
  • RuneLifebuoySupport: a cut lifebuoy.
  • RuneHeadsetTalk to a person, or live support: a cut headset.
  • RuneMailEmail: a cut envelope.
  • RunePhonePhone: a cut handset.
  • RuneMessageA message, or a chat: a cut speech bubble.
  • RuneInboxAn inbox, or nothing to show yet: a cut tray.
  • RuneLockLocked, or private: a cut padlock.
  • RuneRssA feed, or RSS: a stud and two cut waves.
  • RuneBroadcastA broadcast, or a live feed: a stud between cut waves.
  • RuneWavesA raw stream, or water: three cut waves.
  • RuneWrenchTools, or maintenance: a cut wrench.
  • RuneNutA hex nut: hardware, or a fixed part. A cut hexagon round a cut ring.
  • RuneFuelGas, or fees: a cut fuel pump.
  • RuneMonitorThe system theme, or a desktop: a cut monitor on its stand.
  • RuneSunThe light theme: Sowilo's sun, an Ingwaz core with eight rays.
  • RuneMoonThe dark theme: a cut crescent.
  • RuneBeerHomebrew: a cut pint glass.

Filled states and the spinner

runeSolid() fills a rune for a filled state, with the same outline as its stroked twin. RuneLoader is three quarters of a cut hexagon; spin it with animate-spin.

RuneStop, RuneStopSolid
RuneSuccess, RuneSuccessSolid
RuneLoader className="animate-spin"

Size

Without size, CSS sizes the icon, as a sidebar does. Pass size in px to set width and height together. Every icon must read at 14px.

No size prop, [&_svg]:size-5 on the parent
size={14}
size={16}
size={18}
size={20}
size={24}

Colour

The icons draw in currentColor. Set a theme text colour; switch the page theme to see both grounds.

text-foreground
text-muted-foreground
text-accent-ink

Name map

rune-icon-map.json maps every Tabler, lucide and react-icons name the dashboard, the site and the status page use, and their near neighbours, to a rune: 201 tabler, 488 lucide, 440 react-icons names. A name marked ≈ maps to a rune with a different picture; hover it for the difference.

172 of 172 entries: the runes, and the RuneIcon and RuneIconProps types. Each lucide name also maps with an Icon suffix.
RuneTablerlucidereact-icons
RuneAccount≈IconBuilding≈Building≈LuBuilding, ≈TbBuilding
RuneActivityIconActivityActivityLuActivity, TbActivity
RuneAlertIconAlertCircle, IconAlertOctagon, IconExclamationCircleAlertCircle, CircleAlertLuAlertCircle, LuCircleAlert, TbAlertCircle, TbAlertOctagon, TbExclamationCircle
RuneApiIconApiNoneTbApi
RuneArrowDownIconArrowDownArrowDown, MoveDownLuArrowDown, LuMoveDown, TbArrowDown
RuneArrowDownRightIconArrowDownRightArrowDownRight, MoveDownRightLuArrowDownRight, LuMoveDownRight, TbArrowDownRight
RuneArrowLeftIconArrowLeftArrowLeft, MoveLeftLuArrowLeft, LuMoveLeft, TbArrowLeft
RuneArrowRightIconArrowRightArrowRight, MoveRightLuArrowRight, LuMoveRight, TbArrowRight
RuneArrowUpIconArrowUpArrowUp, MoveUpLuArrowUp, LuMoveUp, TbArrowUp
RuneArrowUpDownIconArrowsSort, IconArrowsUpDown, IconArrowsVerticalArrowDownUp, ArrowUpDown, MoveVerticalLuArrowDownUp, LuArrowUpDown, LuMoveVertical, TbArrowsSort, TbArrowsUpDown, TbArrowsVertical
RuneArrowUpRightIconArrowUpRightArrowUpRight, MoveUpRightLuArrowUpRight, LuMoveUpRight, TbArrowUpRight
RuneBeerIconBeerBeerLuBeer, TbBeer
RuneBellIconBellBellLuBell, TbBell
RuneBellOffIconBellOffBellOffLuBellOff, TbBellOff
RuneBilling≈IconCoin≈BadgeDollarSign, CircleDollarSign≈LuBadgeDollarSign, LuCircleDollarSign, ≈TbCoin
RuneBoltIconBoltZapLuZap, TbBolt
RuneBookIconBook2Book, BookTextLuBook, LuBookText, TbBook2
RuneBotIconRobotBotLuBot, TbRobot
RuneBracesIconBracesBracesLuBraces, TbBraces
RuneBroadcastIconBroadcastRadioLuRadio, TbBroadcast
RuneCalculatorIconCalculatorCalculatorLuCalculator, TbCalculator
RuneCalendarIconCalendarCalendarLuCalendar, TbCalendar
RuneCalendarEventIconCalendarEventNoneTbCalendarEvent
RuneCalendarStatsIconCalendarStatsNoneTbCalendarStats
RuneChartAreaIconChartAreaAreaChart, ChartAreaLuAreaChart, LuChartArea, TbChartArea
RuneChartBarIconChartBarBarChart, BarChart3, ≈ChartBar, ChartColumn, ChartColumnBigLuBarChart, LuBarChart3, ≈LuChartBar, LuChartColumn, LuChartColumnBig, TbChartBar
RuneChartCandleIconChartCandleCandlestickChart, ChartCandlestickLuCandlestickChart, LuChartCandlestick, TbChartCandle
RuneChartLineIconChartLineChartLine, ≈ChartSpline, LineChartLuChartLine, ≈LuChartSpline, LuLineChart, TbChartLine
RuneChartStackedNoneChartColumnStackedLuChartColumnStacked
RuneCheckIconCheckCheckLuCheck, TbCheck
RuneChevronDownIconChevronDownChevronDownLuChevronDown, TbChevronDown
RuneChevronFirstIconChevronLeftPipeChevronFirstLuChevronFirst, TbChevronLeftPipe
RuneChevronLastIconChevronRightPipeChevronLastLuChevronLast, TbChevronRightPipe
RuneChevronLeftIconChevronLeftChevronLeftLuChevronLeft, TbChevronLeft
RuneChevronRightIconChevronRightChevronRightLuChevronRight, TbChevronRight
RuneChevronsDownIconChevronsDownChevronsDownLuChevronsDown, TbChevronsDown
RuneChevronsLeftIconChevronsLeftChevronsLeftLuChevronsLeft, TbChevronsLeft
RuneChevronsRightIconChevronsRightChevronsRightLuChevronsRight, TbChevronsRight
RuneChevronsUpIconChevronsUpChevronsUpLuChevronsUp, TbChevronsUp
RuneChevronUpIconChevronUpChevronUpLuChevronUp, TbChevronUp
RuneChipIconCpuCpuLuCpu, TbCpu
RuneCircleIconCircleCircleLuCircle, TbCircle
RuneClockIconClockClockLuClock, TbClock
RuneCloseIconXXLuX, TbX
RuneCloudIconCloud, ≈IconCloudComputingCloudLuCloud, TbCloud, ≈TbCloudComputing
RuneCloudDownloadIconCloudDownloadCloudDownloadLuCloudDownload, TbCloudDownload
RuneCodeIconCodeCode, Code2, CodeXmlLuCode, LuCode2, LuCodeXml, TbCode
RuneCoinsIconCoinsCoinsLuCoins, TbCoins
RuneColumnsIconColumns, IconColumns3Columns, Columns3LuColumns, LuColumns3, TbColumns, TbColumns3
RuneCompassIconCompassCompassLuCompass, TbCompass
RuneComponentIconComponentsComponentLuComponent, TbComponents
RuneCopyIconCopyCopyLuCopy, TbCopy
RuneCreditCardIconCreditCardCreditCardLuCreditCard, TbCreditCard
RuneCrownIconCrownCrownLuCrown, TbCrown
RuneDashboardIconLayoutDashboardLayoutDashboardLuLayoutDashboard, TbLayoutDashboard
RuneDatabaseIconDatabaseDatabase, ≈DatabaseZapLuDatabase, ≈LuDatabaseZap, TbDatabase
RuneDiscountIconDiscount, IconDiscount2BadgePercentLuBadgePercent, TbDiscount, TbDiscount2
RuneDocsIconBookBookOpen, BookOpenTextLuBookOpen, LuBookOpenText, TbBook
RuneDollarIconCurrencyDollarDollarSignLuDollarSign, TbCurrencyDollar
RuneDotIconPoint, IconPointFilledDotLuDot, TbPoint, TbPointFilled
RuneDownloadIconDownloadDownloadLuDownload, TbDownload
RuneEditIconEdit, IconPencilEdit, Edit2, Edit3, Pen, Pencil, PencilLine, PenLine, ≈SquarePenLuEdit, LuEdit2, LuEdit3, LuPen, LuPencil, LuPencilLine, LuPenLine, ≈LuSquarePen, TbEdit, TbPencil
RuneEnterIconCornerDownLeftCornerDownLeftLuCornerDownLeft, TbCornerDownLeft
RuneErrorIconCircleXCircleX, OctagonX, XCircleLuCircleX, LuOctagonX, LuXCircle, TbCircleX
RuneExpiringIconClockXNoneTbClockX
RuneExternalLinkIconExternalLinkExternalLink, SquareArrowOutUpRightLuExternalLink, LuSquareArrowOutUpRight, TbExternalLink
RuneEyeIconEyeEyeLuEye, TbEye
RuneEyeOffIconEyeOffEyeOffLuEyeOff, TbEyeOff
RuneFileIconFile, IconFileTextFile, FileTextLuFile, LuFileText, TbFile, TbFileText
RuneFileCodeIconFileCodeFileCodeLuFileCode, TbFileCode
RuneFileDownloadIconFileDownloadFileDownLuFileDown, TbFileDownload
RuneFileExportIconFileExportFileOutputLuFileOutput, TbFileExport
RuneFilterIconFilterFilter, Funnel, ≈ListFilterLuFilter, LuFunnel, ≈LuListFilter, TbFilter
RuneFlameIconFlameFlameLuFlame, TbFlame
RuneFuelIconGasStationFuelLuFuel, TbGasStation
RuneGaugeIconGaugeGaugeLuGauge, TbGauge
RuneGripIconGripVerticalGripVerticalLuGripVertical, TbGripVertical
RuneHandStopIconHandStopHandLuHand, TbHandStop
RuneHashIconHashHashLuHash, TbHash
RuneHeadsetIconHeadsetHeadsetLuHeadset, TbHeadset
RuneHeartIconHeartHeartLuHeart, TbHeart
RuneHelpIconHelp, IconHelpCircleCircleHelp, HelpCircleLuCircleHelp, LuHelpCircle, TbHelp, TbHelpCircle
RuneHistoryIconHistoryHistoryLuHistory, TbHistory
RuneHomeIconHome, IconHome2Home, HouseLuHome, LuHouse, TbHome, TbHome2
RuneIconIcon, TablerIconLucideIconNone
RuneIconPropsIconProps, TablerIconsPropsLucidePropsNone
RuneInboxIconInboxInboxLuInbox, TbInbox
RuneInfinityIconInfinityInfinityLuInfinity, TbInfinity
RuneInfoIconInfoCircleInfoLuInfo, TbInfoCircle
RuneKeyIconKeyKey, KeyRoundLuKey, LuKeyRound, TbKey
RuneLayersIconStack, IconStack2LayersLuLayers, TbStack, TbStack2
RuneLayoutGridIconLayoutGridLayoutGridLuLayoutGrid, TbLayoutGrid
RuneLifebuoyIconLifebuoyLifeBuoyLuLifeBuoy, TbLifebuoy
RuneLinkIconLinkLink, Link2LuLink, LuLink2, TbLink
RuneListIconListListLuList, TbList
RuneListCheckIconListCheckListCheck, ListChecksLuListCheck, LuListChecks, TbListCheck
RuneListOrderedIconListNumbersListOrderedLuListOrdered, TbListNumbers
RuneLoaderIconLoader, IconLoader2Loader, Loader2, LoaderCircleLuLoader, LuLoader2, LuLoaderCircle, TbLoader, TbLoader2
RuneLockIconLockLock, LockKeyholeLuLock, LuLockKeyhole, TbLock
RuneLoginIconLogin, IconLogin2LogInLuLogIn, TbLogin, TbLogin2
RuneLogoutIconLogout, IconLogout2LogOutLuLogOut, TbLogout, TbLogout2
RuneLogsIconLogsLogs, ScrollTextLuLogs, LuScrollText, TbLogs
RuneMailIconMailMailLuMail, TbMail
RuneMaximizeIconArrowsMaximize, IconMaximize≈Expand, Fullscreen, Maximize, Maximize2≈LuExpand, LuFullscreen, LuMaximize, LuMaximize2, TbArrowsMaximize, TbMaximize
RuneMenuIconMenu, IconMenu2≈AlignJustify, Menu≈LuAlignJustify, LuMenu, TbMenu, TbMenu2
RuneMessageIconMessage, IconMessageCircleMessageCircle, MessageSquareLuMessageCircle, LuMessageSquare, TbMessage, TbMessageCircle
RuneMinimizeIconArrowsMinimize, IconMinimizeMinimize, Minimize2, ≈ShrinkLuMinimize, LuMinimize2, ≈LuShrink, TbArrowsMinimize, TbMinimize
RuneMinusIconMinusMinusLuMinus, TbMinus
RuneMonitorIconDeviceDesktopMonitorLuMonitor, TbDeviceDesktop
RuneMoonIconMoonMoonLuMoon, TbMoon
RuneMoreIconDotsEllipsis, MoreHorizontalLuEllipsis, LuMoreHorizontal, TbDots
RuneMoreVerticalIconDotsVerticalEllipsisVertical, MoreVerticalLuEllipsisVertical, LuMoreVertical, TbDotsVertical
RuneNetworkIconNetworkNetworkLuNetwork, TbNetwork
RuneNotFoundIconError404NoneTbError404
RuneNumberIconNumber123NoneTbNumber123
RuneNutIconNutBoltLuBolt, TbNut
RunePauseIconPlayerPausePauseLuPause, TbPlayerPause
RunePercentIconPercentagePercentLuPercent, TbPercentage
RunePhoneIconPhonePhoneLuPhone, TbPhone
RunePinIconPinPinLuPin, TbPin
RunePlayIconPlayerPlayPlayLuPlay, TbPlayerPlay
RunePlugIconPlug, IconPlugConnectedPlug, ≈PlugZapLuPlug, ≈LuPlugZap, TbPlug, TbPlugConnected
RunePlusIconPlusPlusLuPlus, TbPlus
RunePowerIconPowerPowerLuPower, TbPower
RunePowerOffNonePowerOffLuPowerOff
RuneProfileIconUserUserLuUser, TbUser
RunePuzzleIconPuzzlePuzzleLuPuzzle, TbPuzzle
RuneReceiptIconReceiptReceiptLuReceipt, TbReceipt
RuneRefreshIconRefreshRefreshCcw, RefreshCw, ≈RotateCwLuRefreshCcw, LuRefreshCw, ≈LuRotateCw, TbRefresh
RuneRepeatIconRepeatRepeatLuRepeat, TbRepeat
RuneReportIconFileAnalyticsFileBarChart, FileChartColumnLuFileBarChart, LuFileChartColumn, TbFileAnalytics
RuneReset≈IconArrowBackUp, IconRotateRotateCcw, ≈Undo2LuRotateCcw, ≈LuUndo2, ≈TbArrowBackUp, TbRotate
RuneRewindIconPlayerTrackPrevRewindLuRewind, TbPlayerTrackPrev
RuneRocketIconRocketRocketLuRocket, TbRocket
RuneRssIconRssRssLuRss, TbRss
RuneSaveIconDeviceFloppySaveLuSave, TbDeviceFloppy
RuneSearchIconSearchSearchLuSearch, TbSearch
RuneSelectorIconSelectorChevronsUpDownLuChevronsUpDown, TbSelector
RuneSendIconSendSend, SendHorizonal, SendHorizontalLuSend, LuSendHorizonal, LuSendHorizontal, TbSend
RuneServerIconServer, ≈IconServer2ServerLuServer, TbServer, ≈TbServer2
RuneServerCogIconServerCogServerCogLuServerCog, TbServerCog
RuneSettingsIconSettingsCog, Settings, ≈Settings2LuCog, LuSettings, ≈LuSettings2, TbSettings
RuneShareIconShare, ≈IconShare2≈Share, Share2≈LuShare, LuShare2, TbShare, ≈TbShare2
RuneShieldIconShieldShieldLuShield, TbShield
RuneShieldAlertIconShieldExclamationShieldAlertLuShieldAlert, TbShieldExclamation
RuneShieldCheckIconShieldCheckShieldCheckLuShieldCheck, TbShieldCheck
RuneShieldLockIconShieldLockNoneTbShieldLock
RuneSidebarIconLayoutSidebarPanelLeft, SidebarLuPanelLeft, LuSidebar, TbLayoutSidebar
RuneSidebarCollapseIconLayoutSidebarLeftCollapsePanelLeftClose, SidebarCloseLuPanelLeftClose, LuSidebarClose, TbLayoutSidebarLeftCollapse
RuneSidebarExpandIconLayoutSidebarLeftExpandPanelLeftOpen, SidebarOpenLuPanelLeftOpen, LuSidebarOpen, TbLayoutSidebarLeftExpand
RuneSortAscendingIconSortAscendingArrowUpNarrowWide, SortAscLuArrowUpNarrowWide, LuSortAsc, TbSortAscending
RuneSortDescendingIconSortDescendingArrowDownWideNarrow, SortDescLuArrowDownWideNarrow, LuSortDesc, TbSortDescending
RuneSparklesIconSparkles, ≈IconWand≈Sparkle, Sparkles, ≈WandSparkles≈LuSparkle, LuSparkles, ≈LuWandSparkles, TbSparkles, ≈TbWand
RuneStarIconStarStarLuStar, TbStar
RuneStopIconPlayerStop≈Square≈LuSquare, TbPlayerStop
RuneStopSolidIconPlayerStopFilledNoneTbPlayerStopFilled
RuneSuccessIconCircleCheckCheckCircle, CheckCircle2, CircleCheck, CircleCheckBigLuCheckCircle, LuCheckCircle2, LuCircleCheck, LuCircleCheckBig, TbCircleCheck
RuneSuccessSolidIconCircleCheckFilledNoneTbCircleCheckFilled
RuneSunIconSunSunLuSun, TbSun
RuneTeamIconUsersUsersLuUsers, TbUsers
RuneTerminalIconTerminal, ≈IconTerminal2≈SquareTerminal, Terminal≈LuSquareTerminal, LuTerminal, TbTerminal, ≈TbTerminal2
RuneTimerIconStopwatchTimerLuTimer, TbStopwatch
RuneTrashIconTrashTrash, Trash2LuTrash, LuTrash2, TbTrash
RuneTrophy≈IconAward, IconTrophy≈Award, Trophy≈LuAward, LuTrophy, ≈TbAward, TbTrophy
RuneUploadIconUploadUploadLuUpload, TbUpload
RuneUserPlusIconUserPlusUserPlusLuUserPlus, TbUserPlus
RuneWarningIconAlertTriangleAlertTriangle, TriangleAlertLuAlertTriangle, LuTriangleAlert, TbAlertTriangle
RuneWavesIconWaveSineWavesLuWaves, TbWaveSine
RuneWebhookIconWebhookWebhookLuWebhook, TbWebhook
RuneWindowIconAppWindow, IconBrowser, ≈IconLayoutNavbarAppWindow, ≈PanelTopLuAppWindow, ≈LuPanelTop, TbAppWindow, TbBrowser, ≈TbLayoutNavbar
RuneWorldIconWorldEarth, Globe, Globe2LuEarth, LuGlobe, LuGlobe2, TbWorld
RuneWrenchIconToolWrenchLuWrench, TbTool

Draw a new icon

Add it to its category file under components/brand/rune-icons/ with a doc comment that names its job and its rune or object. Map its library names in scripts/rune-icons/rune-icon-map.json, then run npm run registry:build to refresh this list.

Do

  • Use straight strokes only. A curve or an arc breaks the carved look, so M L H V Z are the only path commands.
  • Cut a ring as a hexagon, a diamond or a square: six corners at most. An octagon reads as a circle at 14px.
  • Cut square ends and mitred corners. The factory sets both, so a path needs no cap or join.
  • Draw on the 24px grid. Keep every point inside 0 0 24 24 and the shape inside the live area the set shares, about 3 to 21.
  • Leave the stroke at 2. The factory sets it, and the icons share one weight at every size. Keep parallel strokes 4 units apart and the facets that make a rune 3 units long or more.
  • Draw one <path> per icon with rune(). Use runeSolid() only where a filled state is the job, such as a stop button. Draw its outline 1 unit outside the stroked twin; a second subpath cuts a hole.
  • Mirror the shapes that pair: chevrons up and down, sort up and down, play and pause, sun and moon.

Don't

  • Round a corner to make a shape read better. Add a short straight cut instead.
  • Draw the rune alone. A reader who knows Tabler or lucide should see the icon's job at 14px first, and the rune second.
  • Set a colour or a stroke width in the path. Callers colour the icon with text colour.
  • Animate inside the icon. RuneLoader is still; the caller spins it with animate-spin.

Chain icon

One logo in lists, tables and cards. Artwork paints before hydration. Missing or failed logos show an initials disc.

Sizes and frames

Sizes sm, md and lg. The tile adds a frame around the same glyph size.

size="sm" frame="none"
size="md" frame="none"
size="lg" frame="none"
size="sm" frame="tile"
size="md" frame="tile"
size="lg" frame="tile"

Theme sources and fallbacks

Switch the page theme. Acurast has no dark artwork and gets a light neutral backplate on dark ground. Paired artwork keeps its own colours.

Acurast: no dark artwork
Basilisk: readable on dark ground
src + darkSrc
NC
No logo
Failed logo
Ethereum
Decorative beside its name

Pass name for initials, src for light artwork and darkSrc for dark artwork. The logo is decorative by default. Use alt when it stands alone. Theme classes select the source before hydration. Set chain-icon-ground once to adjust the backplate. Measured catalog data keeps bright marks on the dark ground and gives dark ink a backplate.

Network badge

Identity stays neutral. Network types appear in words, so colour can carry state.

Network types

Supply the type from your catalog. Existing type words are kept without a second suffix.

Ethereum mainnetSepolia testnetKusama canaryLocal devnetHolesky testnet

Node types and ecosystems

Use the catalog's display names. Premium describes access, not a service state.

ArchiveFullPremiumDedicated onlyEVMSubstrateCosmosMove

Use NetworkBadge with name and type. Put a decorative ChainIcon in its icon slot when the name identifies a chain.NodeTypeBadge and EcosystemBadge take their labels as children. These parts expose no colour variants. Use the state variants of Badge for incidents and availability.

Start free.1 response = 1 credit.

100,000 responses a day at 20 per second, on 150+ networks. Upgrade when you need more.