Skip to content

Emit native token outputs for iOS (Swift) and Android (Kotlin): colours, type scale, spacing, radii #52

Description

@thomasdigby

Context

The native iOS app (currently in App Store review) renders its native chrome — login screen, splash, error screens, toasts — with hardcoded colour, font-size, and radius values that drift from the design system. An Android app will follow. We want the theme package to be the single source of truth for those values, the same way it already is for web via the CSS/JS/Tailwind outputs.

Requirement

The build should emit tokens in native-consumable form alongside the existing outputs in lib/:

  • Swift (lib/theme-*.swift): a ThemeTokens enum of SwiftUI Color(red:green:blue:opacity:) and CGFloat constants
  • Kotlin (lib/theme-*.kt): the equivalent Compose Color(0xAARRGGBB) / Dp/TextUnit constants (needed when the Android app goes into development — can land later than Swift)

Token coverage

Group Emit as Conversion
color.* Color hsl()/hex → sRGB components at build time (SwiftUI has no HSL initialiser)
size.font.* CGFloat (pt) rem × 16 — e.g. sm 0.875 → 14pt
size.radii.* CGFloat (pt) rem × 16 — e.g. md 0.5 → 8pt
size.space.* CGFloat (pt) rem × 16
size.leading.* CGFloat unitless line-height multipliers, emitted as-is

Excluded, deliberately:

  • font.families.* — web font stacks (system-ui, -apple-system, …); the native apps bind their own bundled font by name
  • size.breakpoint.* — windowed-web concern, meaningless full-screen
  • effects.* — CSS box-shadow strings don't translate mechanically to native shadow parameters

Details

  • Flat camelCase names from the token path, e.g. color.blue.800blue800, color.subject.gcse-mathssubjectGcseMaths, size.font.smfontSm. These names become the native apps' API — renames are breaking.
  • Alpha tokens (color.alpha.*) carry their opacity component.
  • Per-theme variants like the other formatters (theme-base.swift, theme-atom.swift, …).
  • No new packaging. The files ship inside the existing npm tarball (files: ["lib"]); the native repos vendor the file for a pinned version (fetched from unpkg in their build). No Swift Package / Maven artifact needed.
  • Tests: generated files contain no unconverted hsl()/hex/rem values and no duplicate constant names.

Status

A working Swift formatter + vitest coverage already exists as a spike for the colour half (built from a local branch; the iOS repo currently vendors its output). Happy to open it as a PR to seed this — sizes are a small extension of the same formatter, and Kotlin follows the same pattern.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions