Photon Design System

Scale

The numeric scales of the system — space, corner radius, and elevation. Color and type have their own foundation pages; this one is everything measured in rem. Click a token to copy its utility class.

Radius

Closed set in theme.css: sm / base /lg, plus Tailwind's full for pills.xs, md, xl, 2xl, and3xl are gone. Every corner in the system — controls, cards, overlays, nested surfaces — must use this set.

  • Use only rounded-sm / rounded-base /rounded-lg / rounded-full for theouter edge. None of the removed stops.
  • Draw rings on the outer wrapper; inset the control (Input usesp-0.5 / 2px) so fill and autofill sit inside the hairline. Documented paddings already include that inset — seeInput.

Squircle

The squircle utility (corner-shape: superellipse(1.1)) softens a rounded-* edge into a superellipse. Use it only forinteractive chrome — hover fills, press surfaces, focus rings, and other feedback layers — never for resting media or static cards.

  • Yes: Button hover / effect sheen, Input focus ring chrome, CloseButton hover chip, Toggle press surface.
  • No: Avatar photos (true rounded-full circle), menus, dialogs, toasts, and other resting bordered panels.
  • Pair with a radius token — squircle alone does not set size.

Concentric correction

Whenever a ring, border, or nested surface sits inside a rounded outer edge, the inner curve must stay parallel. The universal rule — the gap is thetotal edge-to-edge distance (border / ring width + padding):

innerRadius = max(0px, outerRadius − gap)

  • Always derive it from the token with calc — e.g.rounded-[calc(var(--radius-base)-2px)] for a 2px inset insidebase — never a hardcoded resolved px. The correction then tracks the token if the scale ever changes, and the class itself documents the derivation. Concentric corrections are the only allowed arbitrary radii; do not invent unrelated stops.
  • Clamp with max(0px, …) whenever the gap can meet or exceed the radius — a negative border-radius silently invalidates the whole declaration. The 0px unit is required:max(0, …) is invalid inside CSS math and gets dropped too. For statically known-positive cases (all current component insets) the bare calc is fine.
  • Unequal horizontal / vertical gaps → correct each axis with the slash form: border-radius: calc(R − gapX) / calc(R − gapY).
  • Keep any radius ≤ half the element's shortest side — past that the browser rescales all four corners and the derived value silently drifts.full pills are the exception to subtraction: nested surfaces inside a pill stay full.
  • Plain round corners: browsers already offset inset box-shadows concentrically. Prefer a real nested element (or a ::beforefill) at outer − gap so backgrounds never paint over the hairline.
  • When a ring does use squircle, enginesscale the inset shadow instead of offsetting it (csswg #12961), so the ring thickens at the corners. Always cover the inner curve with a ::before at the concentric radius (Input lg:calc(var(--radius-base)-1px) rest /calc(var(--radius-base)-2px) focused). Never putsquircle + border / ring-inseton the same box without that cover — the cards below stay plain round.

Spacing

Photon does not ship a custom spacing scale — it uses Tailwind's default (--spacing = 0.25rem / 4px). Padding, margin, and gap utilities (p-4, gap-2, …) all multiply that unit. Prefer even steps on the 4px grid; the bars below are the sizes components reach for most often.

Elevation

Shadows are for floating layers only — menus, popovers, dialogs, drawers. Cards and buttons stay flat; separation is a hairline border (ring-pho-secondary / border-pho-secondary). The stops in use today are shadow-pho-lg andshadow-pho-xl.