Every colour, size, and timing value below is read from the tokens in @iquit/ui. Contrast ratios are measured against the ground at render time, not quoted from memory. The same token values drive the React Native app, so the two platforms differ in rendering, never in specification.
One rule governs every surface: light comes from directly above the screen. Raised elements catch a 1px specular line on their top edge and cast a shadow below; recessed elements take the shadow on their top inner edge and catch light on their bottom. Surfaces are the same cool light at increasing intensity over the ground, so they stack: a surface on a surface is automatically brighter, which opaque greys cannot do.
Surface intensities reproduce the brand-locked greys almost exactly (5.0% lands on #1A1C25 against the locked #1A1C26; 12.5% is #262936 exactly), so this is a change of mechanism, not a repalette.
A card inside a card. Because surfaces are light rather than opaque greys, the inner one is automatically a second level.
Generated in OKLCH from each brand-locked base, which is pinned unchanged at step 500. Lightness is perceptually even and monotonic, so a step number means the same thing in every ramp. The number under each swatch is its measured WCAG contrast against the ground.
Steps 0–3, 8 and 11 are the brand-locked greys; the rest are interpolated between them in Oklab. Step −1 sits below the ground for wells and inset tracks.
A component asks for a tone, never a hue. Each tone resolves to nine roles, and every toned component uses those nine and nothing else, which is why a danger card and a success card are structurally identical. Note the split between solid and fill: solid is the saturated mark used at chip scale under dark ink, fill is the deep one used at button scale under light text. onSolid and onFill name the text colour each fill carries, so the pairing is a token rather than something each component has to remember.
One card, six tones, one attribute changed. This is the multi-colour panel test: every tone has to hold on the same ground.
The brand tone carries IQUIT.GG itself and the one primary action.
Calm, factual, asks nothing of you.
Harm, relapse, and destructive actions.
Recovery and progress you earned.
Questionnaires and prompts that ask something of you.
Caution and time pressure, short of harm.
Inter throughout. Sizes and line heights are in px so web and native resolve to the same number; tracking is in em.
Conquer gaming. Conquer the world.
Conquer gaming. Conquer the world.
Conquer gaming. Conquer the world.
Conquer gaming. Conquer the world.
Conquer gaming. Conquer the world.
Conquer gaming. Conquer the world.
Default 16.5:1
Muted 7.1:1
Disabled 3.6:1
Gold gradient
A 4px base step. Control heights exist so a button, a field, and a select always line up; md is 44px, the iOS minimum touch target.
xs4pxsm8pxmd16pxlg24pxxl40pxxxl64pxsm8pxmd14pxlg18pxxl34pxfull999px36px44px52pxnone0 layerslow2 layershigh3 layerswell1 layeroverlay3 layersWeb renders the whole stack. React Native renders only the first layer (iOS allows one shadow and Android only an elevation number), so a shadow may never carry meaning the layer and highlight do not already carry.
The opener's staggered entrance uses stagger + rise + riseDistance. Every transition on this page uses one of these durations and the shared easing curve.
fast120msbase200msslow320msstagger90msrise500msriseDistance16pxglassArrive50msspin700mspulse1400mstoast3200mseasingcubic-bezier(0.2, 0, 0, 1)26 primitives. The inventory is exported from @iquit/ui, so a component the mobile app has not implemented yet is a visible gap rather than an assumption.
primarysecondaryghostdangersolidbrandinfodangersuccessfocuswarnsoftbrandinfodangersuccessfocuswarnoutlinebrandinfodangersuccessfocuswarnselectableThe default surface for content.
Tinted surface and border from one tone.
Border only, no fill.
Your data stays on device
Nothing syncs until you create an account.
You're in a relapse window
Last three sessions all started after 11pm.
Longest streak yet
You passed your previous best of 11 days.
A day is still open
Yesterday has no answer yet. It closes at midnight.
62 hours
The only elements that read off the page rather than off a surface. They take the one shadow stack reserved for them and sit above a scrim, which is the light being blocked, so it deepens the ground rather than tinting it. Three, deliberately: a tooltip needs hover, which mobile does not have.