Zazz Design Framework
Foundations

Radius

A short semantic scale, primitive-specific aliases for nesting, and a global multiplier.

Most radius systems are a scale. Zazz adds a primitive layer so nested corners look right.

Radius works in three layers: a short semantic scale (none, xs through xl, full), primitive-specific aliases (button, input, badge, card) that point to specific scale steps, and a global multiplier that scales everything at once.

The semantic layer covers most direct decisions. The primitive layer solves the nesting problem (buttons inside cards, badges on buttons) without designers having to do the math each time.

Semantic radii

The base scale. Five named radii plus none and full.

--radius-none: 0rem;
--radius-xs: calc(var(--step-1) * var(--radius-multiplier)); /* 0.25rem  (4px)  */
--radius-sm: calc(var(--step-1_5) * var(--radius-multiplier)); /* 0.375rem (6px)  */
--radius-md: calc(var(--step-2) * var(--radius-multiplier)); /* 0.5rem   (8px)  */
--radius-lg: calc(var(--step-4) * var(--radius-multiplier)); /* 1rem     (16px) */
--radius-xl: calc(var(--step-7) * var(--radius-multiplier)); /* 1.75rem  (28px) */
--radius-full: 9999rem;

Each numbered radius derives from a step on the spacing scale, multiplied by --radius-multiplier. The scale is intentionally short. Five rungs cover most surfaces, and --radius-full handles capsules and pills.

--radius-none exists so a component can switch off its rounding without resorting to a literal 0. Tokens stay tokens; nothing breaks the convention.

Primitive radii

Frequently-used primitives have dedicated radius tokens that point to a semantic step.

--radius-button: var(--radius-md); /* 0.5rem  (8px)  */
--radius-input: var(--radius-md); /* 0.5rem  (8px)  */
--radius-badge: var(--radius-full); /* capsule        */
--radius-card: var(--radius-lg); /* 1rem    (16px) */

The reason: nested radii look broken when the inner element's radius is too close to the container's. A button inside a card with the same radius reads as not-quite-aligned to the eye. By assigning button and card different semantic steps (--radius-md and --radius-lg), the math comes out right by default.

Primitive tokens also let you retune one primitive group without disturbing the rest. Make every button a capsule:

:root {
  --radius-button: var(--radius-full);
}

Card corners, input rounding, and badge shapes stay where they are.

Global multiplier

--radius-multiplier is the lever for sharpening or rounding every radius at once.

--radius-multiplier: 1;

Default is 1. Bump it for rounder corners across the product; drop it for a sharper, more architectural feel. Components don't care. They consume tokens that resolve through the multiplier.

.architectural {
  --radius-multiplier: 0; /* sharp corners everywhere */
}

.friendly {
  --radius-multiplier: 1.5; /* rounder, softer */
}

--radius-md at multiplier 1.5 becomes step-2 × 1.5 = 0.75rem. The proportions between xs, sm, md, lg, and xl stay constant regardless of multiplier. They all scale together.

--radius-none and --radius-full are unaffected by the multiplier. none is always 0. full is always capsule.

Nested radius math

The classic problem: a button (8px radius) sits inside a card (16px radius). If the button's radius is too close to the card's, the visual rhythm breaks. Zazz's primitive defaults ship with the math worked out:

PrimitiveTokenDefaultResolves to
Card--radius-card--radius-lg16px
Button--radius-button--radius-md8px
Input--radius-input--radius-md8px
Badge--radius-badge--radius-fullcapsule

A card at 16px with a button inside at 8px reads cleanly. The 2:1 ratio gives the eye enough difference. A badge on a button stays capsule regardless of context, dodging the nesting math entirely.

The defaults compose well. When you retune primitives for a project, check the most-common nesting paths in your UI and verify the inner radius stays visibly smaller than the outer.

Where to next

On this page