Skip to content

Design tokens

Every visual value in yemek is a CSS custom property (--yemek-*), defined in src/tokens/index.css and imported once via yemek/style.css. There is deliberately no JS theme-object API — see Architecture and the reasoning in the repo's CLAUDE.md ("Customizability") for why that's deferred until a second real consumer needs it.

Light is the default :root; dark applies via :root[data-theme='dark'] andprefers-color-scheme: dark (same dual-mechanism pattern meal-tracker itself uses), so a consumer can either respect the OS setting or drive an explicit toggle.

Color

TokenLightDarkUsed for
--yemek-color-accent#2f6b52#5fae8aBrand accent — dark moss/jade, not a bright lime or Tailwind's emerald-500 default
--yemek-color-accent-strong#204a39#7fc4a3Accent, emphasized variant
--yemek-color-highlight#e08a4b#eb9d63Warm contrast — celebrations, streaks, CTA emphasis
--yemek-color-highlight-strong#c96f34#f0b283Highlight, emphasized variant
--yemek-color-surface#fbfaf7#17181aCard/component background — warm off-white / warm anthracite, never pure white/black
--yemek-color-surface-inset#f0ede6#232427Recessed surfaces (e.g. track backgrounds)
--yemek-color-text#201f1c#f2f0eaPrimary text
--yemek-color-text-muted#6f6a61#9a968cSecondary/caption text
--yemek-color-border#e3ded3#2e2f32Borders/dividers

Zone colors

Deliberately not identical to the accent color — otherwise "on track" would just mean "the brand," which carries no real signal.

TokenMeaningLightDark
--yemek-color-zone-underStill have room (cool, neutral)#4c7fa6#6ea3c9
--yemek-color-zone-onTrackOn track — the one exception, equals the accent on purpose#2f6b52#5fae8a
--yemek-color-zone-overOver goal (warm, not alarm-red)#c4483b#d97767

Nutrient colors

One token per nutrient, decided ahead of any multi-nutrient component so future components (e.g. a macro-split bar) consistently pull from the same set instead of picking ad-hoc colors.

TokenLightDark
--yemek-nutrient-protein#7d6a99#a693c4
--yemek-nutrient-carbs#cf9d4f#e0b768
--yemek-nutrient-fat#c17b6b#d99c8e
--yemek-nutrient-calories= --yemek-color-accent= --yemek-color-accent
--yemek-nutrient-water#3f9c96#5fc0ba

Heat/streak tokens

A single warm hue family end to end (gold → rust → ember) instead of a generic scale that jumps to an unrelated blue at the top tier. StreakHeatmap's middle tiers reuse --yemek-color-highlight/-highlight-strong rather than adding two more parallel colors.

TokenLightDark
--yemek-heat-1#d4a24e#e6bd72
--yemek-heat-2#a8452f#c15a3c
--yemek-heat-3#7a2e22#9c4530

Spacing & radius

TokenValue
--yemek-space-1--yemek-space-84px, 8px, 12px, 16px, 24px, 32px
--yemek-radius-sm4px
--yemek-radius-md10px
--yemek-radius-full999px — pills/badges only, deliberately not used for cards/buttons

Typography

TokenValue
--yemek-font-display'Familjen Grotesk', ui-sans-serif, system-ui, sans-serif — bold geometric sans for numbers
--yemek-font-family'Work Sans', system-ui, sans-serif — UI text, deliberately not Inter
--yemek-font-size-sm / -md / -lg / -display0.8125rem / 1rem / 1.5rem / 2.5rem
--yemek-font-weight-medium / -bold / -display500 / 700 / 700
--yemek-font-feature-numeric'tnum' 1 — tabular numbers so a changing value never shifts sideways

Fonts

Font files are not bundled into the library build — @fontsource's CSS base64-inlines every language subset of every weight (~640KB) regardless of which ones a consumer actually renders. Install @fontsource/familjen-grotesk and @fontsource/work-sans yourself and import only the weights you use.

Motion

Every component animates state changes — no silent, instant value jumps (CLAUDE.md §1a, a hard rule). Signature motion is an elastic ease-out on fill plus a soft glow pulse on goal reached, deliberately standalone from meal-tracker's own "Nitro" shimmer effect.

TokenValue
--yemek-motion-fast150ms
--yemek-motion-medium350ms
--yemek-motion-slow700ms
--yemek-motion-easecubic-bezier(0.34, 1.56, 0.64, 1) — slight overshoot ("elastic")
--yemek-motion-ease-linearcubic-bezier(0.4, 0, 0.2, 1)

Every animation also has a prefers-reduced-motion: reduce fallback (overshoot/glow collapse to a simple crossfade, or no transition at all) — "always animate" doesn't mean ignoring motion preferences.

Overriding tokens in a host app

Override any subset at your app's root. This is safe for structural/neutral tokens (surface/border/text) even against yemek's own brand colors, since each component reads through the same custom properties:

css
:root {
  --yemek-color-surface: #161c27;
  --yemek-color-border: #2a3242;
  --yemek-color-text: #f4f6fb;
}

One caution learned from meal-tracker's own integration: a token tuned only against yemek's own --yemek-color-surface isn't guaranteed to contrast against an arbitrary host background (an opaque hex can coincidentally match). Where contrast matters regardless of host surface (e.g. MetricRing's track), the component derives its color from --yemek-color-text via color-mix() instead of a fixed surface-relative token.