Skip to content
Kinetixui

Tokens

Source#

tokens/
├─ primitives/
│  ├─ color.json        6 brand ramps + synthesized neutral, 0–1000
│  ├─ dimension.json    spacing, radius, border width
│  ├─ typography.json   Material-3 scale, Montserrat
│  ├─ motion.json       duration + easing (cubicBezier)
│  ├─ opacity.json      hidden/disabled/muted/visible scale
│  ├─ interaction.json  target size, focus ring, press, drag threshold
│  └─ z-index.json      base/raised/docked/focus/sticky/overlay scale
└─ semantic/
   ├─ color.light.json  aliases → semantic contract
   ├─ color.dark.json   synthesized
   └─ typography.json    composite text styles

All DTCG format ($value / $type), extracted from Figma variables.

Build#

pnpm build:tokens   # node style-dictionary/build.mjs

Style Dictionary v4 runs once per theme because two files defining the same token path would collide in a single run. The light pass emits every platform; the dark pass emits the web globals.dark.css and the native semantic theme files that a KinetixTheme needs real dark values for — Theme.dark.kt (Compose), KinetixColorsSwiftUI.dark.swift (SwiftUI) and kinetix_color_scheme.dark.dart (Flutter).

Output#

PlatformFiles
Webweb/globals.css, web/globals.dark.css, web/tokens.ts
iOSios/KinetixColors.swift, ios/Theme.swift (UIColor), ios/KinetixType.swift; ios/KinetixColorsSwiftUI.swift + .dark.swift (SwiftUI Color, for packages/ui-swiftui); ios/KinetixMotion.swift
Androidandroid/Color.kt, android/Theme.kt + Theme.dark.kt, android/KinetixType.kt, res/values/{colors,dimens}.xml; android/KinetixMotion.kt
Flutterflutter/app_colors.dart, flutter/app_theme.dart, flutter/app_text.dart; flutter/kinetix_color_scheme.dart + .dark.dart (for packages/ui-flutter); flutter/kinetix_motion.dart

The KinetixColorsSwiftUI / KinetixColorScheme pairs are additive — the older Theme.swift / app_theme.dart outputs (light-only, colliding class names) are untouched. See SwiftUI and Flutter for why.

Motion + scale#

duration, easing, opacity, and z-index are theme-independent, like typography — defined once, the same in light and dark. Each platform's output groups them into KinetixDuration / KinetixEasing / KinetixOpacity / KinetixZIndex (native — which also get the spatial scale as KinetixSpacing and KinetixRadius, see Foundations) or --duration-* / --easing-* / --opacity-* / --z-index-* custom properties (web), and packages/ui's tailwind.config.ts wires the web ones into real Tailwind utilities — duration-fast, ease-standard, opacity-disabled, z-overlay — used directly in component source instead of the ad hoc duration-200 / z-50 literals components used to carry. A handful of pre-existing values that don't cleanly match a token step (e.g. a couple of opacity-40/opacity-60 icon dimmings) are left as literals with a comment, rather than silently changed to the nearest token.

Newly defined, not yet consumed by components: duration.instant (100ms) and the easing.enter, easing.exit and easing.emphasized curves (standard decelerate / accelerate / expressive), plus the larger spacing steps (spacing.10–spacing.32, 40–128) and radius.xxl (24). They are part of the contract and generated for every platform, but no component reads them yet and there are no Tailwind utilities for the new curves — adopting them is incremental, component by component.

Interaction tokens#

Behavioral values, not visual ones — how big a target has to be, how far a focus ring sits, how a press or a drag reads. Theme-independent, like motion:

TokenValueWired up?
interaction.target.minimum44pxNot yet — WCAG 2.5.5 (AAA) / iOS HIG / Material's converged minimum hit-target size. No component in this package currently has an explicit hit-target smaller than 44px that would benefit without a bigger layout change (Checkbox/Radio/Switch's ~18–20px visual controls rely on callers wrapping them in a Label for the larger click area, the same pattern their Radix primitives assume) — defined for app authors and future components to reach for, not retrofitted here.
interaction.target.default40pxNot yet — the default a standalone control should target when nothing else constrains its size.
interaction.focus.width1pxYes — backs the spread of every shadow.focus* entry's inner layer (tokens/semantic/shadow.json), replacing a hardcoded literal. Compiled CSS output is unchanged (still 0 0 0 1px …).
interaction.focus.offset2pxNot yet as a token reference — matches the dominant ring-offset-2 already used across packages/ui/src/components (4 of 6 current call sites; the other 2 intentionally use ring-offset-1 for tightly-bordered controls). Tailwind's ring-offset-* utilities are static classes, not CSS-var-driven, so wiring this one needs either a custom Tailwind token or a run of literal replacements — left for a follow-up slice.
interaction.press.opacity0.8Not yet — Button's own active state is a color shift (active:bg-primary/90), not opacity-based, so there's no current opacity-driven press consumer to point this at. Defined for components that do reach for opacity feedback.
interaction.drag.threshold4Yes — KanbanBoard's PointerSensor activationConstraint.distance, via tokens.interaction.drag.threshold imported from @kinetixui/tokens. Matches the value KanbanBoard already shipped tuned at, not dnd-kit's textbook default of 8 — this token records the deliberate existing value rather than silently changing drag feel.

Web-only in this slice — none of the native platform ports (ui-compose, ui-swiftui, ui-flutter) consume interaction.* yet.

Semantic contract#

The values every component actually resolves to, in both themes:

TokenLightDark
--background#ffffff#050c11
--foreground#050c11#f0f7ff
--cardsynthesized#ffffff#0b1821
--popoversynthesized#ffffff#0b1821
--primary#1d4ed8#60a5fa
--primary-foreground#f0f7ff#050c11
--secondary#c7cfc7#2e362e
--secondary-foreground#465245#e3e7e3
--muted#f6f6f6#102432
--muted-foreground#6d6d6d#92b2c8
--accent#f0f7ff#102432
--accent-foreground#1d4ed8#f0f7ff
--destructivedarkened for AA#c60a0a#dd6a6a
--success#5d6d5c#90a08f
--warningdarkened for AA#7f5b21#ffc975
--border#92b2c8#395a70
--ringsynthesized#1d4ed8#60a5fa
--brandidentity — the Figma navy#1b3c53#7495ab
--brand-foreground#f0f7ff#050c11
--actiondefaults to primary#1d4ed8#60a5fa
--action-foregrounddefaults to primary-foreground#f0f7ff#050c11
--action-hoveraction @ 90% — native#3460dc#5796e3
--action-pressedaction @ 85% — native#3f69de#528ed7
--linkdefaults to primary#1d4ed8#60a5fa
--focusdefaults to ring#1d4ed8#60a5fa

Deviations from raw Figma#

The build fixes the Primay typo, rounds letterSpacing float artifacts, and synthesizes what Figma lacks (--card, --popover, --ring, the neutral ramp, the dark theme, radius.lg, spacing.7). Each is documented in TOKENS.md with its rationale.