States
Every combination, not every variant
A variant list shows what a component can look like. A matrix shows what it actually reaches. Button, Badge, Input, NativeSelect and the accent helpers are the app's own files; the rows around them are replicas built from the app's class strings. Toggle dark in the header to check both grounds.
Button
ui/button.tsxHover applies each variant's own hover classes; focus is its focus-visible ring (ring-2 ring-ring ring-offset-2).
Sidebar item
Rows built from the app's SIDEBAR_ROW_CLASS and sidebarItemClass on each theme's list colour. Read rows dim to brightness .85, the floor that keeps 4.5:1 against the lightest stop of most rails, and lift to full on hover, where some themes also brighten the text. Unread is white at 900 and stays white on hover; active is the theme's own sidebarItem.active at 700. Channels mark unread by weight alone; DM rows add SIDEBAR_COUNT_PILL_CLASS.
use-sidebar-item-styles.tsReaction
Own reactions fill with withAlpha(accent, 0.12), 12% in both modes, under a 1px inset accent ring, and only the count takes the accent, at 900. They have no hover style. Others are muted with a hairline ring that darkens on hover. 24px pills, 32px under md.
components/reactions.tsxBadges and count pills
Badge is the primitive. Count pills are not: they read --theme-badge, which the shell sets to the theme's pastel accent.dark in both modes, with #16090e text. The rail badge is the same fill at 16px, falling back to #ECB22E outside a workspace; a nav row's count (Drafts & sent) is neutral white/20.
ui/badge.tsxSIDEBAR_COUNT_PILL_CLASS 路 sidebar-button.tsx 路 sidebar-item.tsxSelected
The shared selection style is accentSelected from lib/accent.ts: an accent tint (8% light, 15% dark) that persists under the pointer, plus accent text. Not every consumer takes all of it, and each keeps its own hover: classes, so every row is composed the way its file composes it. Only the status picker keeps tint and accent text on hover; the preferences tabs, the settings sidebar and the mode picker keep the tint but hover their text to foreground. Theme cards and the huddle invite list take the bare tint, so in light they drop to the grey hover; in dark the tint's dark: rule is emitted after hover:bg-accent and holds. Tabs use an accent underline instead of a tint. Shown in Classic.
Page mode
Follows the header toggle.
Forced dark
Wrapped in its own .dark, so it reads dark whatever the header toggle says.
Focus
Buttons, inputs, switches and tabs take the focus ring. Native selects don't: browsers treat a mouse click on a <select> as keyboard focus, so a ring would flash on every click, and a border shift is the cue instead. The composer uses a border shift too (focus-within). Composer toolbar buttons get a 2px inset outline, and huddle invite rows only fill.