Palette
A palette is a single color expanded into a full 50-950 shade ramp. Every palette comes from a theme: a theme assigns a color to each role, and Natcore builds the ramp, mirrors it for dark mode, and exposes adaptive tokens. You can also define your own palettes.
Palettes come from themes
A theme gives each role — primary, surface, and the rest — a single color or explicit anchors.
A lone color becomes the 500 anchor; 50 and 950 derive from it. Any shade can be anchored explicitly.
Unanchored shades interpolate between the bracketing anchors, and a readable on-color is generated for each.
Dark-mode tokens point to the opposite side of the light ramp, so contrast flips without new markup.
The palette surfaces adaptive tokens like color-primary-500 that utilities and components read.
Light and dark
--color-light-primary-{shade}--color-dark-primary-{shade} — the same ramp, reversed/* Dark is the light ramp, mirrored end-to-end */
--color-dark-primary-50: var(--color-light-primary-950);
--color-dark-primary-100: var(--color-light-primary-900);
/* …200↔800, 300↔700, 400↔600, 500 stays… */
--color-dark-primary-950: var(--color-light-primary-50);
/* The adaptive token picks the active scheme's side */
--color-primary-500: light-dark(
var(--color-light-primary-500),
var(--color-dark-primary-500)
);Built-in palettes
Theme role palettes
bg-primary-500 · palette-primarybg-secondary-500 · palette-secondarybg-accent-500 · palette-accentbg-surface-500 · palette-surfacebg-success-500 · palette-successbg-danger-500 · palette-dangerTheming documents the intent of primary, secondary, accent, surface, success, danger, and disabled.
Tailwind color palettes
Every built-in Tailwind color is available as a palette class.
slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose
palette-{color}— for examplepalette-emeraldUsing a palette
Named color utilities
<!-- Name the palette inline with any color utility -->
<button class="bg-primary-500 text-on-primary-500">
Save
</button>
<span class="text-secondary-700">Secondary label</span>
<div class="border border-accent-500">Accent border</div>
The palette class & tone utilities
<!-- A design-system role as the current palette -->
<div class="palette-primary">
<button class="bg-500 text-on-500">Solid</button>
<div class="bg-100 text-on-100">Soft fill</div>
<p class="text-700">Body copy in the active palette</p>
</div>
<!-- Same markup, a built-in Tailwind color -->
<div class="palette-emerald">...</div>
Component modifiers
<!-- A /{palette} modifier accepts a role… -->
<button class="btn-solid/primary">Primary</button>
<button class="btn-soft/accent">Accent</button>
<!-- …or a built-in Tailwind color -->
<button class="btn-solid/emerald">Emerald</button>
<button class="btn-soft/sky">Sky</button>
Custom palettes
1. Install the transform
Add it to postcss.config.js ahead of @tailwindcss/postcss.
// postcss.config.js — the Natcore transform runs BEFORE Tailwind
module.exports = {
plugins: {
"@nataliebasille/natcore-design-system/postcss": {},
"@tailwindcss/postcss": {},
autoprefixer: {},
},
};
Add it to vite.config ahead of @tailwindcss/vite.
// vite.config.ts — use the Vite variant instead
import natcorePalettes from "@nataliebasille/natcore-design-system/vite";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
// natcorePalettes() must come before tailwindcss()
plugins: [natcorePalettes(), tailwindcss()],
});
2. Declare palettes
@import "tailwindcss";
@import "@nataliebasille/natcore-design-system";
@natcore-palettes {
/* A single color becomes the 500 anchor; 50/950 and the
rest of the ramp are derived — the same anchor model a
theme uses for its roles. */
brand: #f04e23;
/* Or pin explicit anchors / nudge any shade 50-950. */
info-50: #eaf6ff;
info-500: #0099ff;
info-950: #001a33;
}brand: #f04e23 becomes the 500 anchor; Natcore derives 50/950 and interpolates the rest.
name-50, name-500, name-950 pin the outer anchors; name-300 (etc.) nudges one step.
Provide all eleven shades to reproduce an exact palette — like the pumpkin example below.
3. Example: pumpkin
@natcore-palettes {
pumpkin-50: #fff5e9;
pumpkin-100: #ffe3c4;
pumpkin-200: #f9c489;
pumpkin-300: #f2a04a;
pumpkin-400: #eb7d1e;
pumpkin-500: #dd5f10;
pumpkin-600: #c24a0e;
pumpkin-700: #a23a12;
pumpkin-800: #7f2f14;
pumpkin-900: #6a2810;
pumpkin-950: #5a230a;
}--color-pumpkin-50 through --color-pumpkin-950, each paired with a readable --color-on-pumpkin-{shade} text color.
4. Use it like any palette
<!-- Everything a built-in palette can do, your palette can too -->
<div class="bg-pumpkin-500 text-on-pumpkin-500">
Pumpkin
</div>
<button class="btn-solid/pumpkin">Carve</button>
<section class="palette-pumpkin">
<span class="bg-500 text-on-500">
Current tone = pumpkin
</span>
</section>
Component modifiers accept the custom palette name just like a built-in role.
Tree-shaking
Using bg-pumpkin-500 alone emits only that shade's tokens — not the whole ramp.
palette-pumpkin pulls the shades it needs; a registered-but-unused palette emits nothing.
You never hand-write shade variables — declare the anchors and the ramp, dark mode, and contrast are generated.