feat: emit native token outputs for iOS (Swift) and Android (Kotlin) - #53
Open
thomasdigby wants to merge 6 commits into
Open
feat: emit native token outputs for iOS (Swift) and Android (Kotlin)#53thomasdigby wants to merge 6 commits into
thomasdigby wants to merge 6 commits into
Conversation
Adds Swift and Kotlin formatters that emit colours, type scale, spacing, radii and line heights as ThemeTokens constants alongside the existing CSS/JS/Tailwind outputs, per theme. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… outputs Migrates token sources to the DTCG format ($value + group-level $type) so style-dictionary's built-in transforms can drive the conversions: - Swift/Kotlin files are now emitted by the built-in ios-swift/enum.swift and compose/object formats, with colour and rem->pt conversion handled by color/ColorSwiftUI, color/composeColor, size/swift/remToCGFloat and size/compose/remToSp|Dp — the hand-rolled formatters are deleted - Custom code shrinks to src/native.ts: the flat camelCase naming contract and the native token filter (coverage + theme scoping) - Web formatters read $value; the dormant color/hsl and size/rem transforms are removed from the web platforms so their outputs stay byte-identical (verified against a pre-migration snapshot) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… coverage - CI workflow runs build + tests + tsc on every PR; a macOS job installs swiftc/kotlinc so the native compile checks actually execute - pretest hooks build before testing, so the suite can no longer pass against stale lib/ output - completeness tests reconcile the token sources against every output, catching filter/naming regressions that silently drop tokens - value fidelity tests assert exact shadow, breakpoint and font-stack values and check colours agree across the JS, Swift and Kotlin outputs by re-deriving sRGB from the source hsl - native-compile tests compile the generated files with swiftc and kotlinc (against minimal Compose stubs), skipping when absent - assets tests cover every export target, typesVersions path and copied asset — previously untested Documents one pre-existing bug as an expected failure: the CSS formatters emit --color-coolGrey-100 while the JS properties map declares --color-cool-grey-100, so that var() resolves to nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
style-dictionary 5.1.1 requires node >=22, and the build relies on native TypeScript support, so Node 20 failed at install. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GitHub's ubuntu-latest image ships both swiftc and kotlinc, so these tests run there rather than skipping, and invoking a real compiler exceeds vitest's 5s default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the bespoke hsl->rgb maths and the separate hex branch with color2k's parseToRgba, which handles hex, hsl and hsla uniformly. Also routes the Compose AARRGGBB comparison through the same parser. color2k is deliberately not tinycolor2, which style-dictionary converts with, so the assertions stay an independent check rather than a restatement of the build's own maths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #52
What
The build now emits tokens in native-consumable form alongside the existing outputs in
lib/, per theme:lib/theme-*.swift— apublic enum ThemeTokensof SwiftUIColor(red:green:blue:opacity:)andCGFloatconstantslib/theme-*.kt— a Composeobject ThemeTokens(package uk.co.atomlearning.theme) ofColor(0xAARRGGBB),.sp/.dpconstantsHow
Rather than hand-rolling the conversions, this uses style-dictionary's built-in formats and transforms (
ios-swift/enum.swift,compose/object;color/ColorSwiftUI,color/composeColor,size/swift/remToCGFloat,size/compose/remToSp|Dp). To enable that, the token sources are migrated to the DTCG format ($value, plus group-level$type), which is what the built-in transforms key off.Custom code is reduced to src/native.ts:
name/native/cameltransform — the flat camelCase naming contract (color.blue.800→blue800,color.subject.gcse-maths→subjectGcseMaths,size.font.sm→fontSm); these names are the native apps' API, renames are breakingsize.font|leading|radii|space; font stacks/breakpoints/effects excluded per the issue) and per-theme scopingsize.leadingdeliberately has no transform — the unitless multipliers pass through as-is.Web outputs unchanged — verified byte-identical
The previously-listed
color/hslandsize/remtransforms were silent no-ops (they filter on$type, which no token declared); they're removed from the web platforms so the DTCG migration cannot alter web output. All 15 web artifacts (*.css,*.js,*.d.ts,themes.css,media.*) were diffed byte-for-byte against a pre-migration build: identical.Test coverage (135 tests, was 37)
This PR also closes long-standing gaps in output coverage:
.github/workflows/ci.yml) — the repo had none. Runs build + tests +tscon every PR, with a macOS job that installsswiftc/kotlincpretesthooks —yarn test/test:runnow build first, so the suite can't pass against stalelib/(we hit exactly that during development)test/completeness.test.ts— reconciles source tokens against every output. Previously nothing asserted that all tokens reach the outputs; now a filter regression that silently drops e.g.color.gl.*fails the buildtest/values.test.ts— exact shadow / breakpoint / font-stack values (shadows were only checked as a wildcard reset before) and cross-output consistency: Swift colours are re-derived from the sourcehsl()and checked against both the JS theme and the Kotlin ARGB outputtest/native-compile.test.ts— compiles every generated file withswiftcandkotlinc(against minimal Compose stubs). Verified locally with a realkotlinc; skips cleanly when the toolchain is absenttest/assets.test.ts—lib/assets/had zero coverage despite backing ~20 export paths. Now every export target,typesVersionspath and copied asset is checked for existence, non-emptiness and file signatureAll new suites were mutation-tested (dropping one Swift constant and emptying one asset produced 7 failures), so they're not passing vacuously.
Pre-existing bug found, not fixed
The new completeness test surfaced a real defect that predates this PR: the CSS formatters build names from
property.name, emitting--color-coolGrey-100, while the JS/.d.tspropertiesmap kebab-cases it to--color-cool-grey-100. Sovar(--color-cool-grey-100)resolves to nothing in CSS. It's encoded as anit.failsexpected failure with a comment rather than hidden — fixing it changes published CSS variable names, so it's a call for you to make (either rename the CSS vars to kebab or stop kebab-casing in the JS map).Also note the build's "Token collisions detected" warnings pre-date this PR (intentional theme overrides of base tokens).
Token coverage
color.*Colorsize.font.*CGFloat/TextUnitsm0.875 →CGFloat(14.00)/14.00.sp)size.radii.*/size.space.*CGFloat/Dpsize.leading.*No packaging changes:
files: ["lib"]already ships the new files; the native repos vendor them for a pinned version (e.g. via unpkg).🤖 Generated with Claude Code