


Outerbase Studio is built with Next.js 14 (App Router, RSC), TypeScript, and Tailwind CSS v4. Component primitives come from shadcn/ui ("new-york" style, slate base color, CSS variables mode). The internal design system is called Orbit, exposing semantic ob-\* utility classes and CSS custom properties. CodeMirror powers the SQL editor. The marketing site (outerbase.com) is also Next.js, deployed via Cloudflare Workers (open-next).
Primary font: Inter (self-hosted WOFF2, weights 400/500/600/700, font-display: swap). Falls back to Helvetica โ ui-sans-serif โ system-ui. Monospace stack: ui-monospace โ SFMono-Regular โ Menlo โ Monaco โ Consolas.
The scale is intentionally tight โ optimised for information-dense database UIs, not editorial reading.
Outerbase uses a neutral-dominant, near-monochrome palette. Pure black (#000) and white (#fff) anchor the system. Accent color is almost absent โ the primary brand action is rendered in dark charcoal (light mode) or near-white (dark mode), keeping the focus on user data rather than chrome. The only chromatic accent is a blue focus ring (oklch(0.707 0.165 254.624)) and red destructive states. SQL table names in the editor use a distinctive hot pink (#e84393 / #fd79a8 dark) โ a deliberate pop of color as a code-syntax cue. OKLCH is used throughout the Orbit token palette for perceptually uniform steps.
Tailwind CSS v4 default spacing scale (base unit 4px / 0.25rem). Key component heights are codified as Orbit size utilities:
Container max-width is 1400px with 2rem horizontal padding. Scrollbars are 6px wide/tall with transparent-until-hover thumbs.
.interactive class wrapper to enable hover/active states and cursor-pointer..ob-focus applies a 1px ring in --color-ob-focus (blue) via focus-visible, no outline flash.cubic-bezier(0.2, 0, 0, 1.5).The app uses a panel/window metaphor with tab-based navigation (.libsql-window-tab). The marketing site is a single-column scroll layout with full-width sections and a max-width container. Dark/light mode is toggled via the .dark class on a parent element (not prefers-color-scheme media query alone), enabling user-controlled switching. overscroll-behavior-x: none is applied globally to prevent horizontal pull-to-refresh.
/\* ============================================
OUTERBASE / ORBIT DESIGN SYSTEM โ CSS VARIABLES
Source: github.com/outerbase/studio
Framework: Tailwind CSS v4 + shadcn/ui (new-york)
============================================ \*/
:root {
/_ โโ Typography โโ _/
--font-sans: Inter, Helvetica, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
/_ Type scale (compact โ data UI) _/
--text-xs: 10px; /_ line-height โ 2.0 _/
--text-sm: 12px; /_ line-height โ 1.33 _/
--text-base: 14px; /_ line-height โ 1.43 _/
/_ โโ Spacing / Radius โโ _/
--radius: 0.5rem; /_ 8px โ lg _/
/_ --radius-md = calc(var(--radius) - 2px) โ 6px _/
/_ --radius-sm = calc(var(--radius) - 4px) โ 4px _/
/_ โโ Easing & Motion โโ _/
--ease-bounce: cubic-bezier(0.2, 0, 0, 1.5);
--default-transition-duration: 100ms;
/_ โโ Semantic tokens (light mode) โโ _/
--background: #fff;
--foreground: #444;
--card: #fff;
--card-foreground: #444;
--popover: #fff;
--popover-foreground: #444;
--primary: #000;
--primary-foreground: #fff;
--secondary: #f5f5f5;
--secondary-foreground: #444;
--muted: #eee;
--muted-foreground: #888;
--accent: #f1f5f9;
--accent-foreground: #444;
--destructive: #f1f5f9;
--destructive-foreground: red;
--border: #e5e5e5;
--input: #e2e8f0;
--ring: #ccc;
--selected: #e2e8f0;
/_ Surface _/
--color-surface: #fff;
--color-surface-hover: #eee;
/_ Scrollbars _/
--scroll-bar: #d4d4d4;
--scroll-bar-hover: #ccc;
/_ Table grid & cell colors _/
--color-table-grid: #eee;
--color-table-change: #f6e58d;
--color-table-cell-number: #2980b9;
--color-table-cell-code: #95a5a6;
--color-table-row-selected: #e4e6f1;
--color-table-row-removed: #ff8b81;
--color-table-row-new: #7bed9f;
/_ โโ ORBIT base palette (light) โโ _/
--color-ob-base-100: #fff; /_ oklch(0.985 0 0) _/
--color-ob-base-200: oklch(0.985 0 0);
--color-ob-base-300: oklch(0.97 0 0);
--color-ob-base-400: oklch(0.922 0 0);
--color-ob-base-500: oklch(0.87 0 0);
--color-ob-base-1000: oklch(0.205 0 0);
--color-ob-border: oklch(0.922 0 0);
--color-ob-border-active: oklch(0.708 0 0);
/_ ORBIT text _/
--text-color-ob-base-100: oklch(0.556 0 0);
--text-color-ob-base-200: oklch(0.439 0 0);
--text-color-ob-base-300: oklch(0.205 0 0);
--text-color-ob-destructive: oklch(0.577 0.245 27.325); /_ red-600 _/
--text-color-ob-inverted: #fff;
/_ ORBIT buttons (light) _/
--color-ob-btn-primary-bg: oklch(0.31 0 0);
--color-ob-btn-primary-bg-hover: oklch(0.23 0 0);
--color-ob-btn-primary-border: oklch(0.556 0 0);
--color-ob-btn-primary-border-hover: oklch(0.439 0 0);
--color-ob-btn-secondary-bg: #fff;
--color-ob-btn-secondary-bg-hover: oklch(0.985 0 0);
--color-ob-btn-secondary-border: oklch(0.922 0 0);
--color-ob-btn-secondary-border-hover: oklch(0.87 0 0);
--color-ob-btn-ghost-bg-hover: oklch(0.96 0 0);
--color-ob-btn-destructive-bg: oklch(0.577 0.245 27.325);
--color-ob-btn-destructive-bg-hover: oklch(0.55 0.238 27.4);
--color-ob-btn-destructive-border: oklch(0.704 0.191 22.216); /_ red-400 _/
--color-ob-btn-destructive-border-hover: oklch(0.645 0.246 16.439); /_ red-500 _/
/_ Focus _/
--color-ob-focus: oklch(0.707 0.165 254.624); /_ blue-400 _/
}
/_ โโ Dark mode overrides โโ _/
.dark {
--background: #0a0a0a;
--foreground: #aaa;
--card: #222;
--card-foreground: #eee;
--popover: #111;
--popover-foreground: #eee;
--primary: #fff;
--primary-foreground: #000;
--secondary: #171717;
--secondary-foreground: #eee;
--muted: #333;
--muted-foreground: #888;
--accent: #2a2d2e;
--accent-foreground: #eee;
--destructive: red;
--destructive-foreground: #fff;
--border: #262626;
--input: #333;
--ring: #444;
--selected: #222;
--color-surface: #000;
--color-surface-hover: #3f3f3f;
--scroll-bar: #353535;
--scroll-bar-hover: #444;
/_ ORBIT dark overrides _/
--color-ob-base-100: oklch(0.145 0 0);
--color-ob-base-200: oklch(0.205 0 0);
--color-ob-base-300: oklch(0.23 0 0);
--color-ob-base-400: oklch(0.269 0 0);
--color-ob-base-500: oklch(0.31 0 0);
--color-ob-base-1000: oklch(0.985 0 0);
--color-ob-border: oklch(0.269 0 0);
--color-ob-border-active: oklch(0.371 0 0);
--text-color-ob-base-100: oklch(0.556 0 0);
--text-color-ob-base-200: oklch(0.708 0 0);
--text-color-ob-base-300: oklch(0.985 0 0);
--text-color-ob-destructive: oklch(0.704 0.191 22.216); /_ red-400 _/
--text-color-ob-inverted: oklch(0.205 0 0);
--color-ob-btn-primary-bg: oklch(0.87 0 0);
--color-ob-btn-primary-bg-hover: oklch(0.9 0 0);
--color-ob-btn-primary-border: oklch(0.97 0 0);
--color-ob-btn-primary-border-hover: #fff;
--color-ob-btn-secondary-bg: oklch(0.205 0 0);
--color-ob-btn-secondary-bg-hover: oklch(0.23 0 0);
--color-ob-btn-secondary-border: oklch(0.269 0 0);
--color-ob-btn-secondary-border-hover: oklch(0.31 0 0);
--color-ob-btn-ghost-bg-hover: oklch(0.23 0 0);
--color-ob-btn-destructive-bg: oklch(0.424 0.199 265.638); /_ reuses blue-800 slot for red-800 _/
--color-ob-btn-destructive-bg-hover: oklch(0.46 0.195 27.2);
--color-ob-btn-destructive-border: oklch(0.396 0.141 25.723); /_ red-700 _/
--color-ob-btn-destructive-border-hover: oklch(0.577 0.245 27.325);
--color-ob-focus: oklch(0.424 0.199 265.638); /_ blue-800 _/
}
| Token | Value (light) | Usage |
|---|---|---|
| --background | #fff | Page / app canvas |
| --foreground | #444 | Default body text |
| --primary | #000 | Primary button fill, strong text |
| --primary-foreground | #fff | Text on primary button |
| --secondary | #f5f5f5 | Secondary button background |
| --muted | #eee | Dividers, disabled backgrounds |
| --muted-foreground | #888 | Placeholder / hint text |
| --accent | #f1f5f9 | Hover highlights, subtle fills |
| --border | #e5e5e5 | Default borders |
| --ring | #ccc | Generic focus ring fallback |
| --destructive-foreground | red | Error / delete text |
| --color-ob-focus | oklch(0.707 0.165 254.6) โ blue-400 | Keyboard focus ring (Orbit) |
| --color-table-cell-number | #2980b9 | Numeric cell values |
| --color-table-row-new | #7bed9f | Newly inserted rows |
| --color-table-row-removed | #ff8b81 | Deleted / staged-remove rows |
| --color-table-change | #f6e58d | Edited (unsaved) cells |
| .cm-table-name (SQL editor) | #e84393 / dark: #fd79a8 | Table name syntax highlight |
| Token | Value | Usage |
|---|---|---|
| Base unit | 4px (0.25rem) | Tailwind spacing scale base |
| Container padding | 2rem (32px) | Horizontal page padding |
| Container max-width | 1400px | Marketing site cap |
| ob-size-sm height | h-6.5 = 26px | Small buttons, compact inputs |
| ob-size-base height | h-8 = 32px | Default button/input height |
| ob-size-lg height | h-9 = 36px | Large interactive elements |
| Scrollbar width/height | 6px | Custom webkit scrollbar |
| Token | Value | Usage |
|---|---|---|
| --font-sans | Inter, Helvetica, ui-sans-serifโฆ | All UI text |
| --font-mono | ui-monospace, SFMono-Regular, Menloโฆ | Code, SQL, query editor |
| --text-xs | 10px / lh ~2.0 | Labels, badges, tiny metadata |
| --text-sm | 12px / lh ~1.33 | Secondary labels, captions |
| --text-base | 14px / lh ~1.43 | Primary body / UI text |
| Font weights used | 400, 500, 600, 700 | Regular โ Bold (Inter) |
| Token | Value | Usage |
|---|---|---|
| --radius (lg) | 0.5rem (8px) | Cards, modals, larger panels |
| --radius-md | calc(--radius - 2px) = 6px | Buttons (base, lg), inputs |
| --radius-sm | calc(--radius - 4px) = 4px | Small buttons, chips |
| rounded-full | 9999px | Circular icon buttons (.circular) |
| Scrollbar thumb | 8px | Custom scrollbar rounding |
| Token | Value | Usage |
|---|---|---|
| shadow-xs | Tailwind xs shadow | Primary & secondary buttons |
| --default-transition-duration | 100ms | All transition-colors (snappier than TW default) |
| --ease-bounce | cubic-bezier(0.2, 0, 0, 1.5) | Springy open/close motions |
| accordion-down/up | 0.2s ease-out (Radix height var) | Accordion expand/collapse |
| shake | 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) | Error shake on invalid input |
| refresh spin | 0.5s ease-in-out infinite + scale(0.9) | Loading/refresh indicator |
| float | 5s linear infinite alternate, translate(5px,15px) | Marketing hero astronaut image |
Framework & tooling: Next.js 14 (App Router) + TypeScript + Tailwind CSS v4. shadcn/ui components in "new-york" style with CSS-variable mode enabled. The internal system is named Orbit โ it surfaces as ob-_ prefixed CSS custom properties and utility classes (.ob-btn, .ob-focus, .ob-disable, .ob-size-_).
Theme support: Full light/dark mode via .dark class on a parent element. Both modes are fully specified โ not just inverted. Dark mode is notably darker than most SaaS products: background is #0a0a0a (near-true black), card is #222, popover is #111. This suits a terminal/data-tool audience.
Data confidence: High โ CSS extracted directly from the open-source repository (github.com/outerbase/studio, src/app/globals.css). The studio app and the marketing site share the same design system. Marketing site uses Next.js static output served from the same Cloudflare deployment.
.ob-btn plus a variant (.btn-primary, .btn-secondary, .btn-ghost, .btn-destructive). Hover/active states are gated behind an .interactive parent class, and toggle states via .toggle. Square and circular shape variants are via .square / .circular parents. This context-class pattern is unusual and worth noting for implementation..cm-table-name) uses hot pink โ the only saturated color in the app UI layer โ making it a recognisable brand-adjacent signature color for the product.