npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@trading-game/design-intelligence-layer

v0.17.4

Published

Trading Game Design System — shadcn/ui components with Tailwind CSS v4

Readme

Trading Game Design System

A component library and design token system for the Trading Game product. Built on shadcn/ui with a custom Figma-driven token architecture, Tailwind CSS v4, and React 19.

Published as @trading-game/design-intelligence-layer on npm.


Quick Start

Installation

npm install @trading-game/design-intelligence-layer@latest --safe-chain-skip-minimum-package-age

Note: The --safe-chain-skip-minimum-package-age flag bypasses npm's 72-hour safe-chain hold on newly published packages. Required when the latest version was published less than 72 hours ago (common for AI agents and CI pipelines).

1. Add the CSS imports

In your main CSS file (e.g. globals.css), add these three lines in this order:

@import "@trading-game/design-intelligence-layer/styles";
@import "tailwindcss";
@source "../node_modules/@trading-game/design-intelligence-layer/dist";

Why @source? Tailwind v4 skips node_modules by default. Without this line, Tailwind won't generate CSS for the component classes. Adjust the path if your CSS file is not one level below node_modules (e.g. in a monorepo use ../../node_modules/...).

1b. Vite projects — add the Tailwind plugin

If your project uses Vite (e.g. Vite + React), you must add the Tailwind CSS plugin to your Vite config:

npm install @tailwindcss/vite
// vite.config.js
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default {
  plugins: [react(), tailwindcss()],
}

Next.js projects do not need this — Next.js uses PostCSS for Tailwind automatically.

2. Fonts (handled automatically)

The styles import loads Plus Jakarta Sans from Google Fonts automatically. No additional font setup is needed.

Use font-body, font-display, or font-sans in your Tailwind classes — all resolve to Plus Jakarta Sans.

3. Use components

import { Button, Card, Badge } from "@trading-game/design-intelligence-layer"

export default function App() {
  return (
    <Card>
      <Button variant="primary">Trade Now</Button>
      <Badge>Live</Badge>
    </Card>
  )
}

Peer dependencies

Make sure these are installed in your project:

npm install react react-dom tailwindcss

Requires React 18+ and Tailwind CSS v4+.


What's inside

  • 56 UI components — buttons, forms, dialogs, charts, sidebars, and more
  • Design tokens — CSS custom properties for color, radius, typography, and transitions synced from Figma
  • Transition tokens — semantic duration and easing tokens (duration-base, ease-standard, etc.) — no hardcoded duration-200 anywhere
  • Light-only theme — white backgrounds, black text, primary blue accents — no dark mode
  • Typography classes — heading scale (h1–xs) and body scale (lg–xs) ready to use
  • TypeScript — full type definitions included
  • ESM + CJS — works with any bundler

Available components

| Component | Import | |---|---| | Accordion | Accordion, AccordionItem, AccordionTrigger, AccordionContent | | Alert | Alert — dismissible status notification. Variants: info, warning, danger, success. Layouts: inline, card. Slots: title, description, icon, action, actions, onDismiss. | | Alert Dialog | AlertDialog, AlertDialogTrigger, AlertDialogContent, ... | | Aspect Ratio | AspectRatio | | Avatar | Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup | | Badge | Badge, badgeVariants | | Banner | Banner — dismissible promotional surface on a tinted primary surface. Brand-blue square IconBadge (40 px, rounded-xs) with a swappable glyph (or pass icon to override the badge entirely). Layouts: inline, card. Slots: title, description, icon/glyph, action, actions, onDismiss, onClick (whole-banner clickable). | | Breadcrumb | Breadcrumb, BreadcrumbList, BreadcrumbItem, ... | | Button | Button, buttonVariants | | Calendar | Calendar, CalendarDayButton | | Card | Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter | | Carousel | Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext | | Chart | ChartContainer, ChartTooltip, ChartLegend, ChartStyle | | Checkbox | Checkbox | | Chip | Chip — pill-shaped, toggle-able selection chip (e.g. game filters). Selected state takes bg-primary/[0.08] + text-primary; unselected uses outline + text-on-prominent. | | Collapsible | Collapsible, CollapsibleTrigger, CollapsibleContent | | Combobox | Combobox, ComboboxInput, ComboboxContent, ComboboxItem, ... | | Command | Command, CommandDialog, CommandInput, CommandList, ... | | Context Menu | ContextMenu, ContextMenuTrigger, ContextMenuContent, ... | | Dialog | Dialog, DialogTrigger, DialogContent, DialogHeader, ... | | Drawer | Drawer, DrawerTrigger, DrawerContent, DrawerHeader, ... | | Dropdown Menu | DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, ... | | Empty State | Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent | | Field | Field, FieldLabel, FieldDescription, FieldError, FieldGroup | | Form | Form, FormItem, FormLabel, FormControl, FormField, FormMessage | | Hover Card | HoverCard, HoverCardTrigger, HoverCardContent | | Input | Input — variants: default (light surfaces), inverse (dark sunken fill + white text, for use on bg-primary / brand-coloured surfaces). | | Input Group | InputGroup, InputGroupAddon, InputGroupButton, InputGroupText — variants: default, inverse (propagates to nested InputGroupInput / InputGroupAddon / InputGroupText via context). | | Input OTP | InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator — Variants: OTP 4 digits, OTP 6 digits, OTP 6 split, OTP 8 digits, OTP 6 separated | | Item | Item, ItemMedia, ItemContent, ItemTitle, ItemDescription | | Kbd | Kbd, KbdGroup | | Label | Label | | Link | Link | | Menubar | Menubar, MenubarMenu, MenubarTrigger, MenubarContent, ... | | Native Select | NativeSelect, NativeSelectOptGroup, NativeSelectOption | | Navigation Button | NavigationButton, navigationButtonVariants | | Navigation Menu | NavigationMenu, NavigationMenuList, NavigationMenuTrigger, ... | | Pagination | Pagination, PaginationContent, PaginationLink, ... | | Popover | Popover, PopoverTrigger, PopoverContent, PopoverAnchor | | Progress | Progress | | Radio Group | RadioGroup, RadioGroupItem | | Resizable | ResizableHandle, ResizablePanel, ResizablePanelGroup | | Scroll Area | ScrollArea, ScrollBar | | Select | Select, SelectTrigger, SelectContent, SelectItem, ... | | Separator | Separator | | Sheet | Sheet, SheetTrigger, SheetContent, SheetHeader, ... | | Sidebar | Sidebar, SidebarProvider, SidebarMenu, SidebarMenuItem, ... | | Skeleton | Skeleton | | Slider | Slider | | Spinner | Spinner | | Stepper | Stepper | | Switch | Switch | | Table | Table, TableHeader, TableBody, TableRow, TableHead, TableCell | | Tabs | Tabs, TabsList, TabsTrigger, TabsContent | | Textarea | Textarea | | Ticket Card | TicketCard, CreditTicketCard | | Toast | Toaster | | Toggle | Toggle, toggleVariants | | Toggle Group | ToggleGroup, ToggleGroupItem | | Tooltip | Tooltip, TooltipTrigger, TooltipContent, TooltipProvider |


Blocks

Blocks are opinionated, composed UI patterns built from design system primitives — exported from the package the same as primitives. Import them by name and pass data via props. Variants (layout / mode / status) live on a single block, not separate components.

import { HeroBlock, ResultBlock, OpenPositionsBlock } from "@trading-game/design-intelligence-layer"

| Block | Variants | Description | |---|---|---| | HeroBlock | layout: "centered" \| "split" | Marketing hero section. Centred = single-column, no image; split = two-column with image. Tagline pill, heading, body, primary + optional secondary CTA. | | AuthBlock | mode: "sign-in" \| "sign-up" | Auth form — logo lockup, OAuth provider buttons (Google, Telegram, X), divider, email input, terms, primary CTA, footer cross-link. | | FAQBlock | layout: "desktop" \| "mobile" | Eyebrow + heading + intro + accordion + optional "Need more help" card. Items passed as { value, question, answer }[]. | | NavBarBlock | — (internal mobile-menu state) | Responsive marketing nav — brand logos, link buttons, Sign in / Sign up CTAs, hamburger drawer on mobile. | | HeaderNavigationBlock | — | In-app trading header. Back · badge · balance · optional history button with history.count numeric badge (bg-primary; ≥99 → 99+) · actions slot. | | OpenPositionsBlock | Position discriminated union (rise-fall / swipe / box-o / digits) | Responsive Sheet (desktop) / Drawer (mobile) listing open positions. Consumer-provided trigger; default empty state. | | ResultBlock | status: "win" \| "loss", ctaMode: "next-round" \| "go-again" \| "conversion" | Win/loss result card. Animated halo + thumb illustration (bundled inline) + contract badge + duration badge + CTA. | | ResultDialog | — | Fixed-width system dialog (title + body + two CTAs). For messages like "out of balance" or "on a roll". |

See AGENTS.md for full usage examples per block.


Button variants and sizes

<Button variant="primary" />                  // Blue filled — main CTA (light surfaces)
<Button variant="primary-inverse" />          // White filled + blue text — main CTA on dark/coloured surfaces
<Button variant="primary-shimmer" />          // Same as `primary` + sweeping diagonal shimmer (headline CTAs that need to draw the eye)
<Button variant="primary-inverse-shimmer" />  // Same as `primary-inverse` + brand-blue shimmer over the white surface
<Button variant="secondary" />                // Black outline — secondary actions (light surfaces)
<Button variant="secondary-inverse" />        // White outline + white text — secondary actions on dark/coloured surfaces
<Button variant="tertiary" />                 // Text only — minimal (light surfaces)
<Button variant="tertiary-inverse" />         // Text only + white text — minimal on dark/coloured surfaces

// Sizes
<Button size="lg" />      // 48px height (default)
<Button size="md" />      // 40px height
<Button size="sm" />      // 32px height
<Button size="xs" />      // 24px height
<Button size="icon-lg" /> // 48px square
<Button size="icon-md" /> // 40px square
<Button size="icon-sm" /> // 28px square
<Button size="icon-xs" /> // 24px square

Navigation Button

An icon-only tertiary button for navigation actions. Transparent background, black icon color (text-on-prominent), grey hover (bg-secondary-hover). All sizes follow the 8-point grid.

<NavigationButton size="lg" aria-label="Home"><HomeIcon /></NavigationButton>

| Size | Button | Icon | |------|--------|------| | lg | 48px | 24px | | md | 40px | 20px | | sm | 32px | 20px | | xs | 24px | 16px |

States: hover (bg-secondary-hover), focus (3px ring-ring/50), active (opacity-60), disabled (opacity-24). Supports asChild for custom elements.

Badge variants

// Default (solid)
<Badge variant="default" />         // Blue (#2323FF)
<Badge variant="default-success" /> // Green
<Badge variant="default-fail" />    // Red

// Standard (neutral grey chip) — for non-status meta like contract type, duration
<Badge variant="standard" />

// Fill (tint background)
<Badge variant="fill" />            // Blue tint
<Badge variant="fill-success" />    // Green tint
<Badge variant="fill-fail" />       // Red tint
<Badge variant="fill-warning" />    // Yellow tint — status/warning
<Badge variant="fill-credit" />     // Frosted-glass surface (`--semantic-boost-surface` = `oklch(1 0 0 / 0.22)`) + white text + `backdrop-blur-md` — Welcome credit / bonus chip. Only readable on coloured surfaces (e.g. a brand-blue hero card); blends into white surfaces by design.
<Badge variant="fill-demo" />       // Peach surface + red-orange text — demo/test-mode flags

// Outline (black border, secondary-hover on hover)
<Badge variant="outline" />

// Ghost (transparent)
<Badge variant="ghost" />
<Badge variant="ghost-success" />
<Badge variant="ghost-fail" />

// Sizes
<Badge size="sm" />   // 24px height
<Badge size="md" />   // 32px height (default)
<Badge size="lg" />   // 40px height

Alert variants

Dismissible status notification (info / warning / danger / success). For promotional / marketing surfaces, use Banner instead.

// Variant — controls the surface tint and icon accent
<Alert variant="info" />      // sky-blue tint
<Alert variant="warning" />   // yellow tint
<Alert variant="danger" />    // red tint
<Alert variant="success" />   // green tint

// Layout — controls the shape
<Alert layout="inline" />     // single row: icon · body · optional action · close X (default)
<Alert layout="card" />       // taller: icon · body · close X top-right, up to 2 buttons bottom-left

// Slots — all optional (at least one of title or description is required)
<Alert
  variant="info"
  layout="inline"
  title="Verify your email"
  description="We sent a confirmation link to [email protected]."
  icon={<MyIcon />}                 // pass null to omit; omit to use the variant default
  action="Resend"                   // inline only — string is wrapped in Button automatically
  onDismiss={() => setOpen(false)}  // when provided, close X is rendered
  dismissLabel="Dismiss"            // aria-label for the close X
/>

<Alert
  variant="info"
  layout="card"
  title="..."
  description="..."
  actions={["Learn more", "Open trading"]}  // card only — array of up to 2 buttons
  onDismiss={() => setOpen(false)}
/>

Behaviour:

  • Dismissal is controlled — clicking the close X calls onDismiss; the parent owns visibility.
  • All variants' buttons default to Button variant="secondary". Pass a fully-styled <Button> to action / actions to override.
  • Inline + single-line content (only title OR only description) renders at a fixed 48px height with everything vertically centered. Inline + multi-line and card layouts grow to fit.
  • role="alert" + aria-live="assertive" for danger / warning; role="status" + aria-live="polite" for info / success.

Banner

Dismissible promotional / marketing surface — for feature launches, offers, onboarding nudges, cross-promo. Single visual treatment (no severity variants); for status messages use Alert instead.

// Layout — controls the shape
<Banner layout="inline" />    // single row: icon · body · optional action · close X (default)
<Banner layout="card" />      // body on row 1, up to 2 action buttons on row 2 bottom-left

// Slots — all optional (at least one of title or description is required)
<Banner
  layout="inline"
  title={<>Spring release <b>is here.</b></>}        // wrap emphasis in <b> for brand-blue accent
  description="New features just landed — take a look at what's changed."
  glyph={<Sparkles className="size-5" />}             // swaps the icon *inside* the default brand badge (default <Gift />)
  icon={<MyIcon />}                                   // full override — replaces the brand badge entirely; null to omit
  action="Get started"                                // string wrapped in Button variant="primary"
  onDismiss={() => setOpen(false)}                    // when provided, close X is rendered
  dismissLabel="Dismiss"                              // aria-label for the close X
/>

<Banner
  layout="card"
  title="..."
  description="..."
  actions={["Learn more", "Get started"]}             // first = Secondary, second = Primary
  onDismiss={() => setOpen(false)}
/>

// Whole-banner clickable — the surface itself is the CTA.
// Mutually exclusive with action / actions (buttons dropped, dev warning).
<Banner
  layout="inline"
  title="Tap anywhere on this banner"
  description="Whole surface is the click target."
  onClick={() => navigate("/promo")}
  onDismiss={() => setOpen(false)}                    // close X still works independently
/>

Visual:

  • Surface: tinted primary surface — bg-primary/[0.08] with border-primary/[0.16] edge, rounded-sm (6 px — matches Card and Alert).
  • Default icon: 40 px brand-blue square (rounded-xs bg-primary, 4 px corners) with a white Gift glyph inside. Swap just the glyph via the glyph prop (e.g. <Sparkles className="size-5" />); replace the entire badge via the icon prop; or icon={null} to omit the column entirely.
  • Multi-line layout (description set, or layout="card"): the leading icon top-aligns with the title (grid uses items-start) so it sits beside the headline rather than floating mid-body. Single-line banners stay vertically centred (items-center).
  • Default action button: Button variant="secondary" size sm for every slot (inline action and both actions[] in card). Pass <Button> elements explicitly to override variant/size.
  • Title supports <b> children styled with text-primary for in-line brand-blue accent.
  • Close X (32 px): vertically-centred in the grid for single-line banners; pinned to the top-right corner (absolute top-3 right-3) when the banner is multi-line (description set) or layout="card" — so it reads as a true corner dismiss instead of floating mid-body. Hovers to bg-secondary-hover.

Component behaviour notes

AlertDialog — button fill

AlertDialogAction and AlertDialogCancel fill the full container width only when size="sm". On size="default" (the default), buttons are natural width and right-aligned.

<AlertDialogContent size="sm">   // buttons fill width (50/50 grid)
<AlertDialogContent size="default"> // buttons natural width, right-aligned

Drawer — footer buttons

DrawerFooter makes all direct child buttons fill the full width automatically via [&>*]:w-full. Stack your buttons inside DrawerFooter and they will always be full-width.

Link — size lg

The lg variant of Link uses text-base (16px). The md and sm variants use text-sm (14px) and text-xs (12px) respectively.

<Link size="lg" />  // 16px — font-base
<Link size="md" />  // 14px — font-sm
<Link size="sm" />  // 12px — font-xs

Select — sizes

SelectTrigger accepts a size prop with three values matching the Input height scale:

<SelectTrigger size="sm" />   // h-8  (32px) — text-xs
<SelectTrigger size="md" />   // h-10 (40px) — text-sm  (default)
<SelectTrigger size="lg" />   // h-12 (48px) — text-base

Tabs — line variant hover

On the line variant, hover state shows text-primary (blue) with no background fill. On the default (pill) variant, hover shows a grey background with text-on-prominent.

Link — underline

Underline is always visible (not hover-only). Class chain: underline decoration-2 underline-offset-4 text-primary.

Switch — pill + circle

Track is a pill (rounded-full); thumb is always a white circle (rounded-full bg-prominent). Checked track: bg-primary. Unchecked track: bg-input.

Tooltip — default surface

Default variant uses a black surface (bg-prominent-inverse text-on-prominent-inverse) with a matching arrow. Use variant="inverse" for a white tooltip when the trigger sits on a saturated coloured surface. The primitive bakes in max-w-xs + whitespace-normal, so consumers no longer need to pass them. When a Kbd is detected inside via :has([data-slot=kbd]), padding bumps to px-3.5 py-2 and siblings receive space-y-1.5 for breathing room.

Sidebar — selection + padding

SidebarHeader / SidebarFooter / SidebarGroup use px-layout-margin-inline py-2 (16 px horizontal via --semantic-layout-margin-inline), with group-data-[collapsible=icon]:px-2 so the collapsed icon rail tightens to 8 px. Active selection: bg-primary/10 + text-primary + font-semibold. For size="lg" menu buttons (typical brand-block + label or profile pattern), the collapsed state uses p-0! justify-center so a 32 px brand block fills the rail while a 16 px lucide icon still centres on the same vertical line.

Button — bg transition

The button base sets transition-[color,border-color,opacity,box-shadow,transform] (not transition-all), so background-color flips instantly on hover instead of fading in over 150 ms. This prevents the brief partially-transparent phase where surrounding surface tints (e.g. an Alert) could bleed through a hovered secondary button.


Design tokens

All tokens are CSS custom properties, loaded automatically via @trading-game/design-intelligence-layer/styles.

Background tokens

| Tailwind class | CSS variable | Value | Usage | |---|---|---|---| | bg-prominent | --prominent | #FFFFFF | Page background | | bg-prominent-inverse | --prominent-inverse | #000000 | Dark surface for use on light pages (e.g. toasts) | | bg-card | --card | #FFFFFF | Card/panel surface | | bg-popover | --popover | #FFFFFF | Popover/dropdown surface | | bg-subtle | --subtle | #F5F5F5 | Subtle tinted surface | | bg-overlay | --overlay | black 50% | Modal/dialog backdrop only | | bg-tabs | --tabs | black 4% alpha | Tab container background (variant="default") | | bg-tabs-active | --tabs-active | #FFFFFF | Active / selected tab pill | | bg-primary | --primary | #2323FF | Brand blue — CTAs | | bg-primary-hover | --primary-hover | #0B0BD2 | Primary button hover (darker blue) | | bg-primary-inverse | --primary-inverse | #FFFFFF | Inverted primary surface — white bg for use on dark/coloured areas | | bg-secondary-hover | --secondary-hover | #EEEEEE | Outline/secondary hover (light grey) | | bg-semantic-win | --semantic-win | green | Profit / positive state | | bg-semantic-loss | --semantic-loss | red | Loss / negative state | | bg-semantic-boost | --semantic-boost | #F9840F amber-orange | Boost / bonus badge background |

Text tokens

Naming convention: every surface (bg) token has a paired text-on-<surface> foreground (text/icon) token. Always use the pair together — bg-primary + text-on-primary, bg-semantic-win + text-on-semantic-win, etc. — for guaranteed legibility regardless of theme.

| Tailwind class | Value | Usage | |---|---|---| | text-on-prominent | #000000 | Text on bg-prominent (default body text on light surfaces) | | text-on-prominent-inverse | #FFFFFF | Text on bg-prominent-inverse (e.g. toast title/description) | | text-on-subtle | mid grey | Text on bg-subtle (secondary / supporting text) | | text-on-disabled | #BBBBBB | Disabled / inactive text | | text-on-primary | #FFFFFF | Text on bg-primary (e.g. primary button label, tooltip text) | | text-on-primary-inverse | #2323FF | Text on bg-primary-inverse (e.g. primary-inverse button label) | | text-on-semantic-win | #FFFFFF | Text on bg-semantic-win (e.g. success badge label) | | text-on-semantic-loss | #FFFFFF | Text on bg-semantic-loss (e.g. fail badge label) | | text-on-semantic-warning | #FFFFFF | Text on bg-semantic-warning (e.g. warning badge label) | | text-on-semantic-boost | #713813 dark amber | Text on bg-semantic-boost (e.g. boost badge label) | | text-primary | #2323FF | Brand blue inline text (use as a foreground over neutral surfaces) | | text-semantic-win | green | Profit / positive inline text | | text-semantic-loss | red | Loss / negative inline text |

Deprecated: text-on-prominent-static-inverse and the matching --on-prominent-static-inverse CSS variable are still defined for backwards-compat with external code that references them via raw var(). Internal components no longer use them. For new code, pick the explicit text-on-<surface> paired with the actual background.

Border & focus tokens

| Tailwind class | CSS variable | Value | Usage | |---|---|---|---| | border-border-subtle | --border-subtle | #EEEEEE | Default — cards, dividers, form borders | | border-border-prominent | --border-prominent | #000000 | Outline variant components (button, badge, toggle) | | border-border | --border | #EEEEEE | @deprecated alias — prefer border-border-subtle | | border-input | --input | #EEEEEE | Input field borders | | ring-ring | --ring | #2323FF | Focus rings |

⚠️ border-border vs border-border-subtle: Both resolve to the same light grey #EEEEEE. border-border is kept for backward compatibility. Prefer border-border-subtle in all new code.

Transition tokens

Duration — primitive scale:

| Tailwind class | CSS variable | Value | Used for | |---|---|---|---| | duration-instant | --primitive-duration-instant | 50ms | Focus rings, hover tints | | duration-fast | --primitive-duration-fast | 100ms | Buttons, inputs, badges | | duration-base | --primitive-duration-base | 200ms | Dropdowns, popovers, accordions | | duration-slow | --primitive-duration-slow | 300ms | Dialogs, sheets, drawers closing | | duration-open | --primitive-duration-open | 500ms | Sheets, drawers entering |

Easing — primitive scale:

| Tailwind class | CSS variable | Value | Used for | |---|---|---|---| | ease-standard | --primitive-ease-standard | cubic-bezier(0.2, 0, 0, 1) | General UI — bidirectional state changes | | ease-enter | --primitive-ease-enter | cubic-bezier(0, 0, 0.2, 1) | Overlays / surfaces entering | | ease-exit | --primitive-ease-exit | cubic-bezier(0.4, 0, 1, 1) | Overlays / surfaces leaving | | ease-linear | --primitive-ease-linear | linear | Sidebar width, progress bar |

Always use token utilities — never raw duration-200 or ease-in-out directly.

Other tokens

| Tailwind class | Value | Usage | |---|---|---| | bg-slider-range | blue 40% | Slider filled range | | rounded-lg | 0.625rem | Base border radius |

Using opacity with tokens

Opacity on tokens is allowed and encouraged:

✅ bg-primary/20           → blue at 20% opacity
✅ border-border-subtle/50 → light grey border at 50% opacity
✅ ring-ring/10            → ring at 10% opacity

❌ bg-black/50             → NOT a token, use bg-overlay instead
❌ bg-white                → NOT a token, use bg-prominent or bg-card

Primitive alpha scales

For structured alpha surfaces, the design system ships two internal alpha primitive scales. These are CSS variables only — not exposed as Tailwind utility classes. Always reference them via a semantic token, never directly in components.

--primitive-mono-alpha-4  through --primitive-mono-alpha-80   (black at N%)
--primitive-blue-alpha-4  through --primitive-blue-alpha-80   (brand blue at N%)
Stops: 4 · 8 · 16 · 24 · 32 · 40 · 50 · 64 · 80

Example — how --tabs uses the alpha scale:

--tabs: var(--primitive-mono-alpha-4); /* black at 4% — tab container background */

--primitive-black-50 is deprecated. It now aliases --primitive-mono-alpha-50. The --overlay semantic token is unaffected.


Typography

The styles export includes pre-built typography classes using Plus Jakarta Sans.

Heading scale (Semibold 600 · tracking 1.5px · uppercase)

| Class | Size / Line height | |---|---| | heading-h1 | 72px / 72px | | heading-h2 | 64px / 64px | | heading-h3 | 48px / 48px | | heading-h4 | 40px / 40px | | heading-xs | 24px / 24px |

Body scale (Semibold 600)

| Class | Size / Line height | |---|---| | body-lg | 18px / 28px | | body-md | 16px / 24px | | body-sm | 12px / 16px | | body-xs | 8px / 12px |

Font utilities

| Tailwind class | Font | |---|---| | font-display | Plus Jakarta Sans — headings, display text | | font-body or font-sans | Plus Jakarta Sans — body text |


Upgrading the design system

When you bump @trading-game/design-intelligence-layer and run npm install (or npm ci):

| How you use the package | What happens | |-------------------------|----------------| | You import components only from @trading-game/design-intelligence-layer | After install and a rebuild, your app uses the new implementations in node_modules/.../dist — buttons, cards, etc. reflect the version you installed. | | You copied components/ui/* (or similar) into your repo | Those files do not auto-update. You must delete them and switch to package imports, or manually merge changes from the new package. |

Overrides: Passing large className strings onto DS components can mask new defaults (e.g. old radius after a “pill button” update). After upgrading, review those callsites.

Cursor / AI agents: The package ships agent rules at node_modules/@trading-game/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc. Cursor does not load rules from node_modules by default — re-copy that file into .cursor/rules/ after each upgrade so instructions match the release (same command as in AI Agent Setup below). The rules include Rule 7 — Package version upgrades: agents should search for duplicated components, align with the package, and tell you explicitly if local component code was replaced.


AI Agent Setup

All AI tools (Cursor, Windsurf, Claude Code, Copilot, and others)

The package ships an AGENTS.md file that most AI tools read automatically. After installing the package, copy it to your project root:

cp node_modules/@trading-game/design-intelligence-layer/AGENTS.md ./AGENTS.md

This tells any AI agent to:

  1. Read the design principles before building any screen
  2. Follow the component and token rules
  3. Run the 7-point checklist before completing any view

Design principles file (bundled in the package):

node_modules/@trading-game/design-intelligence-layer/guides/design-principles/trading-game-design-principles.md

Accessibility standards file (bundled in the package):

node_modules/@trading-game/design-intelligence-layer/guides/accessibility-standards/trading-game-accessibility-standards.md

Personas file (bundled in the package):

node_modules/@trading-game/design-intelligence-layer/guides/personas/trading-game-player-field-guide.md

Brand voice (bundled in the package):

node_modules/@trading-game/design-intelligence-layer/guides/brand-voice/trading-game-brand-voice.md

All four files apply to all projects built with this package — landing pages, product screens, and games. Every AI agent must read all of them before starting any build. Run the design principles and accessibility checklists before completing any screen. Use the personas and brand voice to guide all player-facing copy.

Cursor

Copy the included rule file into your project:

mkdir -p .cursor/rules
cp node_modules/@trading-game/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc .cursor/rules/

Re-run this cp after every design-system version bump so your workspace rules stay in sync with the installed package.

Claude Code

Add the following to your project's CLAUDE.md:

## Design System

This project uses @trading-game/design-intelligence-layer. Before writing any UI:
1. Read node_modules/@trading-game/design-intelligence-layer/guides/design-principles/trading-game-design-principles.md — apply the 8 principles and run the 7-point checklist on every screen
2. Read node_modules/@trading-game/design-intelligence-layer/guides/accessibility-standards/trading-game-accessibility-standards.md — apply WCAG 2.1 AA standards and run the 9-point accessibility checklist on every screen
3. Read node_modules/@trading-game/design-intelligence-layer/guides/personas/trading-game-player-field-guide.md — understand the 3 player modes (Edge Seeker, System Runner, Public Predictor) that shape all copy and UX
4. Read node_modules/@trading-game/design-intelligence-layer/guides/brand-voice/trading-game-brand-voice.md — apply the brand voice: channel-specific voice, banned phrases, vocabulary, and formatting rules for all player-facing copy
5. Check if the component exists in the package — import it, don't re-implement
6. Use only design token classes (bg-prominent, text-on-prominent, border-border-subtle, etc.) — no hardcoded hex or raw Tailwind palette colors
7. Do not install lucide-react, tailwindcss, or other bundled dependencies separately
8. If no token exists for a value, ask before using a hardcoded value
9. After upgrading the package: prefer package imports over local copies of components; if replacing local UI code with the package version, tell the user what was overwritten; re-copy guides/rules/design-system-consuming-project.mdc into .cursor/rules if using Cursor

See node_modules/@trading-game/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc for full rules.
See node_modules/@trading-game/design-intelligence-layer/README.md for complete token and component reference.

Common mistakes

| Wrong | Right | Why | |---|---|---| | bg-gray-100 | bg-subtle | Raw Tailwind palette — use tokens | | bg-white | bg-prominent or bg-card | Not a semantic token | | text-white | text-on-<surface> (e.g. text-on-primary, text-on-prominent-inverse, text-on-semantic-win) | Pick the paired foreground for the actual background | | text-black | text-on-prominent | Not a semantic token | | text-gray-400 | text-on-disabled | Use the disabled text token | | bg-black/50 | bg-overlay | Overlay has its own token | | bg-[#2323FF] | bg-primary | Hardcoded hex — use token | | border-border | border-border-subtle | Old name — prefer the new explicit name | | bg-[var(--primary)] | bg-primary | Raw CSS var — Tailwind v4 maps tokens directly | | hsl(var(--primary)) | bg-primary | Tailwind v3 syntax — not needed in v4 | | Installing lucide-react | Already bundled | Icons are included in the package |


Do NOT install separately

These are bundled with the package. Installing them separately can cause version conflicts:

  • lucide-react — icon library
  • radix-ui — headless primitives
  • class-variance-authority — variant API
  • cmdk — command palette
  • vaul — drawer
  • sonner — toast
  • recharts — charts
  • react-day-picker — calendar
  • embla-carousel-react — carousel
  • react-resizable-panels — resizable panels

Development (contributors only)

# Clone
git clone https://github.com/trading-game/TG-design-system.git
cd TG-design-system

# Install dependencies
npm install

# Start dev server (Next.js component playground)
npm run dev

# Build the library (ESM + CJS + types)
npm run build

# Build the Next.js showcase app
npm run build:next

Updating design tokens

Design tokens are managed in Figma and exported as CSS variables. To update:

  1. Update the CSS custom properties in app/globals.css (playground) and src/styles.css (published package)
  2. Update guides/rules/design-system-consuming-project.mdc to reflect any token renames
  3. Update this README's token tables

Tech stack

  • React 19 + TypeScript
  • Tailwind CSS v4 — CSS-first configuration
  • shadcn/ui (New York style) — base component primitives
  • Radix UI — accessible headless primitives
  • Figma — source of truth for design tokens

Changelog

Unreleased

  • Button — new tertiary-inverse variant: mirror of tertiary (transparent + text-only) but recoloured for use on dark / brand-coloured surfaces — bg-transparent text-on-primary hover:bg-on-primary/[0.10]. Pair with any size including icon-* (icon-only square). Same variant × size orthogonality as every other Button variant.
  • Input — new variant="inverse": sunken dark fill + white text recipe for use on bg-primary (and other coloured surfaces) where the default frosted-white fill blends into the chrome. 25 %-white border, bg-input-inverse-surface fill (35 % black), white text, 45 %-white placeholder, white selection. On focus the border solidifies to 100 % white with a 40 %-white ring. variant="default" (the existing look) is unchanged — no existing call site moves visually.
  • InputGroup — new variant="inverse": mirrors the Input variant. A new InputGroupVariantContext propagates the variant down so InputGroupInput, InputGroupAddon, and InputGroupText all pick up the inverse styling automatically (addon icons / suffix text swap to text-on-primary/70). Override per-instance via className (e.g. border-transparent for the borderless wallet-address treatment).
  • New token — --input-inverse-surface: added in both src/styles.css and app/globals.css (component-token layer, references the existing primitive palette). Mapped via @theme inline to --color-input-inverse-surface so it's exposed as bg-input-inverse-surface to consumers. Tune the dark-fill shade once, propagates everywhere.
  • Form-control consistency pass — radius unified to rounded-xs (4 px): Input, InputGroup, and Select (trigger) moved from rounded-sm (6 px) → rounded-xs (4 px), matching Button across every size. Form controls placed in a row (<Input> + <Select> + <Button>) now share identical heights and identical corner radii.
  • Form-control consistency pass — resting shadows removed: Input and InputGroup dropped their shadow-xs resting drop-shadow so they sit flat alongside the shadowless Select and Button. Overlay shadows (Dialog, Popover, Tooltip, etc.) are untouched — only the skeuomorphic raised-surface shadow on form inputs is gone.
  • InputGroup — addon-side padding no longer mirrors: previously, an inline-end addon would also reduce the input's left padding to pl-2 "for visual symmetry," squeezing text to the field's left edge. Now only the side an addon is attached to reduces its inset; the opposite side keeps the natural Input pl-3 / pr-3 (12 px). Fixes the suffix-text input ("0.00 ... USD") looking flush-left compared to the prefix-icon row.
  • Component playground — Button section split into two columns: the showcase Button page now renders default-surface variants on the left and bg-primary-surface inverse variants on the right, side-by-side, with row-by-row alignment so each (regular ↔ inverse) pair sits on the same y-line. Stacks vertically below lg: (1024 px) with the inverse half dropping below the default half. Inverse rows previously sat in individual bg-primary wrappers — now a single shared blue panel.
  • Component playground — Input / InputGroup inverse showcases: both sections gain a variant="inverse" row demonstrating the new variant on bg-primary. Plus a standalone /input-on-blue route with longer-form exploration (default vs inverse; borderless wallet-address mock; real-world sign-in row).
  • Component playground — /tertiary-inverse route: standalone page demonstrating the new Button tertiary-inverse variant at every icon-* size and labeled size on a brand-blue surface.
  • OpenPositionsBlock — double-divider fix: the block was rendering both its own manual <div className="h-px ..."> between rows AND the new ItemGroup ::after auto-divider, producing two hairlines per gap. The manual divs are removed; the ItemGroup auto-divider now handles spacing. Migration note for consumers: if you wrap your own <Item> rows in an <ItemGroup> and were manually injecting dividers between them, drop the manual dividers — or pass divider={false} on the <ItemGroup> to opt out and keep your own. The same cleanup landed in the demo app's Sheet "List" showcase.
  • Button — two new shimmer variants for headline CTAs: primary-shimmer and primary-inverse-shimmer. Same surface tokens as their non-shimmer cousins, plus relative overflow-hidden to anchor an absolutely-positioned shimmer overlay. The overlay sweeps a diagonal (110deg) gradient driven by a new button-shimmer keyframe — two quick back-to-back sweeps every cycle, then a ~3 s pause off-screen, so it reads as an occasional glint rather than a continuous loader. Peak opacity is tuned per-surface (white at 32 % over brand blue; brand-blue at 22 % over white) and falls off into transparent edges. Suppressed automatically when the button is loading or disabled.
  • Button — button-shimmer keyframe: new @keyframes button-shimmer in both src/styles.css and app/globals.css. Pattern: 0%, 10% → 30%, 100% translateX, with an opacity fade at the seam between the two sweeps so the reset between flashes is invisible.
  • Badge — fill-credit is now a frosted glass chip: bg-semantic-boost-surface was repurposed from the old peach (#FDCA8A) to oklch(1 0 0 / 0.22) — a translucent white surface that composes over whatever sits behind it. Paired with backdrop-blur-md and text-on-primary (white). Custom !font-bold / !text-xs overrides removed — the variant now only sets surface tokens, so font weight / size match every other badge (font-semibold, text-xs from the base). Border override removed too. The variant is intentionally invisible on white surfaces — designed to live on coloured surfaces (e.g. a brand-blue hero card / wallet card), matching the wallet mock's .c-hero-wc.
  • Item — auto-divider in ItemGroup: new divider prop (default true) adds a 1 px bg-border-subtle divider between consecutive default-variant Items. The line is drawn via ::after (not a border on the item itself), sitting in a 4 px gap-1 between items with inset-x-4 so it aligns with the item content padding. The last item's ::after is suppressed; non-default variants (outline, muted) are untouched. Pass divider={false} to opt out for flush items or manual <ItemSeparator /> usage.
  • Tooltip — black surface by default: variant="default" switched from brand-blue to black (bg-prominent-inverse + text-on-prominent-inverse). The arrow follows. variant="inverse" (white) is unchanged. Default primitive now bakes in max-w-xs whitespace-normal, so consumers no longer pass these classes.
  • Tooltip — keyboard-shortcut breathing room: when the tooltip contains a Kbd (detected via :has([data-slot=kbd])), padding bumps to px-3.5 py-2 and child elements get space-y-1.5 so the title / kbd-row pair doesn't crowd.
  • Switch — pill + white circle knob: track switched from rounded-[6px] to rounded-full, thumb from rounded-[3px] to rounded-full. Thumb is always bg-prominent (white) in both states. Checked track: bg-primary. Unchecked track: bg-input. No API change.
  • Link — underline always visible: swapped hover:underlineunderline so the underline reads at rest, not only on hover.
  • Sidebar — primary-tinted selection + 16 px padding rails: active menu items now use bg-primary/10 (brand-blue tint) instead of bg-secondary-hover (grey). SidebarHeader / SidebarFooter / SidebarGroup use px-layout-margin-inline py-2 (16 px horizontal) when expanded; the icon-collapsed rail falls back to px-2 so the icons still centre. The lg-size menu button gets p-0! justify-center when collapsed so a 32 px brand block fills the rail while a 16 px lucide icon still centres in the same column.
  • Banner — close X corner anchor: the close X stays grid-centred on single-line banners but pins to absolute top-3 right-3 whenever description is set or layout="card" — so it reads as a true corner dismiss instead of floating mid-body.
  • Button — instant bg-color hover: the button base now uses transition-[color,border-color,opacity,box-shadow,transform] instead of transition-all. background-color flips instantly on hover so the brief partially-transparent fade phase (which let surrounding surface tints bleed through a hovered secondary button) is gone.

v0.17.4

  • Button — tertiary variant text color changed to black: text-primary (brand blue) → text-on-prominent (black). Hover changed from bg-primary/[0.08] to bg-secondary-hover. Use tertiary-inverse for white text on dark/coloured surfaces (unchanged). Migration: if you were relying on the blue text of tertiary for visual hierarchy, switch to variant="tertiary-inverse" on coloured surfaces, or keep a local className override.
  • Input — readOnly state: passing the native HTML readOnly prop now renders the input with border-transparent, bg-subtle/60, and pointer-events-none — a distinct non-interactive display state. Clearly distinguishable from disabled (which uses opacity-50 + greyed text) and from the default editable state (solid border). No API change — use the standard readOnly HTML attribute.

v0.16.7

  • Credit badge readability pass: <Badge variant="fill-credit"> and the credit pill inside CreditTicketCard now sit on a deeper peach surface and use a bold deep-amber text for crisper read on the grey bg-subtle ticket card and on white / brand-blue surfaces alike. Token chain: --primitive-amber-100 (#FEEBD9) → --primitive-amber-300 (#FDCA8A) and --primitive-amber-800 (#713813) → --primitive-amber-900 (#521900). --semantic-boost-surface and --on-semantic-boost are repointed automatically. Variant class also bumps to !font-bold.
  • CreditTicketCard — responsive compact layout: Compact variant now keeps the credit badge inline next to the value when there's room and only wraps to a second line when balance + badge would overflow into the deposit stub (flex-wrap on the value/badge row, with whitespace-nowrap on the value so the number itself never splits). Non-compact keeps the existing inline behavior.
  • Banner — outer corner radius unified with Card / Alert: outer surface rounded-md (8 px) → rounded-sm (6 px). Brand IconBadge reverted to rounded-xs (4 px square) after a brief detour through rounded-full — the square reads better against the surface.
  • Banner — multi-line icon top-alignment: when a description is present (or layout="card"), the grid switches from items-center to items-start so the leading icon aligns with the title's top instead of sliding to the middle of the body block as it wraps.
  • Banner — class-order fix for pr-12: in the card branch, pr-12 was being merged before SURFACE_CLASSES's px-4, so tailwind-merge kept the smaller right padding and long descriptions wrapped under the absolute-positioned close X. Re-ordering the cn() arguments puts pr-12 after SURFACE_CLASSES so it wins (48 px right padding).
  • Alert — card layout body/close cell overlap fixed: card body changed from col-span-2 col-start-2 to col-start-2 min-w-0. Previously the body spanned cols 2-3 (same cell as the close X), so long descriptions slid under the close. Now the body lives in col 2 only and the close owns col 3.
  • Tooltip — max-w-xs + whitespace-normal built into the primitive: Desktop tooltips now cap at 320 px by default with natural-word wrapping; consumers no longer need to pass these classes. Mobile drawers (the responsive Tooltip mode) keep the full sheet width — the consumer className is no longer applied to the drawer's inner content wrapper, so passing max-w-xs on TooltipContent for desktop tuning no longer leaks into and shrinks the mobile drawer text.
  • Naming note: the v0.14.0 entry below claimed a fill-creditfill-boost and CreditTicketCardBoostTicketCard rename. Those renames were not landed — the current shipped names are still fill-credit, CreditTicketCard, and --semantic-boost-surface / --on-semantic-boost (the boost tokens stayed, the variant/component names didn't get renamed alongside them).

v0.16.5 — v0.16.6

  • New Chip component: Pill-shaped toggle-able selection chip — bg-primary/[0.08] + text-primary when selected, outline + text-on-prominent when unselected. Used for game filters (All games, Rise & Fall, Swipe, Box-O, Digits) and similar mutually-exclusive selectors.
  • FAQ block — two-column layout: FAQBlock gained a two-column variant on desktop.
  • Demo + block logos repointed at the new .webp brand assets (/assets/icon.webp, /assets/tg-logo-full.webp).
  • Open Positions block — "View transaction history" footer link renamed to "View trade history".

v0.16.1 — v0.16.4

  • New Banner component: Promotional surface (separate from Alert, which is now status-only). Default variant places a brand-blue 40 px circular IconBadge (consumer-swappable via glyph) on a tinted primary surface. Layouts: inline, card. Slots: title, description, icon/glyph, action, actions, onDismiss, onClick (whole-banner clickable).
  • fill-demo Badge variant: Opaque peach surface (color-mix of demo @ 10% with prominent) + red-orange --semantic-demo text — for demo / test-mode account flags. Works identically on any background.
  • Alert slimmed to status-only: default and brand variants removed — use Banner for promotional / marketing surfaces.

v0.14.0

  • New Result block: Fixed-width (320 px) end-of-round result card with 5 variants — Win/continuing, Loss/continuing, End of demo, Out of balance, On a roll. Thumb-up/down SVG with subtle radial halo + idle motion (float for win, head-shake for loss). Title (You won! / You lost!), amount headline in semantic colour, contract + duration standard Badge pills, and CTA(s). Digit contracts render the picked digit inside the contract pill with a thin vertical divider. Out-of-balance and on-a-roll variants are dialog-only (no thumb / no amount) — title + body + 2 CTAs. Lives under Blocks → Result in the playground.
  • Sheet Open Positions redesign: Each row now uses a 2-col layout — bet name + stake (left) / PnL + market·duration (right). Dropped per-row asset (V100), round number, and digit/multiplier suffix labels (the game is already implied by the sheet/tab title). Abbreviated durations (sec / min / hr / day) and dropped the Stake prefix on amounts. Added an "Edge cases" row demonstrating drawer behavior with 1 item and 30 items.
  • Section nav sheet — desktop-safe: The mobile section picker Sheet is now conditionally mounted ({isMobile && …}) so it can't be triggered at desktop widths where the permanent sidebar is already visible, avoiding redundant overlay layers.
  • Badge — new standard variant: Neutral grey chip (bg-subtle / text-on-prominent) for non-status meta like contract type and duration. Existing default (solid blue) and all other variants are unchanged.
  • TicketCard / BoostTicketCard:
    • Left content restructured as a 2-column grid so the icon circle scales to match the label + value height (was a fixed size-10 centered against the entire column).
    • Boost badge now sits in row 2 of column 2 — aligned under the value, not pinned to the card's left edge.
    • 2-decimal balance formatting: the value prop is now normalised by formatBalanceValue before render, so values always display with exactly two decimal places regardless of input (e.g. $12,450$12,450.00, $12,450.5$12,450.50, 12341,234.00). Currency prefix/suffix is preserved.
  • Token convention — on-<surface> foreground pairs: Added a Material-style paired foreground for every semantic surface so colour roles are self-documenting and resolve robustly across consumer setups (incl. Module Federation, where raw var() fallbacks were previously needed). New CSS variables and Tailwind utilities:
    • --on-primary (white) / text-on-primary — paired with bg-primary
    • --primary-inverse (white) / bg-primary-inverse — inverted primary surface for use on dark/coloured areas
    • --on-primary-inverse (blue) / text-on-primary-inverse — paired with bg-primary-inverse
    • --prominent-inverse (black) / bg-prominent-inverse — dark surface for use on light pages (e.g. toasts)
    • --on-prominent-inverse (white) / text-on-prominent-inverse — paired with bg-prominent-inverse
    • --on-semantic-win / --on-semantic-loss / --on-semantic-warning (all white) and --on-semantic-boost (dark amber) — paired with the matching bg-semantic-* surfaces.
  • Internal sweep: all internal components (Button, Badge, Tooltip, Toast, Checkbox, Avatar, Input, NativeSelect, TicketCard, BoostTicketCard, sidebar mini-avatar) now use the explicit on-<surface> foregrounds — no more catch-all text-on-prominent-static-inverse.
  • Backwards-compat aliases preserved: --on-prominent-static-inverse (→ var(--on-prominent-inverse)) and --surface-inverse-static (→ var(--primary-inverse)) remain defined in CSS so existing consumer code referencing them via raw var() (e.g. var(--on-prominent-static-inverse, #fff)) keeps working. The Tailwind utility text-on-prominent-static-inverse is also still emitted, but new code should pick the explicit paired token.
  • Sonner → Toast rename: The toast wrapper file moved from components/ui/sonner.tsx to components/ui/toast.tsx. The wrapper component is still Toaster, and toast is now re-exported from this module so consumers no longer need import { toast } from "sonner" directly. Import path: @trading-game/design-intelligence-layer (was ... from "sonner").
  • Boost token rename (kept): --semantic-credit--semantic-boost and --semantic-credit-text--on-semantic-boost. The boost surface and on-boost text tokens are the current shipped names. Breaking change for consumers that referenced the legacy --semantic-credit* names directly.
  • ~~fill-creditfill-boost Badge variant rename~~ — not landed. The Badge variant for credit/bonus labels is still fill-credit (see current v0.16.7 entry above). The intended rename was rolled back during release.
  • ~~CreditTicketCardBoostTicketCard rename~~ — not landed. The component is still CreditTicketCard. Props remain badgeIcon, badgeLabel, badgeValue, badgeCurrency, etc. (see current entry above).
  • Button primary-inverse / secondary-inverse: Now built from the new bg-primary-inverse / text-on-primary-inverse / border-primary-inverse / text-on-prominent-inverse tokens. Visually identical to before, but the underlying class names are different.
  • Toast — action button visual fix: The toast action button (the inverse-coloured "Undo" pill) now uses primary-inverse colouring (white bg + blue text) to match the dark toast surface, instead of the previous bg-primary (blue on dark — low hierarchy / wrong surface-context).

v0.12.0

  • New semantic tokens — credit/boost: --semantic-boost (#F9840F amber-orange) and --on-semantic-boost (#521900 deep amber, updated in v0.16.7 — was #713813) live in both src/styles.css and app/globals.css. Tailwind utilities: bg-semantic-boost, text-on-semantic-boost, plus bg-semantic-boost-surface for the tinted pill background.
  • Badge — fill-credit variant: Variant for credit/bonus labels — bg-semantic-boost-surface background with text-on-semantic-boost foreground. Pair with a Gift or TicketPlus icon. Badge base class no longer applies tracking-wide; letter spacing is default on all badge variants. (Visual updated in v0.16.7 — see entry above.)
  • CreditTicketCard component: Exported alongside TicketCard. Renders the same ticket shell (notches, tear-line, shimmer stub) but with a variable-height card body and an inline <Badge variant="fill-credit"> next to or below the value for credit/bonus display. Props include icon, label, value, currency, stubLabel, onStubClick, stubDisabled, badgeIcon, badgeLabel, badgeValue, badgeCurrency, badgeInfo, compact.
  • TicketCardcompact prop: New boolean prop (default false). When true, reduces left-content padding from px-4 gap-4 to px-2 gap-2 for mobile layouts.
  • TicketCard — welcome-credit props removed: welcomeCredit, freeCredit (deprecated alias), creditUsed, creditTotal, creditCurrency, creditLabel, creditFullWidth, and the Progress-based credit bar have been removed. Use CreditTicketCard for the credit use case.
  • tracking-wide / tracking-widest removed from several components: Badge base class, Link base class, PaginationLink, CommandShortcut, ContextMenuShortcut, DropdownMenuShortcut, and MenubarShortcut no longer apply letter-spacing overrides. All render with default tracking.

v0.10.1

  • TicketCard — Wallet icon: Default icon in the circular badge is now a Wallet icon (replaces CircleDollarSign).
  • TicketCard — sentence case labels: The label above the value (e.g. "Total balance") and the stub label (e.g. "Deposit") no longer apply uppercase or tracking-widest. Both render in sentence case with default letter spacing.
  • Select — sm / md / lg sizes: SelectTrigger now accepts size="sm" (h-8, text-xs) | "md" (h-10, text-sm, default) | "lg" (h-12, text-base), matching the Input and InputGroup height scale. The previous "default" size alias is replaced by "md".

v0.10.0

  • Demo app — responsive mobile layout: The component playground is now fully mobile-friendly. Foundations / Components / Blocks navigation moves to a fixed bottom tab bar on mobile. A hamburger menu opens a slide-in Sheet to pick any component within the active tab. The desktop sidebar and inline tab switcher are unchanged.
  • Sonner toast — mobile-aware behaviour: Toasts now appear at top-center on mobile (single visible toast) and bottom-right on desktop (up to 3). Added custom token-based styling (--on-prominent background, inverse text, font-display title).
  • Toaster registered in root layout: <Toaster /> is now mounted in app/layout.tsx so toast notifications work globally.
  • InputOTP docs: README and design-system guide updated with full variant listing — OTP 4 digits, OTP 6 digits, OTP 6 split, OTP 8 digits, OTP 6 separated.