Two numbers, one geometry.
The lattice is not a grid overlay you align to. It is the generating function for every dimension in the system. This page is the derivation, followed by an honest account of the four places it deliberately does not apply.
The meeting condition
Why the loop radius is exactly half the pitch, and why that is not a stylistic choice.
Dots sit on a square lattice at pitch P. The kolam line must curve around each dot without touching it, at some radius r. Consider two adjacent dots and the loops that pass between them.
If r < P/2, the two loops fail to reach each other and the line has a gap — it breaks, which the form forbids. If r > P/2, the loops overlap and the line crosses itself where it should not. Only at r = P/2 do the two arcs meet exactly once, at the midpoint of the lattice edge between the dots — and there their tangents are both perpendicular to that edge, so they join smoothly.
That is the whole geometry. Because the arcs meet at edge midpoints with matching tangents, any tile can sit next to any other tile and the line stays continuous. The pattern engine is only possible because of it, and so is the token model: fixing r in terms of P fixes the radius scale to the spacing scale forever.
A 5×5 kolam with no crossings — every curve is a quarter arc of radius P/2 centred on a dot.
--valai-2 is not "8px because that looked right". It is one loop radius — the same curve the generator draws around a dot. Increase the pitch and the corner grows in lockstep with the padding, because they are the same number scaled.Spacing
Orthogonal lattice steps only. A half-step would land the line on a dot, which a kolam line may never do — so the scale has no half-steps either.
| Token | Derivation | Value | Scale |
|---|
Three spacing roles
Primitives are magnitudes; roles say what a magnitude is for. Product code uses roles, never primitives — that is what lets the primitive change without a rewrite.
inset · inside a box
- inset-xs1 P
- inset-sm2 P
- inset-md3 P
- inset-lg4 P
- inset-xl6 P
Card padding, panel padding, button padding.
stack · between rows
- stack-tight1 P
- stack2 P
- stack-loose4 P
- section12 P
Vertical rhythm. Label to input is tight; section to section is 12 P.
inline · between siblings
- inline2 P
- gutter3 P
- page-margin4 P
Button groups, grid gutters, page edges.
1 P, then field-to-field is at least 3 P. Adjacent steps are not enough separation to read as a grouping.Sizing
Radii, stroke weights, and control heights.
valai · radius
| Token | Derivation | Value | Corner |
|---|
kambi · stroke
| Token | Use | Value | Weight |
|---|
Three weights is the maximum a drawn system can justify. Rice flour falls at one thickness; the variation here is hairline, edge, and the drawn line itself.
alavu · control heights
Derived from the lattice, then floored at 44px. The floor is the system's one legitimate override and is documented as such — see where derivation breaks.
| Token | Derivation | At P=8 | At P=6 | Used by |
|---|---|---|---|---|
| --alavu-xs | 3 P | 24px | 18px | Badges, tags — non-interactive only |
| --alavu-sm | 4 P | 32px | 24px | Dense toolbars, table row actions |
| --alavu-md | max(5.5 P, 44px) | 44px | 44px floored | Default control height |
| --alavu-lg | max(7 P, 48px) | 56px | 48px floored | Primary CTA, mobile-first forms |
--alavu-sm control on a touch surface must be padded out to 44px of interactive area or it fails WCAG 2.5.8. The visual size and the target size are different measurements and the system does not conflate them.Layout grid
Twelve columns, lattice gutters, mirrored margins — the default symmetry from principle 03 expressed as page structure.
- Columns12
- Gutter3 P
- Page margin (≥ lg)4 P
- Page margin (< md)2 P
- Max content width176 P
- Prose measure72 ch
- Sidebar width30 P
Why twelve. Twelve divides by 2, 3, 4 and 6, which covers every column split a content page needs without introducing a second grid for exceptions. Nothing kolam-derived about it — and the system says so rather than inventing a justification.
Why 72ch. Measure is governed by reading, not by the lattice. Line length is a typographic constraint and belongs to the type layer.
Breakpoints
Breakpoints are viewport facts, not lattice facts — they do not scale with pitch. They were chosen as lattice multiples at P=8 so the numbers stay familiar, and then fixed.
| Name | Width | As lattice (P=8) | Layout change |
|---|---|---|---|
| sm | 320px | 40 P | Single column, 2 P margins, stacked controls |
| md | 480px | 60 P | Two-column forms, inline button groups |
| lg | 768px | 96 P | Sidebar appears, 4 P margins, 12-col grid active |
| xl | 1024px | 128 P | Persistent navigation, three-column content |
| 2xl | 1280px | 160 P | Max width engages, margins become auto |
Density tiers
Kolams are drawn on 3, 5, 7, 9 dot grids. The same recursion gives the density tiers — one attribute, whole interface re-derived.
compact · P = 6
Dense tables, admin consoles, monitoring dashboards. Controls hit the 44px floor, so the visual rhythm tightens while target sizes hold.
default · P = 8
The reference tier. Every published token value on this site is quoted at P = 8 unless stated otherwise.
comfortable · P = 10
Touch-first surfaces, onboarding, marketing pages, accessibility-forward configurations.
<!-- one attribute; nothing else in the app changes --> <html data-density="compact"> /* tokens.css */ :root[data-density="compact"] { --p: 6px; } :root[data-density="default"] { --p: 8px; } :root[data-density="comfortable"] { --p: 10px; }
Elevation
A kolam is flour lying flat on a floor. It casts no shadow. Depth here is expressed by lifting the ground, not by casting light.
Most systems have five or six elevation levels and use them to signal importance, which quietly turns shadow into a second, unowned hierarchy. Pulli has three, and only the third is a shadow of any consequence.
Surface stepping does the work. Depth is primarily communicated by moving between --color-surface-sunken, --color-bg and --color-surface. Shadow is reserved for things that genuinely float above the page and must be dismissible.
- --elevation-raisedhairline shadow
- --elevation-panelcards, popovers
- --elevation-overlaymodal, toast only
Where derivation breaks
Four places the lattice does not govern. A system that hides its exceptions teaches people to make undocumented ones.
| Exception | Governed by | Rule |
|---|---|---|
| Target size | WCAG 2.5.8 | Interactive controls take an absolute 44px floor via max(). At P = 6 the derived height would be 33px. The floor wins, always, and is written into the token rather than patched per component. |
| Hairlines | device pixels | --kambi-1 is a literal 1px, not r/4. Sub-pixel borders render inconsistently across devices and browsers. A hairline is a rendering primitive, not a lattice measurement. |
| Type scale | the diagonal, √2 | Type does not scale with pitch. The lattice governs space and geometry; the lattice diagonal governs proportion. Coupling type to density makes compact modes unreadable rather than dense. |
| Breakpoints & measure | viewport and reading | Breakpoints are properties of devices, and line length is a property of reading. Both are fixed. Deriving them from the pitch would be numerology, not design. |
tokens.css with an inline comment naming the standard or constraint that forced it, and be listed in this table. An exception that lives in component code instead is a bug, however reasonable it looked at the time.