Zazz Design Framework
Foundations

Grayscale

Neutrals, plus shade and tint scales derived from them.

Grayscale is the foundation everything dimmed or faded points to.

Three sub-scales: neutral (raw grayscale, 50 to 950 plus white and black), shade (darkened overlays derived from --neutral-950), and tint (faded overlays derived from --white). Theme tokens like --background, --border, --muted, and --faded all resolve into one of these three.

The point of separating raw neutrals from overlays: neutrals are colors, overlays are opacities. A border at --neutral-200 paints a specific gray; --shade-100 paints whatever sits below at 10% opacity. The two solve different problems and shouldn't be confused.

Neutral

The raw grayscale. Eleven OKLCH steps plus pure white and black.

--white: white;
--neutral-50: oklch(0.9911 0 0);
--neutral-100: oklch(0.9581 0 0);
--neutral-200: oklch(0.871 0.004 286.58);
--neutral-300: oklch(0.794 0.007 286.38);
--neutral-400: oklch(0.708 0.009 286.28);
--neutral-500: oklch(0.629 0.012 286.12);
--neutral-600: oklch(0.535 0.015 285.91);
--neutral-700: oklch(0.442 0.015 285.82);
--neutral-800: oklch(0.336 0.014 285.66);
--neutral-900: oklch(0.241 0.009 285.7);
--neutral-950: oklch(0.198 0.008 285.68);
--black: black;

From --neutral-200 through --neutral-950, the steps carry a faint blue-violet tilt (hue ~286°, chroma 0.004 to 0.015). The two lightest steps (50 and 100) are pure neutral gray at chroma 0. Pure gray feels clinical at scale; a touch of blue in the mid-range and down gives backgrounds, text, and borders warmth without color-casting the rest of the palette.

The endpoints (--white and --black) are kept as keyword colors rather than OKLCH. They're the references shade and tint derive from, so they need to be the cleanest possible anchors.

Use neutral steps directly when you need a specific, opaque gray. Typically borders, dividers, low-emphasis text, or surfaces where transparency would interfere with what sits below.

Shade

Darkened overlays derived from --neutral-950. The opacity scale ranges from 0% to 100%.

--shade-none: oklch(from var(--neutral-950) l c h / 0);
--shade-50: oklch(from var(--neutral-950) l c h / 0.05);
--shade-100: oklch(from var(--neutral-950) l c h / 0.1);
--shade-200: oklch(from var(--neutral-950) l c h / 0.2);
--shade-300: oklch(from var(--neutral-950) l c h / 0.3);
--shade-400: oklch(from var(--neutral-950) l c h / 0.4);
--shade-500: oklch(from var(--neutral-950) l c h / 0.5);
--shade-600: oklch(from var(--neutral-950) l c h / 0.6);
--shade-700: oklch(from var(--neutral-950) l c h / 0.7);
--shade-800: oklch(from var(--neutral-950) l c h / 0.8);
--shade-900: oklch(from var(--neutral-950) l c h / 0.9);
--shade-950: oklch(from var(--neutral-950) l c h / 0.95);
--shade-full: oklch(from var(--neutral-950) l c h / 1);

Shade is what you reach for when you need to dim something: a hover state on a light card, a modal backdrop, a muted overlay over imagery. Whatever sits below shows through; the surface above gets darker.

Because shade derives from --neutral-950 via oklch(from ...), swapping the neutral palette (a rebrand, a warmer or cooler theme) updates every shade overlay automatically. The overlays carry the neutral palette's hue, so warming the neutrals warms the shadows.

Tint

Faded overlays derived from --white. The opacity scale mirrors shade.

--tint-none: oklch(from var(--white) l c h / 0);
--tint-50: oklch(from var(--white) l c h / 0.05);
--tint-100: oklch(from var(--white) l c h / 0.1);
--tint-200: oklch(from var(--white) l c h / 0.2);
--tint-300: oklch(from var(--white) l c h / 0.3);
--tint-400: oklch(from var(--white) l c h / 0.4);
--tint-500: oklch(from var(--white) l c h / 0.5);
--tint-600: oklch(from var(--white) l c h / 0.6);
--tint-700: oklch(from var(--white) l c h / 0.7);
--tint-800: oklch(from var(--white) l c h / 0.8);
--tint-900: oklch(from var(--white) l c h / 0.9);
--tint-950: oklch(from var(--white) l c h / 0.95);
--tint-full: oklch(from var(--white) l c h / 1);

Tint is shade's mirror. It fades what sits below: a hover state on a dark surface, a frosted-glass effect, a soft highlight band. The surface above gets lighter.

Shade vs. tint

The mental model: shade dims, tint fades.

  • Shade: surface gets darker. Use over light backgrounds.
  • Tint: surface gets lighter. Use over dark backgrounds.

This is why theme overlays swap their primitive between modes. --muted (always "darker than the surface") uses --shade-50 in light mode and --tint-50 in dark. --faded (always "lighter than the surface") does the opposite. The semantic stays constant; the underlying primitive flips with the mode.

Use shade and tint directly when an overlay needs to read as transparent: a sticky header over a hero image, a modal backdrop, a hover that lightens or darkens without committing to a specific gray.

Where to next

On this page