The rules of the road
The Raihin design system is one canonical theme. Most decisions about names, structure, and pairings have already been made and locked. New work composes inside this vocabulary rather than inventing alongside it.
The three tiers
Every token carries one of three tier labels. The label says how much room there is to change the value, and how much else has to move when you do.
System identity. Treat as fixed.
- z-index scale (cross-surface layering)
- --ring-width = 3px
- Status hue conventions (red = destructive, green = success, …)
- State-layer opacities (4/6/8/12/16%)
- --size-* ratios
- Opacity scale
Defines how Raihin looks and feels.
- Surfaces (--background, --card, --popover)
- Foreground hierarchies
- --primary, --secondary, --accent
- Status lightness/chroma
- Fonts
- Semantic radii (--radius-button, --radius-card, …)
- Chart palette
Adjust as the product evolves.
- Shadows
- Motion durations & easings
- --overlay opacity
- Per-control surfaces (switch, slider, scrollbar, skeleton)
- --placeholder-color
- Link / code / kbd treatments
- Blur scale
Structural locks
These structural shapes are part of the system identity. Even tokens marked “tunable” are tunable withinthese shapes — never to a different shape.
- 3-tier foreground.
--foreground→--foreground-subtle→--foreground-disabled. Don't collapse to two tiers. - 4-tier border.
--border/--border-subtle/--border-strong/--border-input. - Status triple. Every status name comes with a
-foregroundand a-borderpeer. They travel together. - Status hue conventions. red = destructive, green = success, yellow/orange = warning, blue = info. Hue is locked; lightness and chroma are tunable.
- Five-stop state-layer opacities. 4 / 6 / 8 / 12 / 16 percent. These map to
--state-expanded,--state-hover,--state-focus,--state-active,--state-selected. - --size-* scale ratios. Icon, avatar, and control sizes. Locked.
Spacing & grid
Spacing and grid are deliberately nottokenized. The system inherits Tailwind v4's default ratio scale (--spacing: 0.25rem) and Tailwind's default breakpoints. Tokenizing them would duplicate the scale without adding intent, so it stays as a system-wide constant.
Three places hold structural spacing tokens — reach for these before raw Tailwind utilities:
- Density for component padding and field gap:
p-(--density-card-padding),h-(--density-control-h),gap-(--density-field-gap). These auto-tighten underdata-density="compact". - Layout for page widths:
max-w-(--container-content),max-w-(--container-prose),max-w-(--container-dashboard),max-w-(--container-wide). Avoid ad-hocmax-w-3xl. - Sizing for control, icon, and avatar dimensions:
--size-control-md,--size-icon-sm, etc. Locked ratios.
Rules of thumb for everything else:
- Prefer Tailwind's spacing scale in even steps:
2,3,4,6,8,10,12. Avoid1.5,2.5, and5unless the layout truly requires them. - Reach for
gap-*on a flex or grid parent beforem-*on children. Margins collapse and fight stacking; gap composes. - Page gutters use
px-4 md:px-6 lg:px-10conventionally — match the site shell padding for consistency. - Grid columns use Tailwind's defaults (
grid-cols-1 md:grid-cols-2 lg:grid-cols-3). No tokenized grid system; pick column counts that match the content's natural breakpoints. - Touch targets stay ≥
--size-touch-target(44px). On mobile, never let an interactive element fall below that.
Forbidden patterns
The system is unforgiving about these. They're bugs, not opinions.
<div className="bg-black/30 rounded-3xl z-50">…</div><div className="bg-overlay rounded-card z-overlay">…</div>Inline color/radius/z-index values. Use the token vocabulary.
<div className="bg-destructive/10 text-destructive">Error</div><div className="bg-destructive text-destructive-foreground border border-destructive-border">Error</div>Status uses the solid pattern. /10 on a pastel base loses contrast.
<div className="bg-card text-foreground/70">…</div><div className="bg-card text-card-foreground">…</div>Semantic foreground pairs. Don't split bg-{role} from text-{role}-foreground.
<input className="text-sm" /><input className="text-base md:text-sm" />iOS Safari focus-zoom rule. Text inputs must be at least 16px on mobile.
<div><Card className="mb-4" /><Card className="mb-4" /></div><div className="flex flex-col gap-4"><Card /><Card /></div>Margins collapse and fight stacking. Use gap-* on a flex/grid parent for sibling spacing.
<div className="gap-3 p-5">…</div><div className="gap-2 p-4">…</div>Prefer even Tailwind spacing steps (2, 4, 6, 8, 10, 12). Odd steps usually mean fighting the layout — pick the nearest even step or a density token.
The semantic-pairs rule
For every surface there is exactly one foreground. Always render them together: bg-card text-card-foreground, bg-popover text-popover-foreground, bg-primary text-primary-foreground. If you find yourself reaching for text-foreground/60, you're fighting the system.
The focus ring
Pinned at --ring-width: 3px across the entire system. The focus indicator should look identical on every surface in the product. treat as fixed.