Sabil UI

Search documentation

Search for a command to run...

Foundation

Theming

Every visual decision lives in a CSS variable. Components only read tokens, so one change restyles the whole system.

Colors#

Text is pure near-black #0a0a0a. Fills and tracks use cool slate, and one blue, #056dff, carries every primary action. Hairlines and hover fills are transparent black, so they sit well on any surface. Every pair follows the name / name-foreground convention.

Base

--backgroundPage background
--foregroundDefault text
--cardCard surface
--popoverMenus, popovers

Actions

--primaryDefault button, emphasis
--ringFocus ring
--secondaryLow-emphasis fills
--destructiveDanger, errors

Neutrals

--mutedSubtle fills
--muted-foregroundSecondary text
--accentHover fills
--borderHairlines

Charts

--chart-1Series 1
--chart-2Series 2
--chart-3Series 3
--chart-4Series 4

Surfaces#

Depth is a token too. The primary surface is a vertical gradient from #3c86ff to #056dff with a 1px inner edge. Raised controls are white with a hairline ring and a 0 1px 2px shadow. Tracks are flat slate. Panels are warm off-white.

surface-sunken
surface-raised
surface-primary
bg-panel

Radius#

One base value, --radius: 0.5rem. The scale is md 6px (tab trigger), lg 8px (controls, menu items), xl 12px (menus, popovers) and 2xl 16px (cards, bubbles). Nested shapes use a smaller step than their container.

sm
md
lg
xl
2xl
full

Typography#

Inter for interface text, Geist Mono for code. Buttons use -0.01em tracking, headings use tight tracking.

Display36 / 600
Heading24 / 600
Title18 / 500
Body text for interfaces14 / 400
Caption and helper text12 / 400
Menu item description11 / 400 · 60%
const radius = '0.5rem'Mono 13

Full source#

The complete foundation file. Copy it into your project.

app/globals.css
@import "tailwindcss";@import "tw-animate-css";@import "./variants.css";@custom-variant dark (&:is(.dark *));/* ------------------------------------------------------------------ * Sabil UI — design foundation * All components read these tokens. Change a token, the whole * system follows. * ------------------------------------------------------------------ *//* Motion. Overrides Tailwind's default `ease-out` / `ease-in-out` * with stronger curves, so every existing utility gets the upgrade. */@theme {    --ease-out: cubic-bezier(0.23, 1, 0.32, 1);    --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);    --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);}@theme inline {    --font-heading: var(--font-sans);    --font-mono: var(--font-geist-mono);    --font-sans: var(--font-inter);    --color-brand: var(--brand);    --color-brand-foreground: var(--brand-foreground);    --color-panel: var(--panel);    --color-bubble: var(--bubble);    --color-bubble-strong: var(--bubble-strong);    --color-overlay-border: var(--overlay-border);    --color-sidebar-ring: var(--sidebar-ring);    --color-sidebar-border: var(--sidebar-border);    --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);    --color-sidebar-accent: var(--sidebar-accent);    --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);    --color-sidebar-primary: var(--sidebar-primary);    --color-sidebar-foreground: var(--sidebar-foreground);    --color-sidebar: var(--sidebar);    --color-chart-5: var(--chart-5);    --color-chart-4: var(--chart-4);    --color-chart-3: var(--chart-3);    --color-chart-2: var(--chart-2);    --color-chart-1: var(--chart-1);    --color-ring: var(--ring);    --color-input: var(--input);    --color-border: var(--border);    --color-destructive: var(--destructive);    --color-accent-foreground: var(--accent-foreground);    --color-accent: var(--accent);    --color-muted-foreground: var(--muted-foreground);    --color-muted: var(--muted);    --color-secondary-foreground: var(--secondary-foreground);    --color-secondary: var(--secondary);    --color-primary-foreground: var(--primary-foreground);    --color-primary: var(--primary);    --color-popover-foreground: var(--popover-foreground);    --color-popover: var(--popover);    --color-card-foreground: var(--card-foreground);    --color-card: var(--card);    --color-foreground: var(--foreground);    --color-background: var(--background);    --radius-sm: calc(var(--radius) * 0.5);    --radius-md: calc(var(--radius) * 0.75);    --radius-lg: var(--radius);    --radius-xl: calc(var(--radius) * 1.5);    --radius-2xl: calc(var(--radius) * 2);    --radius-3xl: calc(var(--radius) * 2.5);    --radius-4xl: calc(var(--radius) * 3);}/* * Light. Pure neutrals (#0a0a0a family), slate fills for tracks, * one blue for primary actions. Values taken from the reference design. * Radius scale: md 6 · lg 8 · xl 12 · 2xl 16. */:root {    --radius: 0.5rem;    --background: #ffffff;    --foreground: #0a0a0a;    --card: #ffffff;    --card-foreground: #0a0a0a;    --popover: #ffffff;    --popover-foreground: #0a0a0a;    --primary: #056dff;    --primary-foreground: #ffffff;    --secondary: #f1f5f9;    --secondary-foreground: #0f172b;    --muted: #f1f5f9;    --muted-foreground: #45556c;    --accent: #0a0a0a0a;    --accent-foreground: #0a0a0a;    --destructive: #e7000b;    --border: #0a0a0a14;    --input: #0a0a0a1f;    --ring: #056dff;    --brand: var(--primary);    --brand-foreground: var(--primary-foreground);    --chart-1: #056dff;    --chart-2: #3c86ff;    --chart-3: #8ec5ff;    --chart-4: #1447e6;    --chart-5: #bedbff;    --sidebar: #fafafa;    --sidebar-foreground: #0a0a0a;    --sidebar-primary: #056dff;    --sidebar-primary-foreground: #ffffff;    --sidebar-accent: #0a0a0a0a;    --sidebar-accent-foreground: #0a0a0a;    --sidebar-border: #0a0a0a14;    --sidebar-ring: #056dff;    /* Depth */    --primary-highlight: #3c86ff;    --surface-edge: #00000040;    --overlay-border: #0a0a0a0f;    --panel: #f9f8f6;    --bubble: #0a0a0a0a;    --bubble-strong: #0a0a0a14;    --raised: #ffffff;    --sunken: #f1f5f9;    --dot: #0a0a0a1f;    --shadow-color: #0000000d;}.dark {    --background: #0a0a0a;    --foreground: #fafafa;    --card: #121212;    --card-foreground: #fafafa;    --popover: #161616;    --popover-foreground: #fafafa;    --primary: #2b7bff;    --primary-foreground: #ffffff;    --secondary: #1d1d1f;    --secondary-foreground: #fafafa;    --muted: #1d1d1f;    --muted-foreground: #a1a1a6;    --accent: #ffffff0f;    --accent-foreground: #fafafa;    --destructive: #ff6467;    --border: #ffffff17;    --input: #ffffff24;    --ring: #3c86ff;    --chart-1: #3c86ff;    --chart-2: #6aa6ff;    --chart-3: #8ec5ff;    --chart-4: #1f63e8;    --chart-5: #bedbff;    --sidebar: #0f0f0f;    --sidebar-foreground: #fafafa;    --sidebar-primary: #3c86ff;    --sidebar-primary-foreground: #ffffff;    --sidebar-accent: #ffffff0f;    --sidebar-accent-foreground: #fafafa;    --sidebar-border: #ffffff17;    --sidebar-ring: #3c86ff;    --primary-highlight: #5c9bff;    --surface-edge: #00000080;    --overlay-border: #ffffff14;    --panel: #1a1918;    --bubble: #ffffff0f;    --bubble-strong: #ffffff1f;    --raised: #1d1d1f;    --sunken: #1d1d1f;    --dot: #ffffff1a;    --shadow-color: #00000066;}/* * Surface utilities — the signature look. * surface-primary : blue gradient, 1px inner edge, soft drop shadow. * surface-raised  : white control with a hairline ring (outline button). * surface-sunken  : slate track (tab list, segmented control). * bg-dots         : dotted canvas for previews. */@utility surface-primary {    background-image: linear-gradient(180deg, var(--primary-highlight) 0%, var(--primary) 100%);    box-shadow:        inset 0 0 1px var(--surface-edge),        inset 0 1px 0 0 #ffffff1f,        0 1px 2px 0 var(--shadow-color);}@utility surface-raised {    background-color: var(--raised);    box-shadow:        0 0 0 1px var(--border),        0 1px 2px 0 var(--shadow-color);}@utility surface-sunken {    background-color: var(--sunken);}@utility bg-dots {    background-color: var(--background);    background-image: radial-gradient(var(--dot) 1px, transparent 1px);    background-size: 20px 20px;}@layer base {    * {        @apply border-border outline-ring/50;    }    html {        @apply font-sans;        scroll-padding-top: 5rem;    }    body {        @apply bg-background text-foreground;        letter-spacing: -0.006em;    }    ::selection {        background-color: color-mix(in oklch, var(--brand) 22%, transparent);    }    button:not(:disabled),    [role="button"]:not(:disabled) {        cursor: pointer;    }    /* Every overlay (dropdown, popover, dialog, sheet, tooltip...) enters     * with the strong ease-out unless a component sets its own easing. */    [data-slot$="-content"] {        --tw-ease: var(--ease-out);    }}@media (prefers-reduced-motion: reduce) {    *,    ::before,    ::after {        animation-duration: 1ms !important;        transition-duration: 1ms !important;    }}