Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
Guides
  • Overview
  • Get started
  • Shadcn compatibility
  • Architecture
  • Agent surface
Skins
  • How to build a skin
  • Refined
  • Windows XP
  • Liquid metal
  • Rig
  • Flat
  • Modern Apple
  • Cuicui
  • Linear
  1. Skins
  2. Linear
Skin / Advanced pack

Linear

Linear-inspired skin: indigo brand on a cool neutral ramp, a flat 13px chrome band, 4px radius, hairline borders instead of elevation, and pill-shaped filled actions.

One command installs the pack's theme.css, skin.css and skin.config.tsx over the refined library — tokens, skin.css and slot classes together, plus any runtime the pack mounts. Reinstalling refined resets the theme and skin targets back to the baseline look.

Skin pack
npx shadcn@latest add http://127.0.0.1:3000/r/linear/skin.json
See registry manifest

Pack source

The 3 files this pack ships, read straight from the registry. Each is yours to own and edit after install.

theme.css — token block scoped to [data-skin]; a theme skin's entire brand
src/registry/skin-packs/linear/theme.css
/*
* ADVANCED skin (tokens + skin.css material + skin.config slots) — a reading of Linear's product UI:
* indigo brand on a near-neutral cool ramp, 13px chrome, 4px radius knob, hairline borders instead of
* elevation, and floating surfaces that are the ONLY things allowed a real shadow.
* Scoped [data-skin="linear"] only, never :root, so skins co-compile; a standalone install adds its own
* `@custom-variant dark`. Raw token re-values only — @theme belongs to the baseline.
*/
​
[data-skin="linear"] {
/* 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", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--font-mono: "Berkeley Mono", ui-monospace, SFMono-Regular, "SF 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-micro: 0.625rem; /* 10px */
--text-caption: 0.6875rem; /* 11px */
--text-label: 0.8125rem; /* 13px — sm controls, nav rows, tooltips */
--text-body: 0.8125rem; /* 13px — THE workhorse */
--text-body-lg: 0.9375rem; /* 15px */
--text-meta: 0.6875rem; /* 11px — label pills */
​
/* Headings stay tight and semibold; no 700 anywhere (Linear tops out at semibold). */
--text-heading-4: 0.875rem;
--text-heading-4--line-height: 1.4;
--text-heading-4--font-weight: 600;
--text-heading-3: 1rem;
--text-heading-3--line-height: 1.4;
--text-heading-3--font-weight: 600;
--text-heading-2: 1.1875rem;
--text-heading-2--line-height: 1.3;
--text-heading-2--font-weight: 600;
--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--font-weight: 600;
--text-display--letter-spacing: -0.022em;
--text-title-sm: 0.875rem;
--text-title-sm--line-height: 1.4;
--text-title-sm--font-weight: 600;
--text-title-md: 1rem;
--text-title-md--line-height: 1.4;
--text-title-md--font-weight: 600;
--text-title-lg: 1.1875rem;
--text-title-lg--line-height: 1.3;
--text-title-lg--font-weight: 600;
​
/* Light: pure-white surfaces on a cool gray paper. card == background so the shell's content frame
reads as the one white sheet Linear floats its whole app on. */
--background: oklch(1 0 0);
--foreground: oklch(0.2228 0.0079 274.573); /* #1a1b1f */
--card: oklch(1 0 0);
--card-foreground: oklch(0.2228 0.0079 274.573);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.2228 0.0079 274.573);
--primary: oklch(0.5674 0.1585 275.206); /* #5e6ad2 — THE Linear indigo */
--primary-foreground: oklch(1 0 0);
--muted: oklch(0.9614 0.0013 286.375); /* #f2f2f3 */
--muted-foreground: oklch(0.5401 0.0124 280.281); /* #6d6e76 */
--secondary: oklch(0.9614 0.0013 286.375);
--secondary-foreground: oklch(0.2228 0.0079 274.573);
--accent: oklch(0.9434 0.0013 286.374); /* #ececed — hover / selection fill */
--accent-foreground: oklch(0.2228 0.0079 274.573);
--destructive: oklch(0.6534 0.1835 23.679); /* #eb5757 */
--destructive-foreground: oklch(1 0 0);
--ring-opacity: 1;
--border: oklch(0.9255 0.0027 286.347 / var(--ring-opacity)); /* #e6e6e8 hairline */
--input: oklch(0.9042 0.0027 286.346); /* #dfdfe1 — fields carry a touch more edge */
--ring: oklch(0.5674 0.1585 275.206); /* focus == brand */
--canvas: oklch(0.9734 0.0013 286.375); /* #f6f6f7 — the paper the white sheet sits on */
​
/* Popup rows: a whisper of ink, no brand tint (Linear highlights rows, it doesn't color them).
Derived from --foreground so the dark block inherits the same recipe with no restatement. */
--popup-item-foreground: var(--foreground);
--popup-item-highlight-background: oklch(from var(--foreground) l c h / 0.05);
--popup-item-disabled-opacity: 0.4;
​
/* Surfaces are opaque: no glass anywhere. The modal scrim is the only dim. */
--popover-opacity: 1;
--backdrop-blur-popover: 0px;
--overlay-opacity: 0.3;
--backdrop-blur-overlay: 0px;
--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-y: 1;
--shadow-control-multiplier: 0.6;
--shadow-panel-multiplier: 1;
--shadow-popover-multiplier: 5;
--shadow-modal-multiplier: 8;
--shadow-ambient-multiplier: 8;
​
/* THE radius knob: 4px. Controls ride 1.5x (6px), panels 3x (12px) — small, never pill-shaped by
token (the button pill is a deliberate slot choice, not a radius side effect). */
--radius: 0.25rem;
--radius-control: calc(var(--radius) * 1.5);
--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 */
--corner-shape: round;
--corner-shape-control: var(--corner-shape);
--corner-shape-popover: var(--corner-shape);
--corner-shape-panel: var(--corner-shape);
​
/* Concentric floating-list geometry. Re-declared (not inherited): the baseline formulas live on
:root and would bake the REFINED --radius-control into this pack's popups. */
--popover-padding: 0.25rem;
/* Rows carry the SAME 6px corner as controls (Linear has one row corner everywhere: menus, nav,
tree, and the sliding selection pill, which the library shapes from this token). */
--radius-popup-item: var(--radius-control);
--nest-safe: 1px;
--nest-corner-ratio: 3.41421356;
--radius-popup-item-fit: min(var(--radius-popup-item), calc(var(--control-h-xs) / 2));
--radius-popover: calc(var(--radius-popup-item-fit) + clamp(0px, var(--radius-popup-item-fit) * 1000, var(--popover-padding) - 1px));
​
--padding-x: 0.75rem;
--padding-y: 0.5rem;
​
/* Control ramp: 32px base, so `sm` lands on Linear's 28px button. Re-derived for the same reason
the popup radii are: the ramp formulas on :root would bake the baseline's 36px base. */
--control-h: 2rem;
--control-h-xs: round(var(--control-h) * 0.78, 1px);
--control-h-sm: round(var(--control-h) * 0.89, 1px);
--control-h-md: var(--control-h);
--control-h-lg: round(var(--control-h) * 1.11, 1px);
​
/* Motion is a snap, not a glide — Linear's whole feel rests on this. */
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
--ease-emphasized: cubic-bezier(0.16, 1, 0.3, 1);
--duration-fast: 90ms;
--duration-base: 130ms;
--duration-slow: 200ms;
}
​
/* Dark tokens: the real Linear night — near-black canvas, panel one step up, popups two.
Every surface color is RESTATED (the light block above out-specifies the baseline's :where(.dark)). */
:where(.dark) [data-skin="linear"],
.dark[data-skin="linear"] {
--background: oklch(0.1739 0.0041 285.967); /* #101012 */
--foreground: oklch(0.9784 0.0011 197.139); /* #f7f8f8 */
--card: oklch(0.1739 0.0041 285.967);
--card-foreground: oklch(0.9784 0.0011 197.139);
--popover: oklch(0.2277 0.0057 285.935); /* #1c1c1f — menus sit above the sheet */
--popover-foreground: oklch(0.9784 0.0011 197.139);
--primary: oklch(0.5674 0.1585 275.206);
--primary-foreground: oklch(1 0 0);
--muted: oklch(0.2277 0.0057 285.935);
--muted-foreground: oklch(0.6488 0.0146 262.359); /* #8a8f98 */
--secondary: oklch(0.2277 0.0057 285.935);
--secondary-foreground: oklch(0.9784 0.0011 197.139);
--accent: oklch(0.2494 0.0075 285.844); /* #212125 */
--accent-foreground: oklch(0.9784 0.0011 197.139);
--destructive: oklch(0.6534 0.1835 23.679);
--destructive-foreground: oklch(1 0 0);
--ring-opacity: 1;
--border: oklch(0.2618 0.0113 278.089 / var(--ring-opacity)); /* #23242a */
--input: oklch(0.2986 0.0109 278.197);
--ring: oklch(0.6102 0.1404 276.317); /* the indigo lifts a rung to stay legible on ink */
--canvas: oklch(0.139 0.0029 246.256); /* #08090a */
--popup-item-highlight-background: oklch(from var(--foreground) l c h / 0.07);
--shadow-color: oklch(0 0 0);
--shadow-highlight: oklch(1 0 0 / 0.05);
--shadow-opacity: 1;
}
​
/* prefers-reduced-motion: token-driven, so every derived tempo flattens together. */
@media (prefers-reduced-motion: reduce) {
[data-skin="linear"] {
--duration-fast: 0ms;
--duration-base: 0ms;
--duration-slow: 0ms;
}
}
​

On this page

  • Installation
  • Pack source