<?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: 7onic</title>
    <description>The latest articles on DEV Community by 7onic (@7onic).</description>
    <link>https://dev.to/7onic</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3863968%2Fcec9086a-4a1c-40ec-9254-35980686fe72.png</url>
      <title>DEV Community: 7onic</title>
      <link>https://dev.to/7onic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/7onic"/>
    <language>en</language>
    <item>
      <title>Design to Code #1: Why I Built 7onic</title>
      <dc:creator>7onic</dc:creator>
      <pubDate>Thu, 16 Apr 2026 10:47:03 +0000</pubDate>
      <link>https://dev.to/7onic/design-to-code-1-why-i-built-7onic-48fo</link>
      <guid>https://dev.to/7onic/design-to-code-1-why-i-built-7onic-48fo</guid>
      <description>&lt;p&gt;I remember the exact moment I gave up on handoffs.&lt;/p&gt;

&lt;p&gt;A developer had implemented a card component I designed. The spacing was off by 2 pixels. The border radius was &lt;code&gt;8px&lt;/code&gt; instead of &lt;code&gt;6px&lt;/code&gt;. The shadow was close but not quite right — they'd grabbed a Tailwind default instead of the value in the Figma file. Individually, none of these were worth a ticket. Together, the whole thing just looked... slightly wrong.&lt;/p&gt;

&lt;p&gt;I left a Figma comment. It got fixed in the next sprint. Then the same thing happened on the next component. And the next one. For ten years.&lt;/p&gt;

&lt;p&gt;I'm a designer. I've also been writing frontend code for most of those ten years, which means I've been on both sides of this handoff. I know why the developer used &lt;code&gt;rounded-md&lt;/code&gt; — it's right there in Tailwind, it's close enough, and who has time to check whether 6px rounds to &lt;code&gt;rounded-md&lt;/code&gt; or &lt;code&gt;rounded&lt;/code&gt; or something else entirely. (It doesn't map cleanly to either, by the way.)&lt;/p&gt;

&lt;p&gt;At some point the Figma comments stopped feeling productive and I just started writing the components myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not a people problem
&lt;/h2&gt;

&lt;p&gt;I want to be clear: the developers I worked with weren't sloppy. Most of them were better engineers than me. The issue was that design values lived in Figma and code values lived in... wherever the last developer put them.&lt;/p&gt;

&lt;p&gt;Someone builds a button. They eyeball the Figma file, pick &lt;code&gt;bg-gray-900&lt;/code&gt; because it looks right, move on. Next month, someone else builds a card header. They look at the button, assume that's the canonical dark color, copy it. Except the Figma file actually specified &lt;code&gt;#1a1a1a&lt;/code&gt;, which is close to &lt;code&gt;gray-900&lt;/code&gt; but not the same. Now you've got two slightly different "dark" colors in production and nobody remembers which one is correct.&lt;/p&gt;

&lt;p&gt;Multiply that by every color, every spacing value, every radius, every shadow. Across dozens of components, over months. The drift is slow and constant.&lt;/p&gt;

&lt;p&gt;The actual problem is obvious in hindsight: &lt;strong&gt;there was no shared source of truth.&lt;/strong&gt; The designer had one (Figma). The codebase had another (whatever was already in the code). Keeping them in sync was manual, which means it was nobody's job, which means it didn't happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  I tried all the "normal" solutions first
&lt;/h2&gt;

&lt;p&gt;Before building anything, I did what you'd expect.&lt;/p&gt;

&lt;p&gt;Exported tokens from Figma manually, dropped them into the Tailwind config. Worked great for about three weeks until the Figma file got updated and nobody remembered to sync the code. Back to drift.&lt;/p&gt;

&lt;p&gt;Tried Style Dictionary. It's genuinely powerful, but configuring it to output exactly the formats I needed — CSS variables, Tailwind v3 preset, Tailwind v4 theme, JS exports, TypeScript types — took longer than building the actual components. I spent a full weekend writing transforms and formatters and still didn't have something I trusted.&lt;/p&gt;

&lt;p&gt;Token Studio for Figma? Good plugin. But the exported JSON needed so much massaging before it was useful in a real Tailwind project that I was basically writing a custom pipeline anyway — just with an extra abstraction layer I didn't control.&lt;/p&gt;

&lt;p&gt;Every approach had the same gap. It handled the "get tokens out of Figma" part reasonably well, then left you alone for the "actually wire these into your codebase" part. That last-mile wiring is exactly where things break.&lt;/p&gt;

&lt;p&gt;So yeah, I built my own thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  One file, one command, zero drift
&lt;/h2&gt;

&lt;p&gt;The rule I started with was almost naively strict: &lt;strong&gt;if a value isn't in the design token file, it can't exist in code.&lt;/strong&gt; No &lt;code&gt;bg-gray-500&lt;/code&gt;. No &lt;code&gt;p-[17px]&lt;/code&gt;. No &lt;code&gt;text-[13px]&lt;/code&gt;. If you need a color, there's a token for it. If there isn't, you add one to the token file first.&lt;/p&gt;

&lt;p&gt;I realize that sounds annoying. It is, a little, at first. But it turns out that constraint is the whole point. It forces every visual decision through a single chokepoint: &lt;code&gt;figma-tokens.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One &lt;code&gt;sync-tokens&lt;/code&gt; command reads that file and generates everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS custom properties&lt;/li&gt;
&lt;li&gt;Tailwind v3 preset with RGB channels (so &lt;code&gt;bg-primary/50&lt;/code&gt; works)&lt;/li&gt;
&lt;li&gt;Tailwind v4 &lt;code&gt;@theme&lt;/code&gt; with native color-mix&lt;/li&gt;
&lt;li&gt;TypeScript types&lt;/li&gt;
&lt;li&gt;JSON for anything else&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eleven files total. All from one source. I can change a color in the token file, run the command, and know it's updated everywhere. No searching the codebase for hardcoded hex values. No "did we update the Tailwind config too?"&lt;/p&gt;

&lt;p&gt;On top of that token layer, I built &lt;a href="https://7onic.design" rel="noopener noreferrer"&gt;7onic&lt;/a&gt; — 42 components, all using Radix UI under the hood for accessibility, styled with CVA variants. There's a CLI too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx 7onic add button input &lt;span class="k"&gt;select&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This copies the actual source files into your project. Not a compiled package you import — the real &lt;code&gt;.tsx&lt;/code&gt; files, in your &lt;code&gt;components/&lt;/code&gt; folder, fully yours to read and modify. I went back and forth on this decision a lot. I'll write a separate post about why I landed on copy-paste over npm import, because it's a real trade-off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Radix specifically
&lt;/h2&gt;

&lt;p&gt;Quick detour on this because people ask.&lt;/p&gt;

&lt;p&gt;Accessibility is the part I didn't want to get wrong and also the part I knew I would get wrong if I built it from scratch. Focus traps in dialogs, keyboard navigation in dropdowns, screen reader announcements for toasts — this stuff is brutally hard to get right across browsers and assistive technologies.&lt;/p&gt;

&lt;p&gt;Radix handles all of that and ships completely unstyled. No CSS to override, no opinions about how things look. You bring the design tokens, Radix brings the semantics.&lt;/p&gt;

&lt;p&gt;Could I have built primitives from scratch? Sure. Would the focus management in my dialog component be as robust as what the Radix team has iterated on for years? No. I know where my time is better spent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solo thing
&lt;/h2&gt;

&lt;p&gt;I should mention that I built all of this alone. No team. No design review. No code review. Just me, my Figma files, and a growing collection of markdown documents where I argued with myself about naming conventions.&lt;/p&gt;

&lt;p&gt;(Is the base button size called &lt;code&gt;default&lt;/code&gt; or &lt;code&gt;md&lt;/code&gt;? Does the button need 5 sizes or is 3 enough? These are the questions that keep you up at night when there's nobody else to make the call. I went with 5 sizes and &lt;code&gt;default&lt;/code&gt; as the base, if you're curious. There's a whole doc about why.)&lt;/p&gt;

&lt;p&gt;Building solo has one unexpected benefit: it makes the "no hardcoding" rule actually stick. On a team, someone always has a deadline and a good reason to skip the token file. "I'll clean it up later." Solo, I'm the person who has to clean it up later, and I know I won't, so I just do it right the first time. The constraint isn't aspirational — it's survival.&lt;/p&gt;

&lt;p&gt;The downside is that a design system can grow forever. There's always one more component to add, one more variant to support, one more edge case to handle. I had to get comfortable shipping something incomplete. &lt;a href="https://7onic.design" rel="noopener noreferrer"&gt;7onic&lt;/a&gt; right now has 42 components, supports both Tailwind v3 and v4, has a CLI, and docs in three languages. It's also missing things. That's fine. Ship, iterate, repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;This is the first post in a series called "Design to Code." The plan is to write about the decisions behind 7onic — not the marketing version, but the actual reasoning, including the parts where I got it wrong and had to redo things.&lt;/p&gt;

&lt;p&gt;Some posts I'm planning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How the token pipeline works end to end — the &lt;code&gt;sync-tokens&lt;/code&gt; script, why I generate RGB channel variables, how Tailwind v3 and v4 support both work from the same token file&lt;/li&gt;
&lt;li&gt;The copy-paste vs npm-import debate — why I chose to have the CLI copy source files and what that costs&lt;/li&gt;
&lt;li&gt;Using AI to build a design system — what &lt;code&gt;llms.txt&lt;/code&gt; actually does, how I use Claude to write components, and where it falls apart&lt;/li&gt;
&lt;li&gt;Lessons from 42 components — patterns that scaled, patterns that didn't, things I'd do differently if I started over&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of that sounds interesting, stick around.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next up: how one JSON file becomes CSS variables, Tailwind presets, and TypeScript types — and why my first three attempts at building this pipeline failed.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About 7onic&lt;/strong&gt; — An open-source React design system where design and code never drift. Free, MIT licensed. Docs and interactive playground at &lt;a href="https://7onic.design" rel="noopener noreferrer"&gt;7onic.design&lt;/a&gt;. Source code on &lt;a href="https://github.com/itonys/7onic" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — stars appreciated. More posts in this series at &lt;a href="https://blog.7onic.design" rel="noopener noreferrer"&gt;blog.7onic.design&lt;/a&gt;. Follow updates on X at &lt;a href="https://x.com/7onicHQ" rel="noopener noreferrer"&gt;@7onicHQ&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>designsystem</category>
      <category>designtokens</category>
      <category>figma</category>
      <category>react</category>
    </item>
  </channel>
</rss>
