Skin / Advanced pack
Linear
Linear-inspired skin: indigo brand on a cool neutral ramp, a flat 13px chrome band, 8px controls on 12px panels, hairline borders instead of elevation, and 8px menu rows inset in a 4px gutter.
Source
Explore the files that define this skin. Copy any file to make it your own.
About the skin files
- theme.css
- token block scoped to [data-skin]; a theme skin's entire brand
- skin.css
- the pack's own vars + @utility helpers + descendant CSS the cva recipes can't express
- skin.config.tsx
- per-slot classes, adornments, and the motion flag
/* Values measured off the running Linear app (light + dark), converted from its LCH tokens to oklch. Hairline borders instead of elevation; floating surfaces are the only things allowed a real shadow. */[data-skin="linear"][data-skin] { --sidebar-width: 15.25rem; --control-fill: transparent; /* Inter, self-hosted by the app shell (next/font fills --font-inter); host without it falls to the system stack. */ --font-sans: var(--font-inter, "Inter"), "Inter Variable", "SF Pro Display", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --font-mono: "Berkeley Mono", "SFMono Regular", ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; /* Type scale: Linear runs one flat 13px band for ALL chrome (nav rows, controls, body) and reserves the larger rungs for content headings. Label == body on purpose. */ --text-label: 0.8125rem; /* 13px — sm controls, nav rows, tooltips */ --text-body: 0.8125rem; /* 13px — THE workhorse */ --text-body-lg: 0.9375rem; /* 15px — the editor rung */ /* Headings stay tight and semibold; no 700 anywhere (Linear tops out at semibold). */ --text-heading-4: 0.875rem; --text-heading-3: 1rem; --text-heading-3--line-height: 1.4; --text-heading-2: 1.1875rem; --text-heading-2--line-height: 1.3; --text-heading-1: 1.5rem; /* 24px — the issue-title rung */ --text-heading-1--line-height: 1.25; --text-heading-1--font-weight: 600; --text-heading-1--letter-spacing: -0.014em; --text-display: 2rem; --text-display--line-height: 1.15; --text-display--letter-spacing: -0.022em; /* Light: the content sheet is off-white (#f9f9fa), never pure white — pure white is reserved for the surfaces that float above it. card == background so the shell's frame reads as one sheet. */ --background: oklch(0.9824 0.0013 286.376); /* #f9f9fa */ --foreground: oklch(0.2221 0 0); /* #1b1b1b */ --card: oklch(0.9824 0.0013 286.376); --card-foreground: oklch(0.2221 0 0); --popover: oklch(1 0 0); /* #ffffff — menus are the one white surface */ --popover-foreground: oklch(0.3099 0.0035 286.192); /* #303032 */ --primary: oklch(0.5674 0.1585 275.206); /* #5e6ad2 — THE Linear indigo */ --primary-foreground: oklch(1 0 0); --muted: oklch(0.9343 0.0013 286.374); /* #e9e9ea */ --muted-foreground: oklch(0.4755 0.0032 286.278); /* #5c5c5e */ --secondary: oklch(0.9343 0.0013 286.374); --secondary-foreground: oklch(0.306 0.0035 286.188); /* #2f2f31 */ --accent: oklch(0.91 0.0013 286.373); /* #e1e1e2 — the selected-row fill */ --accent-foreground: oklch(0.2221 0 0); --destructive: oklch(0.576 0.1835 23.679); --destructive-foreground: oklch(1 0 0); --border: oklch(0.9128 0 0 / var(--ring-opacity)); /* #e2e2e2 hairline */ --input: oklch(0.8669 0 0); /* #d3d3d3 — fields carry a touch more edge */ --ring: oklch(0.5648 0.1585 275.564); /* #5e69d1 — Linear's focus indigo, a hair off the brand */ --canvas: oklch(0.9524 0.0013 286.375); /* #efeff0 — the paper the sheet sits on */ /* Surfaces are opaque: no glass anywhere. The modal scrim is the only dim. */ --overlay-opacity: 0.3; --scroll-fade-size: 1.5rem; /* Elevation is EARNED here: controls and panels are held by hairlines, only floating surfaces lift. Hence a near-zero control tier, a 1x panel tier, and popover/modal tiers pushed way up. */ --shadow-color: oklch(0.2217 0.0084 264.39); --shadow-highlight: oklch(1 0 0 / 0.08); /* matte: a whisper of top light, not a lit-paper rim */ --shadow-size: 1; --shadow-opacity: 0.75; --shadow-control-multiplier: 0.6; --shadow-panel-multiplier: 1; --shadow-popover-multiplier: 5; --shadow-modal-multiplier: 8; /* A 1px focus hairline, not a 2px halo — Linear rings with the indigo, it does not glow. */ --focus-ring: var(--ring); --focus-ring-width: 1px; /* Measured off the app: light darkens less than dark lightens. */ --hover-fill: oklch(from var(--foreground) l c h / 0.035); --active-fill: oklch(from var(--foreground) l c h / 0.06); /* THE radius knob: 4px. Controls ride 2x (8px, Linear's --control-border-radius), panels 3x (12px). Never pill-shaped by token — the button pill is a deliberate slot choice, not a radius side effect. */ --radius: 0.25rem; --radius-field: calc(var(--radius) * 2); /* 8px — composer shell, user bubble */ --radius-panel: calc(var(--radius) * 3); /* 12px — dialogs, code, the content sheet */ --radius-scene: calc(var(--radius) * 3.5); /* 14px */ /* Linear insets each row 4px inside the menu and rounds it at 8px; the surface keeps the 12px panel corner. */ --radius-popup-item: 0.5rem; --radius-popover: var(--radius-panel); --padding-x: 0.75rem; --padding-y: 0.5rem; /* Control ramp: 32px base, so `sm` lands on Linear's 28px chrome button. */ --control-h: 2rem; /* Motion is a snap, not a glide — Linear's whole feel rests on this. 150ms is what its controls actually transition at; 250ms is its slowest (opacity) tempo. */ --ease-standard: cubic-bezier(0.4, 0, 0.2, 1); --duration-slow: 250ms;}/* Dark tokens: the real Linear night — near-black canvas, sheet one step up, menus three. */.dark[data-skin="linear"][data-skin],.dark [data-skin="linear"][data-skin] { --background: oklch(0.1826 0.002 286.198); /* #121213 */ --foreground: oklch(1 0 0); /* Linear's primary ink is pure white, not off-white */ --card: oklch(0.1826 0.002 286.198); --card-foreground: oklch(1 0 0); --popover: oklch(0.2482 0.0019 286.263); /* #212122 — menus sit well above the sheet */ --popover-foreground: oklch(0.9247 0.0029 264.541); /* #e5e6e8 */ --muted: oklch(0.2006 0.002 286.221); /* #161617 */ --muted-foreground: oklch(0.6705 0.0029 286.321); /* #959597 */ --secondary: oklch(0.2006 0.002 286.221); --secondary-foreground: oklch(0.9187 0.0029 264.541); /* #e3e4e6 */ --accent: oklch(0.257 0.0037 286.136); /* #232325 */ --accent-foreground: oklch(1 0 0); --border: oklch(0.2517 0.004 264.495 / var(--ring-opacity)); /* #212224 */ --input: oklch(0.278 0.0055 286.04); /* #28282b */ --ring: oklch(0.6102 0.1404 276.317); /* the indigo lifts a rung to stay legible on ink */ --canvas: oklch(0.1403 0.0022 286.109); /* #09090a */ --shadow-color: oklch(0 0 0); --shadow-highlight: oklch(1 0 0 / 0.05); --shadow-opacity: 1; --hover-fill: oklch(1 0 0 / 0.06); --active-fill: oklch(1 0 0 / 0.105);}@media (min-resolution: 2dppx) { [data-skin="linear"][data-skin] { --control-rim-width: 0.5px; }}Installation
Start complete or add only Linear
Complete project installs the canonical Control UI component set and this skin in one step. Skin only replaces the active skin.config.tsx, skin-theme.css, and skin.css targets without changing component source, and includes the pack’s declared runtimes or extensions.
npx shadcn@latest add https://control-ui.dev/r/all-linear.jsonnpx shadcn@latest add https://control-ui.dev/r/skin-linear.json --overwrite