Mine was.
Not deliberately — nobody sat down to write something false.
But the documentation said a thing that was not true, and nothing in the repository could tell.
We maintain component implementations for React, SwiftUI, Jetpack Compose, and Flutter — the four catalogue-complete platforms this story is about.
Every component page listed its supported platforms.
Every listing was a string in a JSON file that a human typed.
And nothing checked whether the implementation it claimed actually existed.
Then I wrote a script that checked.
On its first run it reported that direction-provider — advertised on SwiftUI, Compose, and Flutter — had no implementation on any of them.
Not a partial port.
No file.
Parity is a claim, and claims decay
A single-platform library has a useful property: the documentation and the code often live in the same repository, in the same language, and sometimes come from the same source.
Delete a component and something tends to break.
A cross-platform design system loses that property almost immediately.
The React implementation is TypeScript.
The iOS implementation is Swift.
The Android implementation is Kotlin.
Flutter has Dart.
They are compiled by different toolchains, on different machines, in jobs the web build may know nothing about.
The matrix tying them together is not necessarily code.
It is often just a description of code.
And that description tends to decay in one direction:
Toward optimism.
Nobody forgets to add a platform they just shipped.
People forget to remove one they abandoned.
Or one they planned.
Or one they never actually started.
The gap widens silently, usually in the flattering direction, until somebody tries to use what the documentation said existed.
So the useful question about any cross-platform design system — including this one — is not:
“How many components does it have?”
It is:
What would fail if this claim were false?
If the answer is “nothing”, the claim is decoration.
Three levels of verification
I think of platform verification in three levels.
Level 0 — the name is spelled correctly
Our original check did this.
It validated that "SwiftUI" was a known platform string.
That is useful, but it cannot tell you whether any SwiftUI implementation actually exists.
A system can pass Level 0 perfectly while documenting components that were never built.
Level 1 — source exists
Look for a real implementation file in the platform package.
A component claims SwiftUI?
There should be Swift source.
Claims Compose?
There should be Kotlin source.
Claims Flutter?
There should be Dart source.
This check is cheap, deterministic, and scriptable from almost anywhere.
And it catches an important class of failure:
“We said we built this, but there is no implementation.”
Level 2 — it compiles
A file existing still does not prove that its API is correct.
The platform's own compiler needs to resolve:
- the symbol,
- the arguments,
- the parameter names,
- the types,
- and the surrounding usage.
This is the level that catches a wrong API, rather than just a missing one.
Most projects that verify anything can accidentally stop around Level 0.
The dangerous part is that Level 0 still feels like verification:
- it has a script,
- it runs in CI,
- and it goes green.
What Level 1 caught
The first useful failure was direction-provider.
It claimed three native platforms:
- SwiftUI,
- Jetpack Compose,
- Flutter.
There was no implementation on any of them.
The reason turned out to be more interesting than the mismatch.
Those frameworks already carry layout direction natively:
- SwiftUI →
environment(\.layoutDirection) - Compose →
LocalLayoutDirection - Flutter →
Directionality
There was never really anything useful to port.
So the correct documentation was not:
“Supported on every platform.”
It was:
“React-specific by design; the native frameworks already provide the capability.”
That distinction matters.
Another example was combobox.
It appeared to be a React component.
But there was:
- no
combobox.tsx, - no
Comboboxexport, - and no standalone implementation.
It is a recipe built from Command primitives.
That is still useful.
It is simply not a component.
It says so now.
Correcting those claims moved the published numbers down:
- SwiftUI: 91 → 90
- Compose: 90 → 89
- Flutter: 91 → 90
- Components on all four catalogue-complete platforms: 90 → 89
The numbers got worse.
The documentation got true.
Same event.
The check should insult you
A verifier that only confirms what you already believe is not much of a verifier.
On its first run, ours also flagged sonner as missing on SwiftUI and Flutter.
That result was wrong.
The implementations existed, but under platform-native names:
Toaster.swifttoaster.dart
The interesting part was how to fix the checker.
The tempting solution is to add a lookup table inside the script:
if (component === "sonner" && platform === "SwiftUI") {
return "Toaster";
}
But now the checker knows something the source of truth does not.
That is a trap.
Instead, the alias belongs beside the claim in the manifest:
"sonner": {
"platforms": ["React", "SwiftUI", "Compose", "Flutter"],
"sourceNames": {
"SwiftUI": "Toaster",
"Flutter": "toaster"
}
}
The rule became:
When the checker is wrong, fix the data — not the checker.
A checker full of exceptions eventually stops being a specification and becomes another undocumented source of truth.
What Level 2 caught
Source existing is not the same thing as source being correct.
The gap becomes especially dangerous in documentation, because examples are easy to write by hand and surprisingly easy to never compile.
Our component pages carried roughly 300 hand-written native snippets.
I checked every Kinetix* identifier in them against the real packages.
The first symbol-level pass looked like this:
- SwiftUI: 164 symbols, all real
- Flutter: 175 symbols, all real
- Compose: 182 symbols, with three that did not exist
The worst example was on the Chart page.
The documentation showed this:
// KinetixChart is a hand-drawn CustomPaint bar chart over the
// --chart-1…5 palette (no charting dependency). Line/area are a follow-up.
KinetixChart(
points = listOf(
KinetixChartPoint("Jan", 186f, seriesIndex = 0),
),
)
Neither KinetixChart nor KinetixChartPoint existed in the Compose package at the time.
There was no Compose Chart implementation.
The manifest already said so correctly.
And there was another clue hiding in plain sight:
CustomPaint is a Flutter API.
The Flutter implementation's rationale had effectively been pasted onto an invented Compose example.
The website rendered that code under a tab labelled Android, beside real examples, with nothing visually distinguishing it from working API.
Nobody deliberately lied.
Someone wrote a plausible snippet for a component they expected to build.
And there was no compiler between that sentence and the reader.
A Compose chart has since been written.
Interestingly, the API that eventually landed is close enough to the invented one to be uncomfortable:
KinetixChartKinetixChartPointlabelvalueseriesIndex
But the old example still would not compile against the actual implementation.
The real signature requires a description, because a chart that a screen reader cannot narrate is not finished.
Guessing a future API correctly is not the same as having that API today.
Where symbol checking stops
Symbol checking is cheap.
It catches a lot.
But it is still not compilation.
Consider this example:
KinetixDataTable(
columns = listOf(
KinetixDataColumn("Invoice", sortKey = { it.invoice }) { it.invoice },
),
rows = invoices,
)
A symbol check can catch one problem immediately:
KinetixDataColumn does not exist.
The real class is:
KinetixColumn<T>
But two other problems are invisible to a simple symbol scan.
The parameter is data, not rows.
And the trailing lambda binds to the constructor's last parameter, weight: Float, rather than a cell renderer.
Every symbol could exist and the snippet could still fail to compile.
That is why Level 2 matters.
The direction we are taking is to move examples into files the real platform CI builds, then quote those exact regions on the website.
For example:
// kx-usage:button-demo
KinetixButton(onClick = save) {
Text("Button")
}
// kx-usage:end
The compiler sees the example.
The website reads the same example.
And a drift check fails if the two diverge.
One source.
Two surfaces.
One failure path.
Six of our hundred-odd demo examples currently take their native source that way.
That number is intentionally specific.
Saying “six” is more useful than saying “we're migrating.”
The same failure, one layer up
Our Blocks page had the same problem at a larger scale.
Blocks are composed examples — things like sign-in cards and application layouts.
Originally, one large file contained:
- the live preview,
- a hand-written React snippet,
- a hand-written Compose snippet,
- a hand-written Flutter snippet.
They were supposed to represent the same thing.
They were not generated from the same source.
And they had already drifted.
For example, the React snippet showed:
id="email"
while the live component rendered:
id="bl-email"
Two copies of one thing.
No verification.
Already different.
Then there was the CTA banner.
Its own copy said:
“React, SwiftUI, Compose and Flutter from a single source.”
But it had no SwiftUI source.
And the three snippets that did exist were independently typed strings.
The sentence was wrong twice in eleven words — inside the example intended to demonstrate the claim.
Today, every block snippet comes from a file that compiles.
Five questions worth asking
Whether you are evaluating a design system or maintaining one, these are the questions I would ask.
1. Where does the platform matrix live, and what reads it?
A hand-maintained table in a README is a description.
It is not automatically a specification.
Ask what consumes that matrix and what verifies it.
2. What happens in CI if a component is listed for a platform with no source?
If the answer is:
“Nothing.”
then the listing does not carry much weight.
A platform claim should have a failure condition.
3. Are the documentation snippets compiled?
If not, assume some fraction of them are fiction.
In our case, the obvious symbol-level error rate was around 1%.
That sounds small until the 1% is an API you publish as real.
4. Does the system distinguish “not ported” from “ported”?
Sometimes not porting something is the correct architecture.
That should be documented explicitly.
For components deliberately not implemented on a platform, we now explain the native composition or framework capability to use instead.
A useful “Not a Flutter port — here's why” is much better than a generic Flutter tab that implies an implementation exists.
5. What are the real numbers?
Not just catalogue size.
Ask for:
- count per platform,
- what the denominator is,
- what is excluded,
- and how the number is produced.
For KinetixUI today:
| Platform | Coverage | Status |
|---|---|---|
| React | 98 / 98 | Stable |
| Angular | 80 / 98 | Preview |
| SwiftUI | 90 / 98 | Stable |
| Jetpack Compose | 90 / 98 | Stable |
| Flutter | 90 / 98 | Stable |
The catalogue contains 98 entries: 97 components and one documented recipe.
90 of 98 are implemented across all four catalogue-complete platforms.
There are 8 documented exceptions, each with a reason.
Angular deliberately sits outside that denominator because its catalogue is still incomplete and explicitly labelled Preview.
Folding Angular into the catalogue-complete parity figure would not make the system more truthful.
It would just make the number harder to understand.
Those numbers are generated from repository data.
Edit the visible count by hand and the verification fails.
That is also why some historical numbers earlier in this article are lower.
The correction described above temporarily moved Compose from 90 to 89.
Later, a real Compose Chart implementation moved it back to 90.
Not because someone edited a marketing number.
Because somebody built the missing thing.
A generated number moves when the product moves.
That is the property I want.
The part that matters
None of these guardrails are particularly exotic engineering.
The platform-source check is roughly a couple hundred lines.
At its core, it compares claims with files.
The valuable part is where each check came from.
The platform-source check exists because direction-provider was wrong.
The documentation checks exist because the Chart page advertised an API nobody had written.
The block checks exist because a sentence about a single source sat above multiple independently typed copies.
That leads to the only pattern I would strongly recommend:
When documentation turns out to be wrong, do not just fix the documentation.
Ask:
What could have caught this before a reader did?
Then build that check.
And run it against everything else you have already published.
Mine found four more problems.
You probably will not enjoy that first run.
Run it anyway.
KinetixUI is an open-source design system built around one canonical DTCG token source. Tokens are generated into platform outputs; components are implemented natively per platform rather than transpiled from one shared runtime.
See the evidence
Explore the current generated platform coverage →
The coverage page is derived from the component manifest and checked against real source rather than maintained as a marketing table.
For the architecture behind it — shared tokens, native components, and what parity takes to verify — read:
Cross-platform design systems: architecture, parity and trade-offs →
Top comments (0)