Skip to content

Now in the official shadcn registry directory.

Start free
Getting started

The variant system

ai2's core idea: a component's look is a point in a small coordinate system, not an entry in a flat list.

The problem with flat variants

Most libraries model appearance as one variant prop. That works until visual weight and meaning need to move independently - a destructive action that should be quiet, a success state that should shout. Then you end up writing one-off CSS:

TS
// The flat-list approach: every combination is a new name…
<Button variant="destructive">Delete</Button>
// …and the moment you need "subtle destructive", you write CSS:
<Button className="bg-red-50 text-red-600 hover:bg-red-100">
  Delete
</Button>

Three orthogonal axes

  • variant - visual weight: solid, soft, outline, ghost, link.
  • tone - meaning: neutral, brand, success, warning, danger, info. Mapped to theme tokens, never hardcoded.
  • size - scale: xs through xl where it matters.
TS
// The axis approach: combinations are props.
<Button variant="soft" tone="danger">Delete</Button>
<Button variant="outline" tone="danger">Delete</Button>
<Button variant="ghost" tone="danger" size="sm">Delete</Button>

Principles

  • Axes only where meaningful. Separator has no tones; Badge does. No component carries an axis for symmetry's sake.
  • Safe defaults. Every axis is optional - <Button> is neutral, solid, medium.
  • Tokens underneath. Tones resolve to CSS variables, so themes recolor the whole matrix at once.
  • Inspectable. Every element carries data-slot, tone-aware ones also data-tone - for styling, testing and agents reading the DOM.

The Axis Contract

The axes above are not a loose convention - they are a fixed contract that every component obeys. The vocabulary is closed: variant (visual language), tone (semantic color), size (scale), density (list and table spacing), shape (geometry) and orientation (direction). No component invents a new axis name, so once you have learned one matrix you have learned them all.

Three standard tone sets

Tones come in exactly three fixed sets. A component picks the set that matches its job - never a custom subset:

SetTonesComponentsRole
FULLneutral / brand / success / warning / danger / infoButton, Badge, AlertFeedback surfaces
CONTROLneutral / brand / success / dangerCheckbox, Switch, Radio GroupSelection controls
STATUSneutral / success / dangerInput, Textarea, Select, ComboboxValidation states

On form fields the STATUS set is the validation look: the tone colors the border and the focus ring, nothing else.

One control height scale

Every size axis reads from a single height table, aligned to Button. A component may expose a subset (Input stops at sm - lg), but never a different height - so a md Button, Input and Select trigger always sit flush in one row.

SizeHeight
xsh-7 (28px)
smh-8 (32px)
mdh-9 (36px)
lgh-10 (40px)
xlh-12 (48px)

Recent additions under the contract

Extending an axis is always additive - defaults never change, so existing consumers render identically. The latest wave brought form controls up to the full contract:

  • Input - new tone (neutral / success / danger).
  • Textarea - new size (sm / md / lg) and tone (neutral / success / danger).
  • Select - the trigger now carries the full Input contract: variant (outline / soft / ghost), size (sm / md / lg) and tone (neutral / success / danger).
  • Combobox - new variant, size and tone, matching Select.
  • Toggle - new tone (neutral / brand) for the pressed state; Toggle Group passes it down via context.
  • Field - new orientation (vertical / horizontal) for switch-style rows.
  • Label - new size (sm / md).

Matrix highlights

A sample - every component page documents its own axes in full:

ComponentAxesCombinations
Buttonvariant (5) × tone (6) × size (10)300
Badgevariant (3) × tone (6) × size (3)54
Alertvariant (2) × tone (6)12
Cardvariant (4) × inset (3)12
Progresstone (6) × size (3)18
Switchtone (4) × size (3)12
Checkboxtone (4) × size (3)12
Inputvariant (3) × size (3) × tone (3)27
Selectvariant (3) × size (3) × tone (3)27
Tabsvariant (3)3
Sheetside (4)4

Get new components in your inbox

Component drops, new themes and release notes. No spam, and you can unsubscribe from any email.

© 2026 ai2 design system · MIT licensed · Built with its own components