<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vellira</title>
    <description>The latest articles on DEV Community by Vellira (@vellira).</description>
    <link>https://dev.to/vellira</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4111319%2Fffec2fda-113b-408c-8ef9-006a5d4e2cb0.png</url>
      <title>DEV Community: Vellira</title>
      <link>https://dev.to/vellira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vellira"/>
    <language>en</language>
    <item>
      <title>From Scaffold to Production: How Vellira Generates Components</title>
      <dc:creator>Vellira</dc:creator>
      <pubDate>Wed, 30 Sep 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/vellira/from-scaffold-to-production-how-vellira-generates-components-4d09</link>
      <guid>https://dev.to/vellira/from-scaffold-to-production-how-vellira-generates-components-4d09</guid>
      <description>&lt;p&gt;A component generator becomes useful when it stops thinking in terms of files and starts thinking in terms of &lt;strong&gt;product surfaces&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Creating this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Component.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is the easy part.&lt;/p&gt;

&lt;p&gt;In a real design system, a component may also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;public types,&lt;/li&gt;
&lt;li&gt;package exports,&lt;/li&gt;
&lt;li&gt;tests,&lt;/li&gt;
&lt;li&gt;Storybook,&lt;/li&gt;
&lt;li&gt;metadata,&lt;/li&gt;
&lt;li&gt;documentation,&lt;/li&gt;
&lt;li&gt;platform-specific behavior,&lt;/li&gt;
&lt;li&gt;website examples,&lt;/li&gt;
&lt;li&gt;playground configuration,&lt;/li&gt;
&lt;li&gt;validation contracts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all of those surfaces are created manually, they drift.&lt;/p&gt;

&lt;p&gt;If they are generated without clear ownership, regeneration becomes dangerous.&lt;/p&gt;

&lt;p&gt;So Vellira's component generator is built around a different idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Describe component intent once, turn it into a deterministic plan, and generate the repeatable repository contract around it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Component.tsx&lt;/code&gt; is only one node
&lt;/h2&gt;

&lt;p&gt;A minimal generator might do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input: Button

output:
Button.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That saves some typing.&lt;/p&gt;

&lt;p&gt;But it does not solve most of the repetitive work around a production component library.&lt;/p&gt;

&lt;p&gt;A real component can touch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;runtime implementation
public types
local exports
package exports
API contracts
tests
Storybook
styles / tokens
metadata
docs
website examples
catalog registries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source file is just one node in that graph.&lt;/p&gt;

&lt;p&gt;If the generator creates only the implementation, it automates the cheapest part while leaving most consistency risk with humans.&lt;/p&gt;

&lt;p&gt;The more useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do we already know about this component before generation, and which repository surfaces can be derived from that information deterministically?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Start with explicit intent
&lt;/h2&gt;

&lt;p&gt;Vellira's generator does not only ask for a component name.&lt;/p&gt;

&lt;p&gt;The public CLI expresses things such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm create:component &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;Name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;platform&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;layer&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;category&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;profile&amp;gt;] &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;controlled,keyboard,...] &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--parts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Root,Trigger,Content] &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--check&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of those inputs describe product structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;platform
layer
category
profile
capabilities
parts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Profiles represent reusable component families:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;base
form-control
compound
overlay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key is that these are reusable architectural concepts.&lt;/p&gt;

&lt;p&gt;The generator should understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;compound component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if componentName === "Tabs"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Profiles beat component-name heuristics
&lt;/h2&gt;

&lt;p&gt;This is fragile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if name === "SomeInput":
  generate form state

if name === "SomeTabs":
  generate compound parts

if name === "SomeModal":
  generate overlay behavior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generator slowly becomes a database of product names.&lt;/p&gt;

&lt;p&gt;Instead, reusable intent can be explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;profile=compound
parts=Root,List,Trigger,Content
capabilities=controlled,uncontrolled,keyboard,focus-management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Tabs-like component might therefore be described structurally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm create:component Tabs both components navigation &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;compound &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;controlled,uncontrolled,keyboard,focus-management &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Root,List,Trigger,Content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing is not the exact command.&lt;/p&gt;

&lt;p&gt;It is that the generator receives &lt;strong&gt;facts&lt;/strong&gt;, not hidden assumptions attached to the word &lt;code&gt;Tabs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That makes the system easier to test and extend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan React and React Native together, generate them separately
&lt;/h2&gt;

&lt;p&gt;When a component targets both runtimes, Vellira resolves two targets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packages/react/...
packages/react-native/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They share component identity and product intent.&lt;/p&gt;

&lt;p&gt;But each runtime can still have its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;implementation,&lt;/li&gt;
&lt;li&gt;public API extensions,&lt;/li&gt;
&lt;li&gt;tests,&lt;/li&gt;
&lt;li&gt;styles,&lt;/li&gt;
&lt;li&gt;accessibility semantics,&lt;/li&gt;
&lt;li&gt;interaction behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because cross-platform does not mean identical source code.&lt;/p&gt;

&lt;p&gt;Web may use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DOM
CSS
keyboard events
focus APIs
ARIA
portals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React Native may use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pressable
TextInput
native accessibility props
touch interaction
native styles
Modal / native presentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generator should preserve &lt;strong&gt;shared intent&lt;/strong&gt; without pretending that both runtimes are the same environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate the surrounding product surface
&lt;/h2&gt;

&lt;p&gt;Once the plan is valid, generation can produce much more than the runtime component.&lt;/p&gt;

&lt;p&gt;Depending on the profile, the output may include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;types.ts
index.ts
Component.tsx
Component.test.tsx
Component.stories.tsx
test contracts
styles
compound parts
metadata
docs contracts
component token files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can also update existing repository-owned surfaces such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;layer exports
package root exports
public API contracts
metadata registry
docs registry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the component pipeline can hand off to the website component-page generator to create things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;usage,&lt;/li&gt;
&lt;li&gt;examples,&lt;/li&gt;
&lt;li&gt;accessibility sections,&lt;/li&gt;
&lt;li&gt;API data,&lt;/li&gt;
&lt;li&gt;playgrounds,&lt;/li&gt;
&lt;li&gt;React demos,&lt;/li&gt;
&lt;li&gt;React Native demos,&lt;/li&gt;
&lt;li&gt;catalog registration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point it is no longer just a file generator.&lt;/p&gt;

&lt;p&gt;It is encoding part of the &lt;strong&gt;production lifecycle&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Public exports should not depend on memory
&lt;/h2&gt;

&lt;p&gt;Exports are an easy thing to forget.&lt;/p&gt;

&lt;p&gt;A component can work perfectly inside its own directory and still be unusable to consumers because the package root never exposes it.&lt;/p&gt;

&lt;p&gt;So generation should move through the whole public boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component
    ↓
layer export
    ↓
package root export
    ↓
public API contract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same principle applies to metadata and documentation registries.&lt;/p&gt;

&lt;p&gt;If a repeatable registration step is required every time, it is a good candidate for deterministic generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate test requirements, not empty test files
&lt;/h2&gt;

&lt;p&gt;An empty generated test file is not much of an improvement.&lt;/p&gt;

&lt;p&gt;The baseline test contract should come from the same component intent.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;profile
+ capabilities
+ platform
        ↓
baseline test contract
        ↓
generated test expectations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A form control and a compound navigation component should not receive the same baseline.&lt;/p&gt;

&lt;p&gt;For example, a form control may need evidence for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controlled
uncontrolled
disabled
required
invalid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while a compound navigation component may care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;keyboard
focus-management
controlled
uncontrolled
compound-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generator and the test system should describe the same component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Regeneration needs ownership boundaries
&lt;/h2&gt;

&lt;p&gt;First-time generation is easy compared with regeneration.&lt;/p&gt;

&lt;p&gt;Once humans have added meaningful engineering work, &lt;code&gt;--force&lt;/code&gt; cannot simply mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delete everything
generate everything again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;unless every file is truly generator-owned.&lt;/p&gt;

&lt;p&gt;A safer model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generator-owned surface
→ may be regenerated

manual extension / semantic engineering
→ must be preserved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction is critical.&lt;/p&gt;

&lt;p&gt;If developers cannot trust regeneration, they stop using it.&lt;/p&gt;

&lt;p&gt;Once that happens, generated surfaces begin drifting permanently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fail before writing
&lt;/h2&gt;

&lt;p&gt;A generator touching many parts of a monorepo should validate the plan before mutation.&lt;/p&gt;

&lt;p&gt;Preflight can check things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;required barrels exist,&lt;/li&gt;
&lt;li&gt;registries are structurally valid,&lt;/li&gt;
&lt;li&gt;profile/parts combinations are valid,&lt;/li&gt;
&lt;li&gt;requested canonical resources exist,&lt;/li&gt;
&lt;li&gt;target paths do not conflict,&lt;/li&gt;
&lt;li&gt;overwrite was explicitly authorized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A bad plan should fail before it leaves a half-generated component behind.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This becomes even more important when one generation operation updates multiple packages and registries.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;--dry-run&lt;/code&gt; is a safety feature
&lt;/h2&gt;

&lt;p&gt;A generator with broad repository reach should be previewable.&lt;/p&gt;

&lt;p&gt;Vellira supports a dry-run mode so you can inspect things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;which packages will change?
which files will be created?
which registries will be updated?
which platforms were resolved?
which profile did the intent select?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before any write happens.&lt;/p&gt;

&lt;p&gt;That makes generation reviewable.&lt;/p&gt;

&lt;p&gt;And reviewability is part of safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idempotency is part of the contract
&lt;/h2&gt;

&lt;p&gt;A deterministic generator should converge.&lt;/p&gt;

&lt;p&gt;Run the same authorized generation twice and the second run should not create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duplicate exports,&lt;/li&gt;
&lt;li&gt;duplicate registry entries,&lt;/li&gt;
&lt;li&gt;reordered files,&lt;/li&gt;
&lt;li&gt;formatting churn,&lt;/li&gt;
&lt;li&gt;unrelated changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That requires stable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ordering,&lt;/li&gt;
&lt;li&gt;formatting,&lt;/li&gt;
&lt;li&gt;serialization,&lt;/li&gt;
&lt;li&gt;registration logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Generated surfaces should also support check mode.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm create:component-page &amp;lt;Component&amp;gt; &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can verify that checked-in generated output still matches its canonical input without rewriting it.&lt;/p&gt;

&lt;p&gt;This gives CI a way to catch generated drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downstream generators should still have boundaries
&lt;/h2&gt;

&lt;p&gt;One deterministic tool can call another.&lt;/p&gt;

&lt;p&gt;But that does not mean it should blindly trust every mutation.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component generator
       ↓
website component-page generator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be a useful composition.&lt;/p&gt;

&lt;p&gt;But the orchestrating layer should still know which artifacts are expected to change and detect unexpected disappearance or corruption.&lt;/p&gt;

&lt;p&gt;Automation should compose contracts, not remove them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the generator should not decide
&lt;/h2&gt;

&lt;p&gt;This may be the most important boundary.&lt;/p&gt;

&lt;p&gt;The generator should &lt;strong&gt;not&lt;/strong&gt; try to finish product design.&lt;/p&gt;

&lt;p&gt;A compound scaffold can create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root
List
Trigger
Content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but it should not invent every interaction decision for every future compound component.&lt;/p&gt;

&lt;p&gt;An overlay profile can establish structure.&lt;/p&gt;

&lt;p&gt;It should not pretend to solve every focus, dismissal, presentation, and accessibility edge case.&lt;/p&gt;

&lt;p&gt;A form-control profile can establish state foundations.&lt;/p&gt;

&lt;p&gt;It should not decide every future Input-like API.&lt;/p&gt;

&lt;p&gt;A useful split is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deterministic generator owns
--------------------------------
repository placement
repeatable file structure
exports and registrations
baseline contracts
starter tests
starter stories
metadata scaffolding
generated docs/page plumbing
overwrite/check rules

component engineering owns
--------------------------------
final product semantics
nuanced API decisions
complex accessibility behavior
runtime interaction details
visual refinement
important edge cases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first category exists to give engineers more time for the second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix systemic gaps once
&lt;/h2&gt;

&lt;p&gt;This is where a production generator becomes really valuable.&lt;/p&gt;

&lt;p&gt;Suppose several components reveal the same missing export behavior.&lt;/p&gt;

&lt;p&gt;You can repair each component manually.&lt;/p&gt;

&lt;p&gt;Or you can fix the generator once.&lt;/p&gt;

&lt;p&gt;The same applies to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests
Storybook
metadata
docs
website pages
tokens
platform templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every repeated failure is a chance to improve the production path.&lt;/p&gt;

&lt;p&gt;That changes how the design system grows.&lt;/p&gt;

&lt;p&gt;The next component inherits the fix automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The generator is not primarily a typing shortcut
&lt;/h2&gt;

&lt;p&gt;That is probably the biggest lesson from building this.&lt;/p&gt;

&lt;p&gt;A serious component generator is a way to encode the &lt;strong&gt;repeatable production contract&lt;/strong&gt; of a component library.&lt;/p&gt;

&lt;p&gt;For Vellira, that contract now connects things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;intent
  ↓
generation plan
  ↓
React / React Native targets
  ↓
implementation + public surface
  ↓
tests + stories + metadata + docs
  ↓
validation
  ↓
review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component implementation still matters.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;Component.tsx&lt;/code&gt; no longer has to carry the entire meaning of “done.”&lt;/p&gt;

&lt;p&gt;That is the real value of the generator.&lt;/p&gt;

&lt;p&gt;It moves predictable repository work out of human memory and into a system that can be validated, repeated, and improved once for every component that comes after it.&lt;/p&gt;




&lt;p&gt;Vellira is an open-source React and React Native design system being built in public.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.vellira.dev" rel="noopener noreferrer"&gt;Explore the documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storybook.vellira.dev" rel="noopener noreferrer"&gt;Browse Storybook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vellira-dev/vellira" rel="noopener noreferrer"&gt;Follow Vellira on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>designsystem</category>
      <category>typescript</category>
      <category>react</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Why We Built Quality Gates Before Growing to 20 Components</title>
      <dc:creator>Vellira</dc:creator>
      <pubDate>Mon, 28 Sep 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/vellira/why-we-built-quality-gates-before-growing-to-20-components-439i</link>
      <guid>https://dev.to/vellira/why-we-built-quality-gates-before-growing-to-20-components-439i</guid>
      <description>&lt;p&gt;A component library can look healthy while quietly accumulating consistency debt.&lt;/p&gt;

&lt;p&gt;The build is green.&lt;/p&gt;

&lt;p&gt;The package compiles.&lt;/p&gt;

&lt;p&gt;Storybook opens.&lt;/p&gt;

&lt;p&gt;The component renders in React and React Native.&lt;/p&gt;

&lt;p&gt;Then somebody notices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the public export is missing,&lt;/li&gt;
&lt;li&gt;the website API is stale,&lt;/li&gt;
&lt;li&gt;Storybook only covers the happy path,&lt;/li&gt;
&lt;li&gt;the component claims keyboard support without deterministic evidence,&lt;/li&gt;
&lt;li&gt;or the native implementation does not actually match the declared contract.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those failures necessarily breaks compilation.&lt;/p&gt;

&lt;p&gt;That is why, before rapidly growing Vellira's component catalog toward 20 components, we invested in deterministic &lt;strong&gt;completeness and quality gates&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal was not more infrastructure.&lt;/p&gt;

&lt;p&gt;The goal was &lt;strong&gt;cheaper trust&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component count is a misleading progress metric
&lt;/h2&gt;

&lt;p&gt;A design system can grow from 10 components to 20 and become less reliable.&lt;/p&gt;

&lt;p&gt;Component count tells you very little about whether those components are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exported correctly,&lt;/li&gt;
&lt;li&gt;documented,&lt;/li&gt;
&lt;li&gt;tested,&lt;/li&gt;
&lt;li&gt;accessible,&lt;/li&gt;
&lt;li&gt;represented in Storybook,&lt;/li&gt;
&lt;li&gt;aligned across platforms,&lt;/li&gt;
&lt;li&gt;using canonical design resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A production component is usually much more than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Component.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It may also require:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation
public types
package exports
tests
Storybook
website documentation
examples
API documentation
accessibility evidence
metadata
tokens
platform-specific validation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So adding ten components does not add ten things.&lt;/p&gt;

&lt;p&gt;It can add roughly a hundred new surfaces capable of drifting.&lt;/p&gt;

&lt;p&gt;A more useful question is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How many components do we have?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How many component contracts can the repository actually prove are complete?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A green build proves less than it feels like it proves
&lt;/h2&gt;

&lt;p&gt;Build success matters.&lt;/p&gt;

&lt;p&gt;But a compiler or bundler mostly tells you that the code can be transformed into valid output.&lt;/p&gt;

&lt;p&gt;It usually does not tell you that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the intended component is exported from the package root,&lt;/li&gt;
&lt;li&gt;a public &lt;code&gt;Props&lt;/code&gt; contract exists,&lt;/li&gt;
&lt;li&gt;controlled and uncontrolled behavior are actually tested,&lt;/li&gt;
&lt;li&gt;disabled or invalid states have executable evidence,&lt;/li&gt;
&lt;li&gt;Storybook exposes representative states,&lt;/li&gt;
&lt;li&gt;component documentation is current,&lt;/li&gt;
&lt;li&gt;React Native has platform-appropriate accessibility semantics,&lt;/li&gt;
&lt;li&gt;an overlay actually has presentation behavior on both runtimes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A design system has a much larger product surface than its build graph.&lt;/p&gt;

&lt;p&gt;That is why ordinary build validation is necessary but insufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Completeness and quality are different questions
&lt;/h2&gt;

&lt;p&gt;This distinction became important in Vellira.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Completeness&lt;/strong&gt; asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did we ship every repository surface required by this component?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Quality&lt;/strong&gt; asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do those surfaces contain credible evidence for the contract we claim?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are not the same thing.&lt;/p&gt;

&lt;p&gt;A test file can exist and test almost nothing.&lt;/p&gt;

&lt;p&gt;A Storybook file can exist while showing only &lt;code&gt;Default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Documentation can exist while containing stale or generic content.&lt;/p&gt;

&lt;p&gt;A component can have an implementation but still be missing the public API consumers expect.&lt;/p&gt;

&lt;p&gt;So we keep the two layers separate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Completeness: is the whole component actually there?
&lt;/h2&gt;

&lt;p&gt;Vellira's completeness checks begin from canonical component metadata.&lt;/p&gt;

&lt;p&gt;For a component, the contract may say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;platforms: React + React Native

tests: required
Storybook: required
docs: required
accessibility: required
tokens: required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository can then derive what should exist.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component contract
        ↓
expected repository surfaces
        ↓
actual repository
        ↓
COMPLETE / INCOMPLETE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If React Native is declared but the native implementation does not exist, that should not depend on somebody noticing during review.&lt;/p&gt;

&lt;p&gt;It should fail deterministically.&lt;/p&gt;

&lt;p&gt;The same applies to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;types,&lt;/li&gt;
&lt;li&gt;exports,&lt;/li&gt;
&lt;li&gt;tests,&lt;/li&gt;
&lt;li&gt;stories,&lt;/li&gt;
&lt;li&gt;documentation,&lt;/li&gt;
&lt;li&gt;accessibility surfaces,&lt;/li&gt;
&lt;li&gt;token requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quality: does the evidence support the claim?
&lt;/h2&gt;

&lt;p&gt;Existence alone is not enough.&lt;/p&gt;

&lt;p&gt;Suppose a form component declares:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controlled
uncontrolled
disabled
required
invalid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single happy-path test technically satisfies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test file exists
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it does not provide evidence for those behaviors.&lt;/p&gt;

&lt;p&gt;A quality gate can ask for something stronger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controlled behavior → evidence
uncontrolled behavior → evidence
disabled behavior → evidence
invalid behavior → evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the distinction becomes useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Completeness:
Does the required test surface exist?

Quality:
Does that test surface prove important declared behavior?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same idea applies elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storybook is a product surface too
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;*.stories.tsx&lt;/code&gt; file existing is not automatically useful coverage.&lt;/p&gt;

&lt;p&gt;Imagine a component that supports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;default
disabled
loading
invalid
controlled
uncontrolled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but Storybook shows only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, Storybook coverage exists.&lt;/p&gt;

&lt;p&gt;Practically, the observable component contract is poorly represented.&lt;/p&gt;

&lt;p&gt;So representative states matter too.&lt;/p&gt;

&lt;p&gt;Not every missing story needs to block CI — severity can differ — but the system should at least know the difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;story file exists
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;important states are visible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Documentation can drift independently of code
&lt;/h2&gt;

&lt;p&gt;This is another place where green builds provide very little protection.&lt;/p&gt;

&lt;p&gt;A component implementation can be correct while:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;its website page is stale,&lt;/li&gt;
&lt;li&gt;generated API information is outdated,&lt;/li&gt;
&lt;li&gt;the accessibility section is missing,&lt;/li&gt;
&lt;li&gt;React Native differences are undocumented,&lt;/li&gt;
&lt;li&gt;component-page registration is duplicated or broken.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Generated documentation does not eliminate this problem.&lt;/p&gt;

&lt;p&gt;Generated output can become stale too.&lt;/p&gt;

&lt;p&gt;So a useful pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generate
   ↓
check freshness
   ↓
audit resulting structure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generation reduces manual work.&lt;/p&gt;

&lt;p&gt;Validation prevents generated output from becoming another silent source of drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Public APIs deserve their own checks
&lt;/h2&gt;

&lt;p&gt;Design systems are libraries.&lt;/p&gt;

&lt;p&gt;That makes package boundaries part of the product.&lt;/p&gt;

&lt;p&gt;An implementation might work inside the monorepo while consumers still cannot import it correctly.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation exists
        ↓
internal imports work
        ↓
package root export missing
        ↓
consumer cannot use component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build may still succeed.&lt;/p&gt;

&lt;p&gt;So exports and public type contracts need explicit validation.&lt;/p&gt;

&lt;p&gt;For reusable libraries, the public API is not incidental infrastructure.&lt;/p&gt;

&lt;p&gt;It is part of the product itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-platform components need independent evidence
&lt;/h2&gt;

&lt;p&gt;This becomes especially important with React and React Native.&lt;/p&gt;

&lt;p&gt;Shared semantics do not imply shared implementation evidence.&lt;/p&gt;

&lt;p&gt;For accessibility, Web might rely on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;semantic HTML
roles
ARIA attributes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while React Native uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;accessibilityRole
accessibilityLabel
accessibilityState
accessibilityHint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful quality system should understand that these are different implementations of related product responsibilities.&lt;/p&gt;

&lt;p&gt;The same is true for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keyboard interaction,&lt;/li&gt;
&lt;li&gt;focus management,&lt;/li&gt;
&lt;li&gt;gestures,&lt;/li&gt;
&lt;li&gt;overlays,&lt;/li&gt;
&lt;li&gt;presentation mechanisms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cross-platform validation should preserve shared intent while checking each runtime honestly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overlays expose weak parity very quickly
&lt;/h2&gt;

&lt;p&gt;Consider an overlay-like component.&lt;/p&gt;

&lt;p&gt;The contract may say that it has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controlled state
uncontrolled state
focus management
keyboard behavior
compound API
portal/presentation behavior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the Web, presentation might involve a portal.&lt;/p&gt;

&lt;p&gt;On React Native, it may involve &lt;code&gt;Modal&lt;/code&gt; or another platform-native mechanism.&lt;/p&gt;

&lt;p&gt;The important requirement is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the native source code resemble the web source code?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does each runtime provide credible evidence for the same product responsibility?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why platform-aware gates matter.&lt;/p&gt;

&lt;p&gt;A shared TypeScript API alone cannot prove runtime parity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small deterministic checks compose better
&lt;/h2&gt;

&lt;p&gt;One thing we deliberately avoid is one enormous:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;validate-everything
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;script.&lt;/p&gt;

&lt;p&gt;Instead, different contracts answer different questions.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build
→ can this package be produced?

public API
→ can consumers access what we claim?

completeness
→ are all required surfaces present?

quality
→ is there credible evidence for the declared contract?

docs audit
→ is the public documentation structurally valid?

tests
→ does runtime behavior match expectations?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When something fails, the failure should explain the actual problem.&lt;/p&gt;

&lt;p&gt;That makes the system easier to debug and easier to evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured findings are more useful than log text
&lt;/h2&gt;

&lt;p&gt;Once a quality rule represents a stable engineering judgment, its result should be reusable.&lt;/p&gt;

&lt;p&gt;Instead of only printing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;something is wrong with Select
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a structured finding can contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rule id
dimension
severity
platform
status
message
evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can later support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI output,&lt;/li&gt;
&lt;li&gt;review summaries,&lt;/li&gt;
&lt;li&gt;issue synchronization,&lt;/li&gt;
&lt;li&gt;historical comparison,&lt;/li&gt;
&lt;li&gt;automated routing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule stays deterministic.&lt;/p&gt;

&lt;p&gt;Different consumers can decide how to present its result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate memory, not engineering judgment
&lt;/h2&gt;

&lt;p&gt;There is an important limit here.&lt;/p&gt;

&lt;p&gt;A checker can verify that a &lt;code&gt;Props&lt;/code&gt; contract exists.&lt;/p&gt;

&lt;p&gt;It cannot prove that every prop is well designed.&lt;/p&gt;

&lt;p&gt;A checker can detect accessibility semantics.&lt;/p&gt;

&lt;p&gt;It cannot prove every real user experience is accessible.&lt;/p&gt;

&lt;p&gt;A Storybook rule can detect representative states.&lt;/p&gt;

&lt;p&gt;It cannot decide whether the examples explain the component well.&lt;/p&gt;

&lt;p&gt;A documentation audit can reject empty descriptions.&lt;/p&gt;

&lt;p&gt;It cannot write excellent documentation.&lt;/p&gt;

&lt;p&gt;So the target is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;automate component quality completely&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;automate repeated deterministic invariants so humans can spend review time on the decisions that actually require judgment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That boundary matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix repeated failures at the system level
&lt;/h2&gt;

&lt;p&gt;This is where quality gates become especially valuable.&lt;/p&gt;

&lt;p&gt;Imagine five form controls all reach review without invalid-state coverage.&lt;/p&gt;

&lt;p&gt;You could fix five test files.&lt;/p&gt;

&lt;p&gt;Or you could ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why could five components reach this point with the same omission?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe the reusable fix belongs in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generator templates
metadata
test contracts
quality rules
documentation generation
CI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then every future component benefits.&lt;/p&gt;

&lt;p&gt;That is the difference between fixing a component and fixing the component-production system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a bounded target like 20 components helps
&lt;/h2&gt;

&lt;p&gt;There is nothing magical about the number 20.&lt;/p&gt;

&lt;p&gt;The useful part is the constraint.&lt;/p&gt;

&lt;p&gt;A bounded catalog gives enough variety to exercise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;primitives
form controls
compound APIs
navigation
overlays
React
React Native
controlled state
keyboard interaction
focus management
tokens
icons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while the repository is still small enough to audit completely.&lt;/p&gt;

&lt;p&gt;That creates a good moment to strengthen the production system.&lt;/p&gt;

&lt;p&gt;If you grow the catalog first and introduce quality contracts later, every new rule may uncover years of historical inconsistency.&lt;/p&gt;

&lt;p&gt;It is much cheaper to establish the rules while the system is still manageable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality gates are cheapest before you desperately need them
&lt;/h2&gt;

&lt;p&gt;This is similar to database constraints.&lt;/p&gt;

&lt;p&gt;Adding a constraint before millions of invalid rows exist is much easier than retrofitting one afterward.&lt;/p&gt;

&lt;p&gt;For a design system, early gates establish expectations around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;public APIs,&lt;/li&gt;
&lt;li&gt;testing,&lt;/li&gt;
&lt;li&gt;Storybook,&lt;/li&gt;
&lt;li&gt;documentation,&lt;/li&gt;
&lt;li&gt;accessibility,&lt;/li&gt;
&lt;li&gt;platform parity,&lt;/li&gt;
&lt;li&gt;metadata,&lt;/li&gt;
&lt;li&gt;design resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New components then enter an existing definition of done.&lt;/p&gt;

&lt;p&gt;They do not invent their own.&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal is cheaper trust
&lt;/h2&gt;

&lt;p&gt;Quality gates can sound restrictive.&lt;/p&gt;

&lt;p&gt;I think their more important role is the opposite.&lt;/p&gt;

&lt;p&gt;They reduce the amount of repeated verification humans need to perform.&lt;/p&gt;

&lt;p&gt;When another component enters the catalog, review should not begin by rediscovering whether:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exports matter,&lt;/li&gt;
&lt;li&gt;tests matter,&lt;/li&gt;
&lt;li&gt;accessibility matters,&lt;/li&gt;
&lt;li&gt;documentation matters,&lt;/li&gt;
&lt;li&gt;Storybook matters,&lt;/li&gt;
&lt;li&gt;platform evidence matters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those expectations should already be part of the system.&lt;/p&gt;

&lt;p&gt;Human review should focus on what is actually new about the component.&lt;/p&gt;

&lt;p&gt;That is why we built the gates before aggressively growing the Vellira catalog.&lt;/p&gt;

&lt;p&gt;Not because infrastructure is more important than components.&lt;/p&gt;

&lt;p&gt;Because components become easier to ship confidently when the surrounding system can prove the repeated parts of production readiness.&lt;/p&gt;

&lt;p&gt;The goal is not maximum automation.&lt;/p&gt;

&lt;p&gt;The goal is &lt;strong&gt;more components without multiplying uncertainty at the same rate&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Vellira is an open-source React and React Native design system being built in public.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.vellira.dev" rel="noopener noreferrer"&gt;Explore the documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storybook.vellira.dev" rel="noopener noreferrer"&gt;Browse Storybook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vellira-dev/vellira" rel="noopener noreferrer"&gt;Follow Vellira on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>designsystem</category>
      <category>testing</category>
      <category>ci</category>
      <category>react</category>
    </item>
    <item>
      <title>Component Metadata as a Source of Truth for a Design System</title>
      <dc:creator>Vellira</dc:creator>
      <pubDate>Fri, 25 Sep 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/vellira/component-metadata-as-a-source-of-truth-for-a-design-system-3k2a</link>
      <guid>https://dev.to/vellira/component-metadata-as-a-source-of-truth-for-a-design-system-3k2a</guid>
      <description>&lt;p&gt;A design system starts drifting long before anyone notices a broken component.&lt;/p&gt;

&lt;p&gt;The first warning signs are usually quieter.&lt;/p&gt;

&lt;p&gt;The website says a component supports React Native, but the native package does not export it.&lt;/p&gt;

&lt;p&gt;Storybook has a story, but the component page is missing.&lt;/p&gt;

&lt;p&gt;One checker assumes tests are required while another does not know the component is supposed to support keyboard interaction.&lt;/p&gt;

&lt;p&gt;A generator creates a component, but someone still has to remember to update several registries by hand.&lt;/p&gt;

&lt;p&gt;None of those failures starts inside the component implementation itself.&lt;/p&gt;

&lt;p&gt;They start because the same facts are being maintained in too many places.&lt;/p&gt;

&lt;p&gt;In Vellira, we address that with &lt;strong&gt;machine-readable component metadata&lt;/strong&gt;: one canonical registry describing stable product and tooling facts about every component.&lt;/p&gt;

&lt;p&gt;Generators, documentation tooling, completeness checks, and quality validation can then consume the same contract instead of rediscovering those facts independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hidden cost of five component lists
&lt;/h2&gt;

&lt;p&gt;Imagine maintaining separate lists for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package exports
website catalog
Storybook expectations
documentation requirements
quality checks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With five components, that may be fine.&lt;/p&gt;

&lt;p&gt;With twenty, every component starts carrying more context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name
layer
category
platforms
status
capabilities
dependencies
required engineering surfaces
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A component may be Web-only.&lt;/p&gt;

&lt;p&gt;Another may support React and React Native.&lt;/p&gt;

&lt;p&gt;A form control needs required and invalid states.&lt;/p&gt;

&lt;p&gt;A navigation component needs keyboard behavior.&lt;/p&gt;

&lt;p&gt;An overlay may require focus management and a portal.&lt;/p&gt;

&lt;p&gt;Once those facts exist in several places, drift becomes almost inevitable.&lt;/p&gt;

&lt;p&gt;Every change becomes several opportunities to forget something.&lt;/p&gt;

&lt;p&gt;Automation helps.&lt;/p&gt;

&lt;p&gt;But automation still needs an authority.&lt;/p&gt;

&lt;p&gt;That is where metadata becomes useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  What belongs in component metadata?
&lt;/h2&gt;

&lt;p&gt;A simplified version of Vellira's component contract looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ComponentMetadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primitives&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;patterns&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;action&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feedback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;layout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utility&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;readonly &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;)[];&lt;/span&gt;

  &lt;span class="nl"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;compound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;experimental&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deprecated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;ComponentCapability&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nl"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ComponentDependencies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentRequirements&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Capabilities describe reusable facts such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controlled
uncontrolled
disabled
required
invalid
loading
keyboard
focus-management
compound-api
portal
responsive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requirements describe which engineering surfaces a component must have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tests,&lt;/li&gt;
&lt;li&gt;Storybook,&lt;/li&gt;
&lt;li&gt;documentation,&lt;/li&gt;
&lt;li&gt;accessibility evidence,&lt;/li&gt;
&lt;li&gt;tokens,&lt;/li&gt;
&lt;li&gt;icons,&lt;/li&gt;
&lt;li&gt;component-specific token contracts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is what metadata &lt;strong&gt;does not&lt;/strong&gt; contain.&lt;/p&gt;

&lt;p&gt;It is not documentation prose.&lt;/p&gt;

&lt;p&gt;It is not an implementation.&lt;/p&gt;

&lt;p&gt;It is a compact collection of facts that tooling can make decisions from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different components should produce different contracts
&lt;/h2&gt;

&lt;p&gt;A useful metadata model should represent real differences without creating a special schema for every component.&lt;/p&gt;

&lt;p&gt;Consider a few examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Button
&lt;/h3&gt;

&lt;p&gt;Button has a relatively small contract.&lt;/p&gt;

&lt;p&gt;It supports React and React Native and has capabilities such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disabled
loading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not need the same behavioral model as an overlay or compound navigation component.&lt;/p&gt;

&lt;p&gt;Simple components should be allowed to have simple metadata.&lt;/p&gt;

&lt;h3&gt;
  
  
  FormField
&lt;/h3&gt;

&lt;p&gt;FormField belongs to a different architectural layer.&lt;/p&gt;

&lt;p&gt;It is a &lt;code&gt;pattern&lt;/code&gt;, belongs to the &lt;code&gt;form&lt;/code&gt; category, and uses a &lt;code&gt;form-control&lt;/code&gt; profile.&lt;/p&gt;

&lt;p&gt;Its capabilities include things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disabled
required
invalid
compound-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells tooling something important without forcing it to infer architecture from the name &lt;code&gt;FormField&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tabs
&lt;/h3&gt;

&lt;p&gt;Tabs declares things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controlled
uncontrolled
keyboard
focus-management
compound-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A checker should not need code like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if component === "Tabs":
  remember keyboard tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component declares the capability once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modal
&lt;/h3&gt;

&lt;p&gt;An overlay can declare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controlled
uncontrolled
keyboard
focus-management
compound-api
portal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That says focus management and portal behavior belong to the component contract.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; try to describe the exact focus algorithm.&lt;/p&gt;

&lt;p&gt;That remains implementation work.&lt;/p&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata should be a contract, not just configuration
&lt;/h2&gt;

&lt;p&gt;A TypeScript interface is not enough.&lt;/p&gt;

&lt;p&gt;The values themselves need validation.&lt;/p&gt;

&lt;p&gt;Otherwise something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;focus-managment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can silently become a new accidental capability because somebody misspelled &lt;code&gt;focus-management&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So Vellira validates things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;supported platforms,&lt;/li&gt;
&lt;li&gt;supported layers,&lt;/li&gt;
&lt;li&gt;supported categories,&lt;/li&gt;
&lt;li&gt;supported profiles,&lt;/li&gt;
&lt;li&gt;lifecycle statuses,&lt;/li&gt;
&lt;li&gt;capability names,&lt;/li&gt;
&lt;li&gt;duplicate values,&lt;/li&gt;
&lt;li&gt;dependency structure,&lt;/li&gt;
&lt;li&gt;required engineering fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Invalid metadata fails before downstream tooling starts making assumptions from it.&lt;/p&gt;

&lt;p&gt;That changes the mental model.&lt;/p&gt;

&lt;p&gt;Metadata is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here is some configuration that probably describes the component.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here is the contract downstream tooling is allowed to trust.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  One registry gives every tool the same starting point
&lt;/h2&gt;

&lt;p&gt;Individual components can keep separate metadata files for reviewability.&lt;/p&gt;

&lt;p&gt;But tooling needs one deterministic registry.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Button.metadata.ts
Select.metadata.ts
Tabs.metadata.ts
Modal.metadata.ts
...
        ↓
componentMetadata registry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumers start from the same registry rather than independently scanning directories.&lt;/p&gt;

&lt;p&gt;That gives us an important distinction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Directory scanning tells you what happens to exist.&lt;/p&gt;

&lt;p&gt;Metadata tells you what is supposed to exist.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are not the same question.&lt;/p&gt;

&lt;p&gt;A half-generated component can leave files on disk.&lt;/p&gt;

&lt;p&gt;A removed export can leave an implementation behind.&lt;/p&gt;

&lt;p&gt;File presence alone is not enough to describe the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generation should create the contract too
&lt;/h2&gt;

&lt;p&gt;A source of truth is much more useful when adding a component does not require manually remembering to update it.&lt;/p&gt;

&lt;p&gt;In Vellira, component generation includes metadata in the generation plan.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component intent
      ↓
generation plan
      ↓
implementation
types
tests
stories
      ↓
Component.metadata.ts
      ↓
canonical registry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Registration needs to be deterministic too.&lt;/p&gt;

&lt;p&gt;Repeated generation should not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duplicate entries,&lt;/li&gt;
&lt;li&gt;randomly reorder imports,&lt;/li&gt;
&lt;li&gt;create unrelated diffs,&lt;/li&gt;
&lt;li&gt;leave partially registered components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why generator quality is not just about generating files.&lt;/p&gt;

&lt;p&gt;It is about producing stable repository state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Completeness checks become product-aware
&lt;/h2&gt;

&lt;p&gt;This is where metadata starts paying for itself.&lt;/p&gt;

&lt;p&gt;Instead of maintaining another list of known components, a completeness checker can read the canonical registry.&lt;/p&gt;

&lt;p&gt;The metadata says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this component supports React
this component supports React Native
tests are required
Storybook is required
docs are required
accessibility evidence is required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The checker then compares those promises with the repository.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metadata
   ↓
what should exist?
   ↓
repository
   ↓
does it actually exist?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If React Native support is declared but the native implementation is missing, the result is not a reviewer eventually noticing it.&lt;/p&gt;

&lt;p&gt;It becomes deterministic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;react-native declared
        ↓
native implementation expected
        ↓
implementation missing
        ↓
INCOMPLETE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same pattern works for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;public exports,&lt;/li&gt;
&lt;li&gt;tests,&lt;/li&gt;
&lt;li&gt;stories,&lt;/li&gt;
&lt;li&gt;docs,&lt;/li&gt;
&lt;li&gt;accessibility surfaces,&lt;/li&gt;
&lt;li&gt;tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This removes a lot of memory work from review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality checks can consume the same facts
&lt;/h2&gt;

&lt;p&gt;Completeness and quality are different problems.&lt;/p&gt;

&lt;p&gt;Completeness asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are all required surfaces present?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quality asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the implementation satisfy our engineering rules?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They should remain separate systems.&lt;/p&gt;

&lt;p&gt;But they can share the same component facts.&lt;/p&gt;

&lt;p&gt;A quality engine can receive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component metadata
selected platform
repository root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and decide which checks are applicable.&lt;/p&gt;

&lt;p&gt;A component that does not support React Native should not accidentally run native-specific checks.&lt;/p&gt;

&lt;p&gt;A component declaring keyboard behavior can participate in keyboard-related validation without every rule maintaining its own list of component names.&lt;/p&gt;

&lt;p&gt;That is exactly the kind of reuse metadata is good at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source of truth does not mean source of everything
&lt;/h2&gt;

&lt;p&gt;This boundary matters a lot.&lt;/p&gt;

&lt;p&gt;Metadata should &lt;strong&gt;not&lt;/strong&gt; contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every public prop,&lt;/li&gt;
&lt;li&gt;every event sequence,&lt;/li&gt;
&lt;li&gt;CSS,&lt;/li&gt;
&lt;li&gt;React Native styles,&lt;/li&gt;
&lt;li&gt;complete accessibility implementation,&lt;/li&gt;
&lt;li&gt;documentation prose,&lt;/li&gt;
&lt;li&gt;test cases,&lt;/li&gt;
&lt;li&gt;visual design decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;keyboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in metadata means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keyboard behavior belongs to this component's contract.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It does not mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keyboard behavior has been proven correct.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tests and validation still need to provide that evidence.&lt;/p&gt;

&lt;p&gt;Likewise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docs: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means documentation is required.&lt;/p&gt;

&lt;p&gt;The documentation itself still belongs to the documentation system.&lt;/p&gt;

&lt;p&gt;If metadata tries to describe every runtime detail, it eventually becomes another programming language.&lt;/p&gt;

&lt;p&gt;At that point it stops reducing complexity and starts creating it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid component-name heuristics
&lt;/h2&gt;

&lt;p&gt;This is fragile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if component === "Tabs":
  run keyboard checks

if component === "Modal":
  expect portal behavior

if component === "FormField":
  treat as pattern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Names are product identity.&lt;/p&gt;

&lt;p&gt;They should not become the architecture of every tool.&lt;/p&gt;

&lt;p&gt;A stronger model asks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Does it declare keyboard behavior?
Does it declare portal behavior?
Which layer does it belong to?
Which profile does it use?
Which platforms does it support?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means a future component can participate in existing tooling without adding another hardcoded condition.&lt;/p&gt;

&lt;p&gt;This is one of the biggest benefits of a reusable capability vocabulary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata turns drift into evidence
&lt;/h2&gt;

&lt;p&gt;Without a canonical contract, inconsistency usually appears as a review surprise.&lt;/p&gt;

&lt;p&gt;Someone notices:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The website says this supports native, but the package doesn't.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We forgot Storybook for this component.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This overlay has no accessibility coverage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With metadata-driven tooling, the repository can identify the disagreement itself.&lt;/p&gt;

&lt;p&gt;The important transformation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;human memory
     ↓
explicit contract
     ↓
deterministic comparison
     ↓
actionable finding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tooling is not replacing engineering judgment.&lt;/p&gt;

&lt;p&gt;It is removing the things humans should not have to remember manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep independent systems independent
&lt;/h2&gt;

&lt;p&gt;Once metadata becomes useful, there is a temptation to make everything depend directly on it.&lt;/p&gt;

&lt;p&gt;That can create another kind of coupling.&lt;/p&gt;

&lt;p&gt;In Vellira, the responsibilities remain separate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component metadata
→ canonical facts

generator
→ deterministic scaffold and registration

completeness checker
→ required surface presence

quality checker
→ engineering rule evaluation

docs / website / Storybook
→ actual content and presentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They share facts.&lt;/p&gt;

&lt;p&gt;They do not become one giant subsystem.&lt;/p&gt;

&lt;p&gt;That separation lets each piece evolve without turning the metadata schema into the center of every implementation decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical rules I would use elsewhere
&lt;/h2&gt;

&lt;p&gt;If I were introducing component metadata into another design system, I would follow a few rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Centralize facts that are already duplicated
&lt;/h3&gt;

&lt;p&gt;Do not start by designing a universal ontology.&lt;/p&gt;

&lt;p&gt;Start with facts several systems already maintain independently.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use constrained vocabularies
&lt;/h3&gt;

&lt;p&gt;This:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;experimental | beta | stable | deprecated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is far more useful to automation than an unrestricted status string.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Validate before consumption
&lt;/h3&gt;

&lt;p&gt;Malformed metadata should fail before downstream tooling acts on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Keep one canonical registry
&lt;/h3&gt;

&lt;p&gt;Separate files are good for review.&lt;/p&gt;

&lt;p&gt;One deterministic registry is good for tooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Let generators register metadata automatically
&lt;/h3&gt;

&lt;p&gt;If humans still need to remember registry plumbing, there is still a memory gap.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Let requirements drive checks
&lt;/h3&gt;

&lt;p&gt;A metadata field becomes useful when something can verify it.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Prefer reusable capabilities over component names
&lt;/h3&gt;

&lt;p&gt;Model the behavior, not a growing list of exceptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Keep evidence outside the declaration
&lt;/h3&gt;

&lt;p&gt;Metadata declares expectations.&lt;/p&gt;

&lt;p&gt;Tests, docs, and implementation provide evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Treat schema changes like API changes
&lt;/h3&gt;

&lt;p&gt;Changing the vocabulary changes what tooling is allowed to assume.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Stop before metadata becomes another language
&lt;/h3&gt;

&lt;p&gt;If your schema starts describing every runtime branch and style decision, it has gone too far.&lt;/p&gt;

&lt;h2&gt;
  
  
  A source of truth is useful because reality can disagree with it
&lt;/h2&gt;

&lt;p&gt;That sounds contradictory, but it is the point.&lt;/p&gt;

&lt;p&gt;Metadata creates a stable statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This component supports React and React Native.

Tests are required.

Storybook is required.

Documentation is required.

Accessibility evidence is required.

These capabilities belong to its contract.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the repository can be compared against those statements.&lt;/p&gt;

&lt;p&gt;When implementation, docs, exports, tests, or stories disagree with the contract, the disagreement becomes detectable.&lt;/p&gt;

&lt;p&gt;Without the canonical statement, every downstream system has to guess.&lt;/p&gt;

&lt;p&gt;That is why component metadata can be much more than a catalog convenience.&lt;/p&gt;

&lt;p&gt;It becomes a compact contract between a component library and the tooling responsible for keeping that library coherent.&lt;/p&gt;

&lt;p&gt;For Vellira, the goal is not to describe everything.&lt;/p&gt;

&lt;p&gt;It is to describe stable facts once, validate them, and stop asking every downstream system to rediscover them.&lt;/p&gt;

&lt;p&gt;Vellira is an open-source React and React Native design system being built in public.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.vellira.dev" rel="noopener noreferrer"&gt;Explore the documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storybook.vellira.dev" rel="noopener noreferrer"&gt;Browse Storybook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vellira-dev/vellira" rel="noopener noreferrer"&gt;Follow Vellira on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>designsystem</category>
      <category>typescript</category>
      <category>tooling</category>
      <category>react</category>
    </item>
    <item>
      <title>One Design System, Two Runtimes: Sharing Contracts Between React and React Native</title>
      <dc:creator>Vellira</dc:creator>
      <pubDate>Wed, 23 Sep 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/vellira/one-design-system-two-runtimes-sharing-contracts-between-react-and-react-native-5eh0</link>
      <guid>https://dev.to/vellira/one-design-system-two-runtimes-sharing-contracts-between-react-and-react-native-5eh0</guid>
      <description>&lt;p&gt;A cross-platform design system sounds simple until the first real component forces the platforms apart.&lt;/p&gt;

&lt;p&gt;React and React Native can expose the same product concept — a button, checkbox, input, modal — while relying on very different runtime primitives.&lt;/p&gt;

&lt;p&gt;The web has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DOM attributes,&lt;/li&gt;
&lt;li&gt;anchors,&lt;/li&gt;
&lt;li&gt;CSS,&lt;/li&gt;
&lt;li&gt;hover,&lt;/li&gt;
&lt;li&gt;keyboard behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;React Native has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Pressable&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;native accessibility props,&lt;/li&gt;
&lt;li&gt;gesture events,&lt;/li&gt;
&lt;li&gt;native styling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trying to hide all of those differences behind one implementation usually makes the abstraction worse.&lt;/p&gt;

&lt;p&gt;In Vellira, I have been taking a different approach:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Share the semantic contract, not the runtime implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That distinction has become one of the most useful architectural rules in the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real goal is consistency, not identical code
&lt;/h2&gt;

&lt;p&gt;When building a cross-platform component library, it is easy to treat the problem mainly as code sharing.&lt;/p&gt;

&lt;p&gt;If a web button and a native button look similar, why not reuse as much implementation as possible?&lt;/p&gt;

&lt;p&gt;Because developers do not primarily consume the internal component tree.&lt;/p&gt;

&lt;p&gt;They consume the public API and its behavior.&lt;/p&gt;

&lt;p&gt;For a Button, concepts such as these should feel familiar on both platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;appearance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;size&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shape&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loading&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;disabled&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fullWidth&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iconOnly&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A developer should be able to learn those concepts once and carry that knowledge between React and React Native.&lt;/p&gt;

&lt;p&gt;The implementation underneath them does not need to be identical.&lt;/p&gt;

&lt;p&gt;A more useful definition of parity is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cross-platform parity means shared semantics where the platforms agree, plus explicit platform-specific capabilities where they do not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is much more durable than forcing two different runtimes through one abstraction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put shared semantics in a platform-neutral contract
&lt;/h2&gt;

&lt;p&gt;In Vellira, the common Button vocabulary lives in a shared type package.&lt;/p&gt;

&lt;p&gt;A simplified version looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;neutral&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;danger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonAppearance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;soft&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BaseButtonProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ButtonColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ButtonAppearance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;size&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ButtonSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;square&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rounded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;fullWidth&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;loadingText&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;iconOnly&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is nothing specifically web or native in that interface.&lt;/p&gt;

&lt;p&gt;It describes what a Vellira Button means.&lt;/p&gt;

&lt;p&gt;That shared layer gives both runtimes a stable semantic center.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;appearance="soft"&lt;/code&gt; exists on both platforms, it should represent the same design intent.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;loading&lt;/code&gt; disables interaction, that rule should remain consistent too.&lt;/p&gt;

&lt;p&gt;This is where code sharing provides the most value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let each platform extend the contract honestly
&lt;/h2&gt;

&lt;p&gt;Once the shared semantics are defined, each runtime can extend them with the capabilities developers naturally expect.&lt;/p&gt;

&lt;p&gt;On the web, a Button can expose native HTML button behavior and anchor-related properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;
  &lt;span class="kd"&gt;extends&lt;/span&gt;
    &lt;span class="nx"&gt;BaseButtonProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonHTMLAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
      &lt;span class="nx"&gt;AnchorHTMLAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLAnchorElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;target&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;download&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;iconStart&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;iconEnd&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;badge&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;shortcut&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;asChild&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React Native begins with the same shared contract, but its runtime surface is different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;
  &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;BaseButtonProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PressableProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* exclusions */&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;iconStart&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ButtonIconElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;iconEnd&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ButtonIconElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;iconSize&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GestureResponderEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;style&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StyleProp&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ViewStyle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;textStyle&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StyleProp&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TextStyle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;testID&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two APIs clearly belong to the same design system.&lt;/p&gt;

&lt;p&gt;But they are not artificially identical.&lt;/p&gt;

&lt;p&gt;A web developer still gets normal web capabilities.&lt;/p&gt;

&lt;p&gt;A React Native developer still gets normal native capabilities.&lt;/p&gt;

&lt;p&gt;The design system owns the shared semantics without becoming an abstraction tax on either platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Healthy divergence is part of a good API
&lt;/h2&gt;

&lt;p&gt;Some differences should not be normalized away.&lt;/p&gt;

&lt;p&gt;A web Button can naturally behave like an anchor.&lt;/p&gt;

&lt;p&gt;That is where &lt;code&gt;href&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt;, or &lt;code&gt;asChild&lt;/code&gt; make sense.&lt;/p&gt;

&lt;p&gt;React Native does not have an HTML anchor element.&lt;/p&gt;

&lt;p&gt;Inventing a fake &lt;code&gt;href&lt;/code&gt; prop for native just to make the signatures look symmetrical would create parity on paper while making the API less natural.&lt;/p&gt;

&lt;p&gt;The same is true in the other direction.&lt;/p&gt;

&lt;p&gt;React Native has concepts such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;StyleProp&amp;lt;ViewStyle&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;gesture events&lt;/li&gt;
&lt;li&gt;native accessibility properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wrapping every one of these in invented cross-platform names would make the library harder to understand and debug.&lt;/p&gt;

&lt;p&gt;A useful test is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this property describe product-level component behavior, or the runtime that implements it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Product-level behavior is a strong candidate for the shared contract.&lt;/p&gt;

&lt;p&gt;Runtime behavior usually belongs to the platform package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share design decisions separately from implementation
&lt;/h2&gt;

&lt;p&gt;Props are only one layer of cross-platform consistency.&lt;/p&gt;

&lt;p&gt;Things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spacing,&lt;/li&gt;
&lt;li&gt;radii,&lt;/li&gt;
&lt;li&gt;typography,&lt;/li&gt;
&lt;li&gt;colors,&lt;/li&gt;
&lt;li&gt;interaction states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;also need a common source of truth.&lt;/p&gt;

&lt;p&gt;Those decisions can live in shared tokens while being consumed differently by CSS and React Native style objects.&lt;/p&gt;

&lt;p&gt;The principle is the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Share the decision. Adapt the execution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A shared radius token does not require CSS and React Native styling to work the same way.&lt;/p&gt;

&lt;p&gt;It only requires both implementations to resolve the same design intent.&lt;/p&gt;

&lt;p&gt;That prevents “cross-platform” from turning into “lowest common denominator.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Test parity at the contract boundary
&lt;/h2&gt;

&lt;p&gt;Separate implementations introduce another risk: drift.&lt;/p&gt;

&lt;p&gt;The web Button might gain a new appearance or loading rule while React Native silently falls behind.&lt;/p&gt;

&lt;p&gt;So parity needs explicit validation.&lt;/p&gt;

&lt;p&gt;There are two kinds of confidence we care about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Contract confidence&lt;/strong&gt; — shared semantics are defined in one authoritative place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime confidence&lt;/strong&gt; — each implementation proves that those semantics behave correctly on its own platform.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I prefer this model over a huge shared component with a growing number of platform conditionals.&lt;/p&gt;

&lt;p&gt;A shared file can look beautifully DRY while hiding real platform-specific bugs.&lt;/p&gt;

&lt;p&gt;Separate runtime tests make the differences visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical three-layer model
&lt;/h2&gt;

&lt;p&gt;The architecture we are converging on in Vellira can be summarized in three layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Shared semantics
&lt;/h3&gt;

&lt;p&gt;Define the concepts developers should learn once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;size
appearance
color
loading
disabled
shape
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These belong in platform-neutral contracts.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Platform API
&lt;/h3&gt;

&lt;p&gt;Extend the shared vocabulary with runtime-native capabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Web:
href
target
DOM attributes
asChild

React Native:
onPress
accessibilityLabel
style
testID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not disguise real platform differences.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Platform implementation
&lt;/h3&gt;

&lt;p&gt;Use the native tools of each runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Web:
DOM + CSS

React Native:
Pressable + native styles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation can diverge significantly as long as the shared semantics remain trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  This changes how we design new components
&lt;/h2&gt;

&lt;p&gt;With this architecture, the first question is no longer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How can we reuse the most code?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, we ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What behavior is genuinely shared?&lt;/li&gt;
&lt;li&gt;Which names should mean the same thing everywhere?&lt;/li&gt;
&lt;li&gt;Which states need equivalent semantics?&lt;/li&gt;
&lt;li&gt;Which capabilities are platform-specific?&lt;/li&gt;
&lt;li&gt;What must be validated so parity cannot drift accidentally?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only after that does implementation reuse become interesting.&lt;/p&gt;

&lt;p&gt;Sometimes there is useful shared code below the component layer.&lt;/p&gt;

&lt;p&gt;Sometimes two mostly independent implementations are the correct answer.&lt;/p&gt;

&lt;p&gt;Both are fine if the public contract remains coherent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule I keep coming back to
&lt;/h2&gt;

&lt;p&gt;Cross-platform design systems do not need maximum code sharing.&lt;/p&gt;

&lt;p&gt;They need maximum semantic clarity.&lt;/p&gt;

&lt;p&gt;Developers should feel that React and React Native belong to the same system.&lt;/p&gt;

&lt;p&gt;They should recognize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the vocabulary,&lt;/li&gt;
&lt;li&gt;the states,&lt;/li&gt;
&lt;li&gt;the design decisions,&lt;/li&gt;
&lt;li&gt;the interaction intent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they should not have to give up the strengths of their platform to get that consistency.&lt;/p&gt;

&lt;p&gt;For Vellira, that means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Share contracts aggressively. Share implementations selectively.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is a smaller abstraction.&lt;/p&gt;

&lt;p&gt;But it is a much more durable one.&lt;/p&gt;




&lt;p&gt;Vellira is an open-source design system for React and React Native being built in public.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.vellira.dev" rel="noopener noreferrer"&gt;Explore the documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vellira-dev/vellira" rel="noopener noreferrer"&gt;Browse the source on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>reactnative</category>
      <category>designsystem</category>
      <category>typescript</category>
    </item>
    <item>
      <title>AI Can Generate More UI. Who Keeps It Consistent?</title>
      <dc:creator>Vellira</dc:creator>
      <pubDate>Sat, 19 Sep 2026 16:12:38 +0000</pubDate>
      <link>https://dev.to/vellira/ai-can-generate-more-ui-who-keeps-it-consistent-mkf</link>
      <guid>https://dev.to/vellira/ai-can-generate-more-ui-who-keeps-it-consistent-mkf</guid>
      <description>&lt;p&gt;AI can now generate UI faster than many teams can review it.&lt;/p&gt;

&lt;p&gt;Describe a component to a coding model and, a few minutes later, you may have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an implementation,&lt;/li&gt;
&lt;li&gt;types,&lt;/li&gt;
&lt;li&gt;tests,&lt;/li&gt;
&lt;li&gt;documentation,&lt;/li&gt;
&lt;li&gt;several variations,&lt;/li&gt;
&lt;li&gt;and a refactor of the surrounding code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a real productivity improvement.&lt;/p&gt;

&lt;p&gt;But it changes the bottleneck.&lt;/p&gt;

&lt;p&gt;The difficult question is becoming less:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How quickly can we create another component?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And more:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do we keep the whole UI system coherent when humans and AI can change it this quickly?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is one of the problems we are building Vellira around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generation is getting cheaper. Fragmentation is not.
&lt;/h2&gt;

&lt;p&gt;Imagine an AI-generated component that looks correct, compiles, and works in isolation.&lt;/p&gt;

&lt;p&gt;That still leaves a lot unanswered.&lt;/p&gt;

&lt;p&gt;Does the component duplicate something that already exists?&lt;/p&gt;

&lt;p&gt;Does its API follow the same conventions as similar components?&lt;/p&gt;

&lt;p&gt;Does it use the same design tokens?&lt;/p&gt;

&lt;p&gt;Does the React Native version represent the same product capability correctly?&lt;/p&gt;

&lt;p&gt;Are accessibility expectations covered?&lt;/p&gt;

&lt;p&gt;Were Storybook, documentation, exports, metadata, and tests updated with the implementation?&lt;/p&gt;

&lt;p&gt;A component can be locally correct while making the system around it less consistent.&lt;/p&gt;

&lt;p&gt;And the faster we produce UI, the faster these small differences can accumulate.&lt;/p&gt;

&lt;p&gt;You start seeing things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;similar components with slightly different APIs,&lt;/li&gt;
&lt;li&gt;local colors and spacing bypassing shared tokens,&lt;/li&gt;
&lt;li&gt;Web and React Native implementations drifting unintentionally,&lt;/li&gt;
&lt;li&gt;accessibility behavior changing between components,&lt;/li&gt;
&lt;li&gt;documentation falling behind the actual API,&lt;/li&gt;
&lt;li&gt;duplicate capabilities appearing under different names,&lt;/li&gt;
&lt;li&gt;tests encoding different expectations for equivalent behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI did not invent these problems.&lt;/p&gt;

&lt;p&gt;It increases the rate at which they can appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  A component library is necessary, but it is not enough
&lt;/h2&gt;

&lt;p&gt;A traditional design system gives you components, tokens, guidelines, and documentation.&lt;/p&gt;

&lt;p&gt;Those are still essential.&lt;/p&gt;

&lt;p&gt;But a catalog can tell you that &lt;code&gt;Button&lt;/code&gt;, &lt;code&gt;Select&lt;/code&gt;, or &lt;code&gt;Tabs&lt;/code&gt; exists.&lt;/p&gt;

&lt;p&gt;It cannot automatically answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether a new implementation should exist at all,&lt;/li&gt;
&lt;li&gt;whether it follows the established API model,&lt;/li&gt;
&lt;li&gt;whether its platform differences are intentional,&lt;/li&gt;
&lt;li&gt;whether its documentation still matches the code,&lt;/li&gt;
&lt;li&gt;whether it satisfies the same quality expectations as the rest of the system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This becomes especially important when developers and coding agents work in the same repository.&lt;/p&gt;

&lt;p&gt;A coding model can produce perfectly reasonable code that does not quite belong to the system around it.&lt;/p&gt;

&lt;p&gt;The question becomes bigger than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this component work?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this change still belong to the same product and engineering system?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The system around generation becomes more important
&lt;/h2&gt;

&lt;p&gt;Coding models are getting better quickly.&lt;/p&gt;

&lt;p&gt;We should assume they will become better at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;implementing components,&lt;/li&gt;
&lt;li&gt;modifying large codebases,&lt;/li&gt;
&lt;li&gt;writing tests,&lt;/li&gt;
&lt;li&gt;refactoring APIs,&lt;/li&gt;
&lt;li&gt;generating documentation,&lt;/li&gt;
&lt;li&gt;making coordinated multi-file changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is good.&lt;/p&gt;

&lt;p&gt;The goal should not be to slow them down.&lt;/p&gt;

&lt;p&gt;But faster production means the repository needs stronger boundaries.&lt;/p&gt;

&lt;p&gt;If an agent changes ten components in one pass, the project still needs to know whether:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;their APIs remain coherent,&lt;/li&gt;
&lt;li&gt;accessibility behavior remains correct,&lt;/li&gt;
&lt;li&gt;public exports remain synchronized,&lt;/li&gt;
&lt;li&gt;documentation still reflects reality,&lt;/li&gt;
&lt;li&gt;design tokens are still authoritative,&lt;/li&gt;
&lt;li&gt;React and React Native only diverge where they are supposed to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The better generation becomes, the more useful explicit and deterministic project contracts become.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are building into Vellira
&lt;/h2&gt;

&lt;p&gt;Vellira started as a cross-platform design system for React and React Native.&lt;/p&gt;

&lt;p&gt;The foundation is still familiar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reusable components,&lt;/li&gt;
&lt;li&gt;shared design tokens,&lt;/li&gt;
&lt;li&gt;Storybook,&lt;/li&gt;
&lt;li&gt;documentation,&lt;/li&gt;
&lt;li&gt;platform-specific implementations behind shared product intent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But while building the component catalog, another requirement became obvious.&lt;/p&gt;

&lt;p&gt;Creating the component is only one stage of its lifecycle.&lt;/p&gt;

&lt;p&gt;A production component also needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;metadata,&lt;/li&gt;
&lt;li&gt;public types,&lt;/li&gt;
&lt;li&gt;exports,&lt;/li&gt;
&lt;li&gt;tests,&lt;/li&gt;
&lt;li&gt;accessibility coverage,&lt;/li&gt;
&lt;li&gt;Storybook examples,&lt;/li&gt;
&lt;li&gt;website documentation,&lt;/li&gt;
&lt;li&gt;platform validation,&lt;/li&gt;
&lt;li&gt;completeness checks,&lt;/li&gt;
&lt;li&gt;quality checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we started making those expectations machine-readable and repeatable.&lt;/p&gt;

&lt;p&gt;For example, instead of asking a reviewer to remember every required artifact, the project can check whether a component has the expected implementation, tests, documentation, metadata, exports, and platform support.&lt;/p&gt;

&lt;p&gt;Instead of treating component creation as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generate file → done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we are moving toward something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;intent
  ↓
component production
  ↓
implementation
  ↓
tests / accessibility / metadata / docs
  ↓
quality and completeness validation
  ↓
review
  ↓
canonical component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not any single checker or generator.&lt;/p&gt;

&lt;p&gt;It is that the lifecycle becomes explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The author matters less than the resulting state
&lt;/h2&gt;

&lt;p&gt;I do not think AI-generated code needs to live in a completely separate category.&lt;/p&gt;

&lt;p&gt;A human can introduce an inconsistent API.&lt;/p&gt;

&lt;p&gt;A coding agent can follow an existing convention perfectly.&lt;/p&gt;

&lt;p&gt;A developer can forget to update documentation.&lt;/p&gt;

&lt;p&gt;Automation can keep it synchronized.&lt;/p&gt;

&lt;p&gt;Once code enters the repository, the important question is not who typed it.&lt;/p&gt;

&lt;p&gt;The important question is whether the resulting state of the project is valid.&lt;/p&gt;

&lt;p&gt;That is why we are more interested in lifecycle tooling than in trying to distinguish “human code” from “AI code.”&lt;/p&gt;

&lt;p&gt;Both humans and AI benefit when more of the consistency burden belongs to the system itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-platform UI makes this harder
&lt;/h2&gt;

&lt;p&gt;Vellira targets both React and React Native.&lt;/p&gt;

&lt;p&gt;And “the same component” does not necessarily mean “the same implementation.”&lt;/p&gt;

&lt;p&gt;Web and native platforms have different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;primitives,&lt;/li&gt;
&lt;li&gt;accessibility APIs,&lt;/li&gt;
&lt;li&gt;keyboard and focus models,&lt;/li&gt;
&lt;li&gt;interaction patterns,&lt;/li&gt;
&lt;li&gt;layout constraints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some behavior should be shared.&lt;/p&gt;

&lt;p&gt;Some behavior should intentionally diverge.&lt;/p&gt;

&lt;p&gt;So a good cross-platform system cannot simply copy the Web implementation into React Native and call that consistency.&lt;/p&gt;

&lt;p&gt;It needs to preserve the same product intent while allowing each runtime to behave appropriately.&lt;/p&gt;

&lt;p&gt;AI can generate both implementations quickly.&lt;/p&gt;

&lt;p&gt;The harder problem is determining whether those implementations still represent one coherent component.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottleneck is moving toward coordination
&lt;/h2&gt;

&lt;p&gt;This is probably the most interesting consequence of AI-assisted frontend development.&lt;/p&gt;

&lt;p&gt;When producing UI becomes cheaper, teams can produce more of it.&lt;/p&gt;

&lt;p&gt;More experiments.&lt;/p&gt;

&lt;p&gt;More parallel changes.&lt;/p&gt;

&lt;p&gt;More automated contributions.&lt;/p&gt;

&lt;p&gt;More components.&lt;/p&gt;

&lt;p&gt;The limiting resource moves away from typing speed.&lt;/p&gt;

&lt;p&gt;It moves toward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;coordination,&lt;/li&gt;
&lt;li&gt;consistency,&lt;/li&gt;
&lt;li&gt;review,&lt;/li&gt;
&lt;li&gt;maintenance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That changes what useful developer tooling looks like.&lt;/p&gt;

&lt;p&gt;If ten UI changes can be produced in the time it previously took to produce one, tooling that helps those ten changes remain part of the same system becomes much more valuable.&lt;/p&gt;

&lt;p&gt;That is the direction we are exploring with Vellira:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI can generate more UI. Vellira is being built to help keep that UI coherent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where Vellira is today
&lt;/h2&gt;

&lt;p&gt;We are still proving this inside Vellira itself.&lt;/p&gt;

&lt;p&gt;The immediate work is concrete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grow the React and React Native component catalog,&lt;/li&gt;
&lt;li&gt;make component production more repeatable,&lt;/li&gt;
&lt;li&gt;strengthen accessibility and quality validation,&lt;/li&gt;
&lt;li&gt;improve documentation,&lt;/li&gt;
&lt;li&gt;make the public project easier to install and use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are deliberately making these workflows reliable inside one real design system before claiming that they can manage arbitrary external repositories.&lt;/p&gt;

&lt;p&gt;But the direction is clear.&lt;/p&gt;

&lt;p&gt;Frontend teams are going to work with more generated code, not less.&lt;/p&gt;

&lt;p&gt;The answer should not be to make generation slower.&lt;/p&gt;

&lt;p&gt;It should be to make the system around generation stronger.&lt;/p&gt;




&lt;p&gt;Vellira is an open-source React and React Native design system being built in public.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.vellira.dev" rel="noopener noreferrer"&gt;Explore the documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storybook.vellira.dev" rel="noopener noreferrer"&gt;Browse Storybook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vellira-dev/vellira" rel="noopener noreferrer"&gt;Follow the project on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>react</category>
      <category>reactnative</category>
      <category>designsystem</category>
    </item>
  </channel>
</rss>
