coloris.js generates primative CSS variables from your accent and background colors based on Radix Colors .
:root {
/* Accent Scale */
--accent-1: oklch(99.4% 0.0011 106.4);
--accent-2: oklch(98.2% 0.0015 106.4);
--accent-3: oklch(95.6% 0.0023 97.61);
--accent-4: oklch(93.2% 0.0034 100.6);
--accent-5: oklch(91% 0.0038 95.91);
--accent-6: oklch(88.6% 0.005 98.34);
--accent-7: oklch(85.3% 0.0062 96.6);
--accent-8: oklch(79.2% 0.0083 96.5);
--accent-9: oklch(0% 0 none);
--accent-10: oklch(30% 0.0079 96.31);
--accent-11: oklch(49.8% 0.0079 106.7);
--accent-12: oklch(24.3% 0.0079 96.31);
/* Neutral Scale */
--neutral-1: oklch(99.4% 0.0011 106.4);
--neutral-2: oklch(98.2% 0.0015 106.4);
--neutral-3: oklch(95.6% 0.0023 97.61);
--neutral-4: oklch(93.2% 0.0034 100.6);
--neutral-5: oklch(91% 0.0038 95.91);
--neutral-6: oklch(88.6% 0.005 98.34);
--neutral-7: oklch(85.3% 0.0062 96.6);
--neutral-8: oklch(79.2% 0.0083 96.5);
--neutral-9: oklch(64.1% 0.0103 106.7);
--neutral-10: oklch(60.6% 0.0096 106.7);
--neutral-11: oklch(49.8% 0.0079 106.7);
--neutral-12: oklch(24.3% 0.0079 96.31);
/* Accent Scale Alpha */
--accent-a1: color(display-p3 0.349 0.349 0.0235 / 0.012);
--accent-a2: color(display-p3 0.1608 0.1608 0.0235 / 0.028);
--accent-a3: color(display-p3 0.1294 0.0667 0.0078 / 0.063);
--accent-a4: color(display-p3 0.1294 0.1294 0.0118 / 0.099);
--accent-a5: color(display-p3 0.1255 0.098 0.0078 / 0.13);
--accent-a6: color(display-p3 0.102 0.0745 0.0039 / 0.161);
--accent-a7: color(display-p3 0.098 0.098 0.0039 / 0.208);
--accent-a8: color(display-p3 0.0863 0.0745 0.0039 / 0.287);
--accent-a9: color(display-p3 0 0 0);
--accent-a10: color(display-p3 0.0275 0.0235 0 / 0.84);
--accent-a11: color(display-p3 0.0314 0.0314 0 / 0.632);
--accent-a12: color(display-p3 0.0235 0.0196 0 / 0.891);
/* Neutral Scale Alpha */
--neutral-a1: color(display-p3 0.349 0.349 0.0235 / 0.012);
--neutral-a2: color(display-p3 0.1608 0.1608 0.0235 / 0.028);
--neutral-a3: color(display-p3 0.1294 0.0667 0.0078 / 0.063);
--neutral-a4: color(display-p3 0.1294 0.1294 0.0118 / 0.099);
--neutral-a5: color(display-p3 0.1255 0.098 0.0078 / 0.13);
--neutral-a6: color(display-p3 0.102 0.0745 0.0039 / 0.161);
--neutral-a7: color(display-p3 0.098 0.098 0.0039 / 0.208);
--neutral-a8: color(display-p3 0.0863 0.0745 0.0039 / 0.287);
--neutral-a9: color(display-p3 0.051 0.051 0.0039 / 0.471);
--neutral-a10: color(display-p3 0.0392 0.0392 0 / 0.51);
--neutral-a11: color(display-p3 0.0314 0.0314 0 / 0.632);
--neutral-a12: color(display-p3 0.0235 0.0196 0 / 0.891);
/* Surfaces */
--neutral-surface: color(display-p3 1 1 1 / 80%);
--accent-surface: color(display-p3 0.9725 0.9725 0.9647 / 0.8);
/* Fallback for browsers that don't support P3 */
@supports not (color: color(display-p3 1 1 1)) {
/* Accent Scale */
--accent-1: #fdfdfc;
--accent-2: #f9f9f8;
--accent-3: #f1f0ef;
--accent-4: #e9e9e6;
--accent-5: #e2e1de;
--accent-6: #dad9d6;
--accent-7: #cfcfca;
--accent-8: #bcbbb5;
--accent-9: #000;
--accent-10: #2f2e29;
--accent-11: #63635e;
--accent-12: #21201c;
/* Neutral Scale */
--neutral-1: #fdfdfc;
--neutral-2: #f9f9f8;
--neutral-3: #f1f0ef;
--neutral-4: #e9e9e6;
--neutral-5: #e2e1de;
--neutral-6: #dad9d6;
--neutral-7: #cfcfca;
--neutral-8: #bcbbb5;
--neutral-9: #8d8d86;
--neutral-10: #82827c;
--neutral-11: #63635e;
--neutral-12: #21201c;
/* Accent Scale Alpha */
--accent-a1: #55550003;
--accent-a2: #25250007;
--accent-a3: #20100010;
--accent-a4: #1f1f0019;
--accent-a5: #1f180021;
--accent-a6: #19130029;
--accent-a7: #19190035;
--accent-a8: #1915014a;
--accent-a9: #000000;
--accent-a10: #070600d6;
--accent-a11: #080800a1;
--accent-a12: #060500e3;
/* Neutral Scale Alpha */
--neutral-a1: #55550003;
--neutral-a2: #25250007;
--neutral-a3: #20100010;
--neutral-a4: #1f1f0019;
--neutral-a5: #1f180021;
--neutral-a6: #19130029;
--neutral-a7: #19190035;
--neutral-a8: #1915014a;
--neutral-a9: #0f0f0079;
--neutral-a10: #0c0c0083;
--neutral-a11: #080800a1;
--neutral-a12: #060500e3;
/* Surfaces */
--neutral-surface: #ffffffcc;
--accent-surface: #f8f8f6cc;
}
/* Common properties */
--background: #fff;
--accent-contrast: #fff;
}
Using these variables, you can create your own sematic color system. For example, for shadcn/ui:
@layer theme {
:root {
--bg: var(--background);
--fg: var(--neutral-12);
--muted: var(--neutral-surface);
--muted-foreground: var(--neutral-11);
--popover: var(--background);
--popover-foreground: var(--neutral-12);
--border: var(--neutral-7);
--input: var(--neutral-7);
--primary: var(--accent-9);
--primary-foreground: var(--accent-contrast);
--secondary: var(--neutral-3);
--secondary-foreground: var(--neutral-12);
--outline: var(--neutral-12);
--ring: var(--neutral-12);
}
}
Use it as an one-off or programmatic color palette generator.
One-off projects
Use this web app to generate primative CSS variables for your accent and background colors. Copy the code and remove the variables you don't need.
Multi-theme platform with global design system
Install the coloris-js package.
npm install coloris-js
Generate CSS variables.
import { coloris } from "coloris-js";
const colors = coloris({
appearance: "light",
accent: "#BCFB46",
background: "#ffffff",
neutral: "sand",
});
Use in React server components.
import { coloris } from "coloris-js";
import { getTheme } from "@/lib/fetchers";
export default async function Layout({
children,
params,
}: {
children: React.ReactNode;
params: { id: string };
}) {
const { id } = params;
const theme = await getTheme(id);
const { accent, background, neutral } = theme;
const colors = coloris({
appearance: "light",
accent,
background,
neutral,
});
return (
<>
<style>{colors}</style>
{children}
</>
);
}