Prodular UI
Themes

Create a theme

Start from a brand color or your logo. The generator builds 12-step scales in OKLCH for light and dark, tunes them until every text pair passes WCAG AA, and exports CSS, Figma variables and a built-in theme.

Light
MRR
$184,320Up8.4%
Net retention
116%Up3.1 pts
Logo churn
2.1%Down0.8 pts
EnterpriseDraftv2.14.0PaidScheduledPast dueFailedSuggested
InsightDetected 2 hours ago

Compute spend rose 38% after the September 9 deploy

The nightly ledger export now runs on every workspace instead of only active ones. It accounts for $11,900 of the increase.

Compute, 14 days$43,210+38% vs prior
vendor-invoices-q3.csvCSV, 214 KB
Northbeam MSA 2026.pdfPDF, 1.2 MB
Workspace
Settings for Northbeam Studio. Changes apply to all 24 members.
tidewater.app/

Old links redirect for 30 days.

Every Monday at 9:00.

Dark
MRR
$184,320Up8.4%
Net retention
116%Up3.1 pts
Logo churn
2.1%Down0.8 pts
EnterpriseDraftv2.14.0PaidScheduledPast dueFailedSuggested
InsightDetected 2 hours ago

Compute spend rose 38% after the September 9 deploy

The nightly ledger export now runs on every workspace instead of only active ones. It accounts for $11,900 of the increase.

Compute, 14 days$43,210+38% vs prior
vendor-invoices-q3.csvCSV, 214 KB
Northbeam MSA 2026.pdfPDF, 1.2 MB
Workspace
Settings for Northbeam Studio. Changes apply to all 24 members.
tidewater.app/

Old links redirect for 30 days.

Every Monday at 9:00.

Scales

Twelve steps each. 1 to 2 backgrounds, 3 to 5 component fills, 6 to 8 borders, 9 to 10 solids, 11 to 12 text.

123456789101112
Brand, light
Brand, dark
Gray, light
Gray, dark

Contrast

70 of 70 pass

The generator keeps your hue and only moves lightness: solids until their text reaches 4.5:1, text steps until they read on their own tints.

PairLightDarkMin
foregroundon background15.6816.417:1
foregroundon card16.0015.577:1
muted-foregroundon background5.889.634.5:1
muted-foregroundon card6.009.144.5:1
muted-foregroundon muted5.347.724.5:1
primary-foregroundon primary5.724.624.5:1
primary-foregroundon primary-hover6.975.484.5:1
secondary-foregroundon secondary14.2413.154.5:1
brand-subtle-foregroundon brand-subtle5.498.384.5:1
brand-subtle-foregroundon card6.179.084.5:1
destructive-foregroundon destructive4.974.764.5:1
destructive-subtle-foregroundon card6.598.684.5:1
success-subtle-foregroundon card5.659.554.5:1
warning-subtle-foregroundon card6.188.914.5:1
info-subtle-foregroundon card6.019.194.5:1
destructive-subtle-foregroundon destructive-subtle5.838.124.5:1
success-foregroundon success4.624.624.5:1
success-subtle-foregroundon success-subtle5.088.654.5:1
PairLightDarkMin
warning-foregroundon warning9.069.064.5:1
warning-subtle-foregroundon warning-subtle5.498.234.5:1
info-foregroundon info4.664.664.5:1
info-subtle-foregroundon info-subtle5.368.444.5:1
ai-foregroundon ai-subtle5.869.084.5:1
ai-foregroundon card6.179.084.5:1
sidebar-foregroundon sidebar15.1916.417:1
tertiary-foregroundon background4.664.884.5:1
tertiary-foregroundon card4.754.634.5:1
severity-critical-foregroundon severity-critical4.974.764.5:1
severity-high-foregroundon severity-high4.654.654.5:1
severity-elevated-foregroundon severity-elevated11.3111.314.5:1
severity-routine-foregroundon severity-routine4.654.664.5:1
severity-critical-subtle-foregroundon severity-critical-subtle5.838.124.5:1
severity-high-subtle-foregroundon severity-high-subtle5.738.134.5:1
severity-elevated-subtle-foregroundon severity-elevated-subtle5.378.384.5:1
severity-routine-subtle-foregroundon severity-routine-subtle5.348.404.5:1

Export

Paste after prodular-ui.css and set data-theme="custom" on html or any element. Add class="dark" for dark mode.

/* Prodular UI theme "Custom". Paste after prodular-ui.css,
   then set <html data-theme="custom"> (add class="dark" for dark mode). */
[data-theme="custom"] {
  --background: oklch(0.993 0.0033 263.81);
  --foreground: oklch(0.25 0.009 263.81);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.25 0.009 263.81);
  --popover: oklch(1 0 0);
  --raised: oklch(1 0 0);
  --popover-foreground: oklch(0.25 0.009 263.81);
  --primary: oklch(0.5196 0.1828 263.81);
  --primary-hover: oklch(0.4746 0.1828 263.81);
  --primary-foreground: oklch(1 0 0);
  --secondary: oklch(0.96 0.0102 263.81);
  --secondary-hover: oklch(0.937 0.0115 263.81);
  --secondary-foreground: oklch(0.25 0.009 263.81);
  --muted: oklch(0.96 0.0102 263.81);
  --muted-foreground: oklch(0.5 0.0115 263.81);
  --accent: oklch(0.96 0.0102 263.81);
  --accent-foreground: oklch(0.25 0.009 263.81);
  --border: oklch(0.88 0.0128 263.81);
  --border-strong: oklch(0.772 0.0128 263.81);
  --input: oklch(0.838 0.0128 263.81);
  --ring: oklch(0.772 0.1156 263.81);
  --brand-subtle: oklch(0.96 0.0188 263.81);
  --brand-subtle-foreground: oklch(0.5 0.1572 263.81);
  --sidebar: oklch(0.982 0.0077 263.81);
  --sidebar-foreground: oklch(0.25 0.009 263.81);
  --sidebar-primary: oklch(0.5196 0.1828 263.81);
  --sidebar-primary-foreground: oklch(1 0 0);
  --sidebar-accent: oklch(0.937 0.0115 263.81);
  --sidebar-accent-foreground: oklch(0.25 0.009 263.81);
  --sidebar-border: oklch(0.88 0.0128 263.81);
  --sidebar-ring: oklch(0.772 0.1156 263.81);
  --chart-1: oklch(0.5196 0.1828 263.81);
  --chart-2: oklch(0.83 0.1447 73.82);
  --chart-3: oklch(0.55 0.0974 184);
  --chart-4: oklch(0.58 0.2 12);
  --chart-5: oklch(0.55 0.2 325);
  --ai-1: oklch(0.5196 0.1828 263.81);
  --ai-2: oklch(0.54 0.21 284);
  --ai-3: oklch(0.7 0.13 232);
  --ai-subtle: oklch(0.982 0.0084 263.81);
  --ai-border: oklch(0.88 0.0583 263.81);
  --ai-foreground: oklch(0.5 0.1572 263.81);
  --destructive: oklch(0.57 0.21 25);
  --destructive-foreground: oklch(1 0 0);
  --destructive-subtle: oklch(0.96 0.0197 25);
  --destructive-subtle-foreground: oklch(0.5 0.1806 25);
  --destructive-border: oklch(0.838 0.0891 25);
  --success: oklch(0.545 0.1592 147.64);
  --success-foreground: oklch(1 0 0);
  --success-subtle: oklch(0.96 0.0288 150.91);
  --success-subtle-foreground: oklch(0.5 0.1376 146.55);
  --success-border: oklch(0.838 0.096 148.73);
  --warning: oklch(0.8 0.1551 67.27);
  --warning-foreground: oklch(0.22 0.0498 64);
  --warning-subtle: oklch(0.96 0.0306 73.82);
  --warning-subtle-foreground: oklch(0.5 0.1121 65.09);
  --warning-border: oklch(0.838 0.102 69.45);
  --info: oklch(0.56 0.1585 250);
  --info-foreground: oklch(1 0 0);
  --info-subtle: oklch(0.96 0.0197 250);
  --info-subtle-foreground: oklch(0.5 0.1415 250);
  --info-border: oklch(0.838 0.0837 250);
  --tertiary-foreground: oklch(0.555 0.0128 263.81);
  --severity-critical: oklch(0.57 0.21 25);
  --severity-critical-foreground: oklch(1 0 0);
  --severity-critical-subtle: oklch(0.96 0.0197 25);
  --severity-critical-subtle-foreground: oklch(0.5 0.1806 25);
  --severity-critical-border: oklch(0.838 0.0891 25);
  --severity-high: oklch(0.58 0.1834 39.64);
  --severity-high-foreground: oklch(1 0 0);
  --severity-high-subtle: oklch(0.96 0.021 42.91);
  --severity-high-subtle-foreground: oklch(0.5 0.1617 38.55);
  --severity-high-border: oklch(0.838 0.0932 40.73);
  --severity-elevated: oklch(0.86 0.16 92.18);
  --severity-elevated-foreground: oklch(0.22 0.0449 90);
  --severity-elevated-subtle: oklch(0.96 0.0288 96.55);
  --severity-elevated-subtle-foreground: oklch(0.5 0.1022 90.73);
  --severity-elevated-border: oklch(0.838 0.096 93.64);
  --severity-routine: oklch(0.56 0.0128 263.81);
  --severity-routine-foreground: oklch(1 0 0);
  --severity-routine-subtle: oklch(0.96 0.0102 263.81);
  --severity-routine-subtle-foreground: oklch(0.5 0.0115 263.81);
  --severity-routine-border: oklch(0.838 0.0128 263.81);
  --brand-1: oklch(0.993 0.0032 263.81);
  --brand-2: oklch(0.982 0.0084 263.81);
  --brand-3: oklch(0.96 0.0188 263.81);
  --brand-4: oklch(0.937 0.0299 263.81);
  --brand-5: oklch(0.912 0.0422 263.81);
  --brand-6: oklch(0.88 0.0583 263.81);
  --brand-7: oklch(0.838 0.08 263.81);
  --brand-8: oklch(0.772 0.1156 263.81);
  --brand-9: oklch(0.5196 0.1828 263.81);
  --brand-10: oklch(0.4746 0.1828 263.81);
  --brand-11: oklch(0.5 0.1572 263.81);
  --brand-12: oklch(0.25 0.0768 263.81);
  --gray-1: oklch(0.993 0.0033 263.81);
  --gray-2: oklch(0.982 0.0077 263.81);
  --gray-3: oklch(0.96 0.0102 263.81);
  --gray-4: oklch(0.937 0.0115 263.81);
  --gray-5: oklch(0.912 0.0128 263.81);
  --gray-6: oklch(0.88 0.0128 263.81);
  --gray-7: oklch(0.838 0.0128 263.81);
  --gray-8: oklch(0.772 0.0128 263.81);
  --gray-9: oklch(0.58 0.0128 263.81);
  --gray-10: oklch(0.535 0.0128 263.81);
  --gray-11: oklch(0.5 0.0115 263.81);
  --gray-12: oklch(0.25 0.009 263.81);
  --radius: 0.625rem;
  --font-ui: var(--font-geist), ui-sans-serif, system-ui, sans-serif;
}
.dark[data-theme="custom"],
.dark [data-theme="custom"],
[data-theme="custom"].dark {
  --background: oklch(0.165 0.009 263.81);
  --foreground: oklch(0.945 0.0064 263.81);
  --card: oklch(0.195 0.0102 263.81);
  --card-foreground: oklch(0.945 0.0064 263.81);
  --popover: oklch(0.232 0.0128 263.81);
  --raised: oklch(0.295 0.0128 263.81);
  --popover-foreground: oklch(0.945 0.0064 263.81);
  --primary: oklch(0.5696 0.1828 263.81);
  --primary-hover: oklch(0.5296 0.1828 263.81);
  --primary-foreground: oklch(1 0 0);
  --secondary: oklch(0.262 0.0128 263.81);
  --secondary-hover: oklch(0.295 0.0128 263.81);
  --secondary-foreground: oklch(0.945 0.0064 263.81);
  --muted: oklch(0.262 0.0128 263.81);
  --muted-foreground: oklch(0.78 0.0115 263.81);
  --accent: oklch(0.262 0.0128 263.81);
  --accent-foreground: oklch(0.945 0.0064 263.81);
  --border: oklch(0.335 0.0128 263.81);
  --border-strong: oklch(0.47 0.0128 263.81);
  --input: oklch(0.392 0.0128 263.81);
  --ring: oklch(0.47 0.1389 263.81);
  --brand-subtle: oklch(0.232 0.0548 263.81);
  --brand-subtle-foreground: oklch(0.78 0.1112 263.81);
  --sidebar: oklch(0.165 0.009 263.81);
  --sidebar-foreground: oklch(0.945 0.0064 263.81);
  --sidebar-primary: oklch(0.5696 0.1828 263.81);
  --sidebar-primary-foreground: oklch(1 0 0);
  --sidebar-accent: oklch(0.232 0.0128 263.81);
  --sidebar-accent-foreground: oklch(0.945 0.0064 263.81);
  --sidebar-border: oklch(0.335 0.0128 263.81);
  --sidebar-ring: oklch(0.47 0.1389 263.81);
  --chart-1: oklch(0.5696 0.1828 263.81);
  --chart-2: oklch(0.83 0.1447 73.82);
  --chart-3: oklch(0.55 0.0974 184);
  --chart-4: oklch(0.585 0.2 12);
  --chart-5: oklch(0.585 0.2 325);
  --ai-1: oklch(0.5696 0.1828 263.81);
  --ai-2: oklch(0.58 0.21 284);
  --ai-3: oklch(0.72 0.13 232);
  --ai-subtle: oklch(0.195 0.0256 263.81);
  --ai-border: oklch(0.335 0.1024 263.81);
  --ai-foreground: oklch(0.78 0.1112 263.81);
  --destructive: oklch(0.58 0.21 25);
  --destructive-foreground: oklch(1 0 0);
  --destructive-subtle: oklch(0.232 0.063 25);
  --destructive-subtle-foreground: oklch(0.78 0.1283 25);
  --destructive-border: oklch(0.392 0.1302 25);
  --success: oklch(0.545 0.1592 147.64);
  --success-foreground: oklch(1 0 0);
  --success-subtle: oklch(0.232 0.048 150.91);
  --success-subtle-foreground: oklch(0.78 0.1184 146.55);
  --success-border: oklch(0.392 0.0992 148.73);
  --warning: oklch(0.8 0.1551 67.27);
  --warning-foreground: oklch(0.22 0.0498 64);
  --warning-subtle: oklch(0.232 0.0492 73.82);
  --warning-subtle-foreground: oklch(0.78 0.1258 65.09);
  --warning-border: oklch(0.392 0.0852 69.45);
  --info: oklch(0.56 0.1585 250);
  --info-foreground: oklch(1 0 0);
  --info-subtle: oklch(0.232 0.054 250);
  --info-subtle-foreground: oklch(0.78 0.1163 250);
  --info-border: oklch(0.392 0.1109 250);
  --tertiary-foreground: oklch(0.6 0.0128 263.81);
  --severity-critical: oklch(0.58 0.21 25);
  --severity-critical-foreground: oklch(1 0 0);
  --severity-critical-subtle: oklch(0.232 0.063 25);
  --severity-critical-subtle-foreground: oklch(0.78 0.1283 25);
  --severity-critical-border: oklch(0.392 0.1302 25);
  --severity-high: oklch(0.58 0.1834 39.64);
  --severity-high-foreground: oklch(1 0 0);
  --severity-high-subtle: oklch(0.232 0.057 42.91);
  --severity-high-subtle-foreground: oklch(0.78 0.1324 38.55);
  --severity-high-border: oklch(0.392 0.1178 40.73);
  --severity-elevated: oklch(0.86 0.16 92.18);
  --severity-elevated-foreground: oklch(0.22 0.0449 90);
  --severity-elevated-subtle: oklch(0.232 0.0478 96.55);
  --severity-elevated-subtle-foreground: oklch(0.78 0.1184 90.73);
  --severity-elevated-border: oklch(0.392 0.0803 93.64);
  --severity-routine: oklch(0.615 0.0128 263.81);
  --severity-routine-foreground: oklch(0.22 0.0045 263.81);
  --severity-routine-subtle: oklch(0.232 0.0128 263.81);
  --severity-routine-subtle-foreground: oklch(0.78 0.0115 263.81);
  --severity-routine-border: oklch(0.392 0.0128 263.81);
  --brand-1: oklch(0.165 0.0183 263.81);
  --brand-2: oklch(0.195 0.0256 263.81);
  --brand-3: oklch(0.232 0.0548 263.81);
  --brand-4: oklch(0.262 0.0768 263.81);
  --brand-5: oklch(0.295 0.0914 263.81);
  --brand-6: oklch(0.335 0.1024 263.81);
  --brand-7: oklch(0.392 0.1133 263.81);
  --brand-8: oklch(0.47 0.1389 263.81);
  --brand-9: oklch(0.5696 0.1828 263.81);
  --brand-10: oklch(0.5296 0.1828 263.81);
  --brand-11: oklch(0.78 0.1112 263.81);
  --brand-12: oklch(0.945 0.026 263.81);
  --gray-1: oklch(0.165 0.009 263.81);
  --gray-2: oklch(0.195 0.0102 263.81);
  --gray-3: oklch(0.232 0.0128 263.81);
  --gray-4: oklch(0.262 0.0128 263.81);
  --gray-5: oklch(0.295 0.0128 263.81);
  --gray-6: oklch(0.335 0.0128 263.81);
  --gray-7: oklch(0.392 0.0128 263.81);
  --gray-8: oklch(0.47 0.0128 263.81);
  --gray-9: oklch(0.6 0.0128 263.81);
  --gray-10: oklch(0.645 0.0128 263.81);
  --gray-11: oklch(0.78 0.0115 263.81);
  --gray-12: oklch(0.945 0.0064 263.81);
}
.dark[data-theme="custom"], .dark [data-theme="custom"], [data-theme="custom"].dark { color-scheme: dark; }