KuKi
Intelligent health companion for your furbabies β a token-driven system for Android, iOS, and the web (PWA), built from the KuKi brand mark and the app's own screens. This page mirrors the Figma library 1:1; component classes/props here are exactly what ships in react/src.
How this is organized
Three tiers, same as Figma: primitives (raw ramps) β semantic (role-based, light+dark) β components (consume semantic only). Nothing in react/src touches a primitive or a raw hex value directly.
tokens/generate_tokens.py β single source of truth, asserts WCAG AA on save
tokens/css/tokens.css β generated CSS custom properties (light + dark)
react/src/components/*.tsx β typed components, semantic tokens only
react/styles/components.css β component CSS
assets/ai-sparkle-animated.svg β KuKi's real animated Pet AI icon
Usage guidelines
Do
- Use semantic tokens (
surface/*,text/*) β never raw hex or primitives β in components. - Reserve status colors for symptom-free streaks only.
- Keep buttons as pills; keep cards at
radius/containerwith smoothing on.
Don't
- Don't introduce a second typeface β Google Sans Flex only, vary by weight.
- Don't use pure black for high-emphasis fills β use chocolate ink.
- Don't invent new status hues β extend the existing five-state model.
Color
Derived from the KuKi mark and login screen: terracotta replaces black as the primary action color, golden cream is the accent, and chocolate ink stands in for black on high-emphasis surfaces. Status colors carry specific meaning tied to symptom-free streaks.
Wellness status meaning
The core semantic mapping behind every Wellness Card.
Primitive ramps
50β950 step scales. Semantic tokens alias these; components never reference them directly.
Semantic surfaces (current theme)
background: var(--kuki-sem-surface-brand);
color: var(--kuki-sem-text-on-brand);
Typography
Google Sans Flex, one family across weights ExtraLightβBold. No secondary typeface, no code font β this is a pet health app, not a code editor. This section is theme-aware; toggle dark mode to see both.
.kuki-wellness__value { font-size: var(--kuki-font-size-6xl); font-weight: var(--kuki-font-weight-extralight); }
.kuki-metric-card__value { font-size: var(--kuki-font-size-xl); font-weight: var(--kuki-font-weight-semibold); }
Iconography
Material Symbols (Outlined), matching the icon language already used across the app screens.
Buttons
One unified Button component β the old separate IconButton is now just iconOnly. 5 appearances Γ 2 tones Γ 6 states (incl. loading) Γ 3 sizes Γ icon-only Γ selected = 720 variants in Figma. A representative set below; full matrix on the Button page.
Appearance
<Button appearance="primary" size="lg">Start tracking</Button>
<Button appearance="secondary">View as Guest</Button>
<Button appearance="ghost">Skip</Button>
<Button appearance="link">Learn more</Button>
<Button appearance="emphasis">Get started</Button>
Icon slots
4 independent slots: outer leadingIcon/trailingIcon sit at the button's own edge padding; inner leadingAdornment/trailingAdornment hug the label inside their own content group, at a gap that's independent of the outer icons.
<Button appearance="emphasis" size="lg"
leadingIcon={<HomeIcon/>} leadingAdornment={<CheckIcon/>}
trailingAdornment={<CheckIcon/>} trailingIcon={<HomeIcon/>}>
Content
</Button>
Icon scaling by size
Icons are bound to distinct size tokens per button size (12 / 16 / 20px labeled, 16 / 20 / 24px icon-only) β not a single fixed size stretched to fit.
Tone, disabled & loading
loading is never an overlay on the normal appearance β it forces the same blocked look as disabled, with a spinner as the leading adornment next to editable text.
<Button appearance="primary" tone="destructive">Log seizure</Button>
<Button appearance="primary" loading>Please wait</Button>
<Button appearance="primary" disabled>Disabled</Button>
Icon-only (iconOnly)
Same appearance/tone/state/loading system as labeled buttons β just iconOnly forcing a fixed circular shape instead of a hug-padded pill. This replaces the old separate IconButton.
<Button appearance="emphasis" size="lg" iconOnly label="Add" icon={<AddIcon/>} />
<Button appearance="ghost" iconOnly selected label="Favourite" icon={<HeartIcon/>} />
Badges & Chips
Badges are tight rectangular tags (sex, spayed/neutered, due/overdue status). Chips are pill-shaped with an optional icon, used on card headers.
Badge
<Badge tone="pink" emphasis="bold">Female</Badge>
<Badge tone="critical" emphasis="subtle">Overdue</Badge>
Chip
<Chip tone="turquoise" icon={<ScaleIcon />}>Weight</Chip>
Cards
Every card corner uses the squircle utility (preview/squircle.js) β a true superellipse clip-path approximating Figma's native corner smoothing, since CSS border-radius alone has no smoothing control.
Wellness Card β the five states
Gradient is centered and symmetric; the pastel edge color reaches all four corners evenly.
<WellnessCard status="critical" title="Seizure Streak" value="0 days"
subtitle="2 grand mal seizures today" />
Metric Cards
<MetricCard metric="sleep" label="Sleep" value="No logs" sub="Log today's sleep" empty />
Card with list rows
<SectionHeader title="Health Summary" action="Edit" />
<ListRow title="Combined" value="8 Jul '26" valueSub="Overdue by 3 days" valueSubTone="critical" />
Pet AI icon
The swirling background is a CSS approximation of a shader β a blurred, rotating conic gradient screen-blended over a radial base (no WebGL). The sparkle burst on top is KuKi's real animated asset, inlined at runtime with uniquified ids (preview/ai-orb.js) so its CSS keyframes actually run β browsers freeze animated SVGs loaded via plain <img> at their first frame β and so multiple orbs on one page never collide.
@keyframes. Respects prefers-reduced-motion for the shader background.
<AIOrb sparkleSrc="/assets/ai-sparkle-animated.svg" label="Pet AI" onClick={openAssistant} />