tradecn-slate

A neutral theme with light and dark palettes: cool surfaces, a blue primary, and bluish green and vermilion for up and down.

:root {
  --accent: oklch(0.93 0.012 245);
  --accent-foreground: oklch(0.25 0.03 245);
  --background: oklch(0.985 0.003 250);
  --border: oklch(0.88 0.008 250);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.2 0.02 250);
  --chart-1: oklch(0.5 0.15 245);
  --chart-2: oklch(0.58 0.15 60);
  --chart-3: oklch(0.52 0.13 165);
  --chart-4: oklch(0.5 0.15 330);
  --chart-5: oklch(0.58 0.12 90);
  --chart-6: oklch(0.52 0.12 225);
  --chart-7: oklch(0.5 0.18 40);
  --chart-8: var(--color-foreground);
  --destructive: oklch(0.53 0.22 27);
  --down: oklch(0.46 0.19 40);
  --down-soft: oklch(0.46 0.19 40 / 18%);
  --expiring: oklch(0.53 0.2 350);
  --expiring-soft: oklch(0.53 0.2 350 / 18%);
  --flat: var(--color-muted-foreground);
  --flat-soft: oklch(0.55 0 0 / 14%);
  --foreground: oklch(0.2 0.02 250);
  --input: oklch(0.88 0.008 250);
  --link-1: oklch(0.53 0.2 25);
  --link-2: oklch(0.5 0.15 250);
  --link-3: oklch(0.5 0.13 150);
  --link-4: oklch(0.52 0.12 85);
  --muted: oklch(0.95 0.006 250);
  --muted-foreground: oklch(0.46 0.02 250);
  --panel-active: var(--color-primary);
  --panel-drag-target: var(--color-ring);
  --panel-error: var(--color-destructive);
  --panel-sync: oklch(0.5 0.12 220);
  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.2 0.02 250);
  --primary: oklch(0.5 0.15 245);
  --primary-foreground: oklch(0.99 0.003 250);
  --radius: 0.375rem;
  --ring: oklch(0.5 0.15 245);
  --secondary: oklch(0.95 0.006 250);
  --secondary-foreground: oklch(0.25 0.02 250);
  --sidebar: oklch(0.965 0.005 250);
  --sidebar-accent: oklch(0.92 0.01 245);
  --sidebar-accent-foreground: oklch(0.25 0.03 245);
  --sidebar-border: oklch(0.88 0.008 250);
  --sidebar-foreground: oklch(0.2 0.02 250);
  --sidebar-primary: oklch(0.5 0.15 245);
  --sidebar-primary-foreground: oklch(0.99 0.003 250);
  --sidebar-ring: oklch(0.5 0.15 245);
  --stale: oklch(0.52 0.13 75);
  --stale-soft: oklch(0.52 0.13 75 / 16%);
  --tradecn-color-body-bg: oklch(0.985 0.003 250);
  --tradecn-color-body-fg: oklch(0.2 0.02 250);
  --tradecn-font-accessible: 'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif;
  --tradecn-font-accessible-mono: 'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --tradecn-font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --tradecn-font-numeric: var(--tradecn-font-sans);
  --tradecn-font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --tradecn-font-weight-body: 450;
  --tradecn-font-weight-grid: 500;
  --tradecn-line-height-body: 1.45;
  --tradecn-line-height-grid: 1.35;
  --tradecn-numeric-variant: lining-nums tabular-nums;
  --tradecn-text-size-body: 14px;
  --tradecn-text-size-grid: 13px;
  --tradecn-text-size-grid-min: 12px;
  --up: oklch(0.52 0.13 165);
  --up-soft: oklch(0.52 0.13 165 / 18%);
}

.dark {
  --accent: oklch(0.29 0.02 245);
  --accent-foreground: oklch(0.95 0.008 250);
  --background: oklch(0.16 0.01 250);
  --border: oklch(0.3 0.012 250);
  --card: oklch(0.2 0.012 250);
  --card-foreground: oklch(0.95 0.008 250);
  --chart-1: oklch(0.74 0.12 240);
  --chart-2: oklch(0.78 0.15 65);
  --chart-3: oklch(0.74 0.15 165);
  --chart-4: oklch(0.74 0.15 330);
  --chart-5: oklch(0.85 0.13 90);
  --chart-6: oklch(0.78 0.11 225);
  --chart-7: oklch(0.7 0.18 40);
  --chart-8: var(--color-foreground);
  --destructive: oklch(0.7 0.19 22);
  --down: oklch(0.66 0.19 40);
  --down-soft: oklch(0.66 0.19 40 / 18%);
  --expiring: oklch(0.74 0.17 350);
  --expiring-soft: oklch(0.74 0.17 350 / 18%);
  --flat: var(--color-muted-foreground);
  --flat-soft: oklch(0.7 0 0 / 14%);
  --foreground: oklch(0.95 0.008 250);
  --input: oklch(0.32 0.012 250);
  --link-1: oklch(0.7 0.19 25);
  --link-2: oklch(0.7 0.14 250);
  --link-3: oklch(0.74 0.14 150);
  --link-4: oklch(0.85 0.15 90);
  --muted: oklch(0.24 0.01 250);
  --muted-foreground: oklch(0.72 0.015 250);
  --panel-active: var(--color-primary);
  --panel-drag-target: var(--color-ring);
  --panel-error: var(--color-destructive);
  --panel-sync: oklch(0.78 0.12 220);
  --popover: oklch(0.21 0.012 250);
  --popover-foreground: oklch(0.95 0.008 250);
  --primary: oklch(0.74 0.12 240);
  --primary-foreground: oklch(0.16 0.02 250);
  --ring: oklch(0.7 0.12 240);
  --secondary: oklch(0.26 0.012 250);
  --secondary-foreground: oklch(0.95 0.008 250);
  --sidebar: oklch(0.13 0.01 250);
  --sidebar-accent: oklch(0.24 0.012 250);
  --sidebar-accent-foreground: oklch(0.95 0.008 250);
  --sidebar-border: oklch(0.3 0.012 250);
  --sidebar-foreground: oklch(0.95 0.008 250);
  --sidebar-primary: oklch(0.74 0.12 240);
  --sidebar-primary-foreground: oklch(0.16 0.02 250);
  --sidebar-ring: oklch(0.7 0.12 240);
  --stale: oklch(0.8 0.15 80);
  --stale-soft: oklch(0.8 0.15 80 / 16%);
  --tradecn-color-body-bg: oklch(0.16 0.01 250);
  --tradecn-color-body-fg: oklch(0.95 0.008 250);
  --tradecn-font-accessible: 'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif;
  --tradecn-font-accessible-mono: 'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --tradecn-font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --tradecn-font-numeric: var(--tradecn-font-sans);
  --tradecn-font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --tradecn-font-weight-body: 450;
  --tradecn-font-weight-grid: 500;
  --tradecn-line-height-body: 1.45;
  --tradecn-line-height-grid: 1.35;
  --tradecn-numeric-variant: lining-nums tabular-nums;
  --tradecn-text-size-body: 14px;
  --tradecn-text-size-grid: 13px;
  --tradecn-text-size-grid-min: 12px;
  --up: oklch(0.77 0.14 165);
  --up-soft: oklch(0.77 0.14 165 / 18%);
}

@layer base {
  :root {
    font-variant-numeric: var(--tradecn-numeric-variant);
  }
  .tradecn-num, [data-numeric] {
    font-variant-numeric: var(--tradecn-numeric-variant);
  }
}
:root[data-accessibility="hyperlegible"], [data-accessibility="hyperlegible"] {
  --tradecn-font-sans: var(--tradecn-font-accessible);
  --tradecn-font-mono: var(--tradecn-font-accessible-mono);
}

Installation

pnpm dlx shadcn@latest add tradecn/ui/tradecn-slate#v2.0.0
npx shadcn@latest add tradecn/ui/tradecn-slate#v2.0.0
yarn dlx shadcn@latest add tradecn/ui/tradecn-slate#v2.0.0
bunx --bun shadcn@latest add tradecn/ui/tradecn-slate#v2.0.0

Usage

A theme installs no files. It rewrites the variables in your stylesheet. Configure the @tradecn namespace for the release you want, following Installation. When reading an older versioned page, select that same release in the Installation guide's version menu before copying the namespace. Replace src/index.css below with the tailwind.css path from your components.json:

pnpm dlx shadcn@latest add @tradecn/tradecn-slate --diff src/index.css
npx shadcn@latest add @tradecn/tradecn-slate --diff src/index.css
yarn dlx shadcn@latest add @tradecn/tradecn-slate --diff src/index.css
bunx --bun shadcn@latest add @tradecn/tradecn-slate --diff src/index.css

A bare --diff does not show theme CSS changes. Review the stylesheet diff and commit your current files, then remove --diff and the stylesheet path to install the theme.

API Reference

This one overwrites

Non-theme tradecn items add only missing tokens. Slate replaces shadcn's palette variables (--background, --foreground, --card, --popover, --primary, --secondary, --muted, --accent, their foreground partners, --destructive, --border, --input, and --ring), all eight --chart-* colors, the eight --sidebar-* colors, and --radius. It sets every tradecn token in both modes, resetting typography tokens to their defaults, including custom --tradecn-font-* stacks. It leaves your app's --font-sans alone.

It also adds the numeric variant on :root and the numeric hooks, plus the hyperlegible font remap described in Typography. Restore your stylesheet from version control to undo the installation.

Two sides

Light uses cool off-white with near-black text and white cards. Dark uses near-black with off-white text, a lighter blue primary, and cards a step lighter than the page.

Token Light Dark
--background oklch(0.985 0.003 250) oklch(0.16 0.01 250)
--foreground oklch(0.2 0.02 250) oklch(0.95 0.008 250)
--card oklch(1 0 0) oklch(0.2 0.012 250)
--primary oklch(0.5 0.15 245) oklch(0.74 0.12 240)
--up oklch(0.52 0.13 165) oklch(0.77 0.14 165)
--down oklch(0.46 0.19 40) oklch(0.66 0.19 40)

The marks use a lightness suited to each background. Your app's dark class selects the palette, the way shadcn does it. Color explains why themes have two palettes.

The direction pair

Slate adapts the bluish green and vermilion from Okabe and Ito's palette for its two backgrounds. Their guidance combines color with labels, shapes, or patterns. Both direction colors clear 4.5 to 1 on the page and on a card in both modes. If the desk wants red for up, choose tradecn-slate-east.

The repository's luminance calculation gives the direction pair a grayscale ratio of 1.54 to 1 in light and 1.72 to 1 in dark. Tests require at least 1.3 to 1. These gaps do not guarantee distinguishability in print or with color vision deficiencies, and both fall below the 3 to 1 threshold for lightness as an additional visual distinction in WCAG's use-of-color guidance. Keep direction visible in signs or labels.

flat follows your muted foreground, panel-active follows primary, panel-drag-target follows ring, and panel-error follows destructive.

The items' light marks are this theme's

tradecn-amber is tradecn.dev's default theme. The light marks an item installs (--up, --down, --stale, --expiring, --panel-sync, and the four --link-*) use slate's values, so a project without a theme keeps green for up. These defaults clear 4.5 to 1 on white. Installing slate preserves those light marks while replacing the surfaces and primary. The dark defaults are tuned for shadcn's surfaces and differ from slate's.

Items never overwrite an existing variable. A project that installed an item before 1.0 keeps its older light values until it removes them and installs the item again, or installs a theme.

The colors are checked

The contrast tests require 4.5 to 1 for the declared text/surface pairs and for primary, up, down, stale, expiring, link-1 through link-4, panel-sync, destructive, and ring on the background and card, in both modes, and for destructive on a destructive control's own tint, 10 percent in light mode and 20 in dark, over the background, a card, and a popover, as every shadcn style's destructive Button draws it. They blend a tint the way a browser does, in gamma-encoded sRGB.

The foreground clears 4.5 to 1 on every tint an item puts text on: the soft tints, and primary and destructive at 15 and 12 percent, over the background and a card. A tone's own color on its tint can fall below that in light mode, so tinted badges, rule highlights, a countdown running short, a stale feed tier, the session warning, the active inquiry, and the ladder's own size carry foreground text, state text inside them included, and a selected grid row is marked by a bar rather than filled. The marks a grid cell carries also clear 4.5 to 1 on the grid's own tints: the focused column and the ladder's mid row. A cell's fill flash starts at its direction's tint and fades within its window, holding for the whole window under reduced motion, and a new row's arrival highlight tints it flat-soft for a second and a half; state-colored text can read below 4.5 to 1 on either while it lasts. These checks do not cover every component state or chart colors.

The calculated light primary contrast is 5.6 to 1 on the page. The tightest checked pair is up on the depth ladder's light mid row, at 4.52 to 1, down in Slate East, which turns the pair around; on the light page it is 4.8 to 1. The browser matrix installs each theme separately into three consumers and reads every theme color back from the page in both modes.

What it does not do

The theme installs no components, font files, or runtime switcher. Your app controls the dark class to select between the installed palettes.

This page is docs/tradecn-slate.md at v2.0.0.