A Figma system where nothing hard-codes a hex
Four variable collections, 34 components, and the Figma plugin API traps that cost me a 23,828 pixel tall page before I caught them.
The requirement was blunt: colours, font sizes, themes and both languages all live in variables. Nothing in the file hard codes a hex or a size.
That is more interesting than it sounds, because it decides whether a design file is a picture of a product or a description of one.
Four collections
Vouch holds 15 colour tokens with Light and Dark modes. The dark theme is a warm green-black, deliberately not the blue-charcoal every dark SaaS ships. That default is the night-mode twin of the blue-to-purple gradient, and it signals nobody chose the palette.
Typography holds the family, weight names, seven sizes and seven line heights, with EN and AR modes. Arabic is set a step larger with more leading, up to 72 pixels on display sizes. That is not a preference. The script reads smaller at the same point size and its ascenders and diacritics need the room.
Layout holds spacing, radius, the three frank cut sizes and key dimensions.
Copy holds 78 interface strings in EN and AR. Switching that mode flips the interface language rather than duplicating every screen.
Nine text styles bind their size and line height to the Typography variables, so the whole ramp responds to the language switch.
Two components that actually work
Most component libraries are catalogues. Two of these behave.
The Filter group has Expanded and Collapsed variants, and the header is wired with a CHANGE_TO reaction, so clicking it swaps the variant in the prototype. The collapse works rather than being a picture of a collapse. The same trick makes the Toggle real.
That matters more than it sounds. A control that looks interactive but does nothing when clicked is not a design detail, it is simply broken, and reviewers click things.
The traps
I drove this file through the plugin API, and it punished a few assumptions.
Setting textAutoResize = 'HEIGHT' on an empty text node pins its width near zero. Every subsequent line then wraps to one character. My first auto-layout rebuild of the home page came out 23,828 pixels tall as a single-character column. The fix is order: set the characters first, append to the parent, set FILL, then set HEIGHT.
resize() resets sizing modes to FIXED. Calling it after setting HUG silently locks the dimension. This is how a card that should hug its content ends up 10 pixels tall.
A child set to layoutSizingVertical = 'FILL' inside a hugging parent breaks the hug. The parent keeps its old height and the content overflows without complaint. I had job cards overflowing by up to 32 pixels and clipping the action off the bottom, and the card was reporting a perfectly sensible height the whole time.
Fills bind through setBoundVariableForPaint, which returns a new paint you have to capture and reassign. node.setBoundVariable('fills', v) throws.
Two smaller ones: counterAxisAlignItems rejects STRETCH, and prototype transitions reject INSTANT. A frame also cannot navigate to itself, which quietly breaks any loop where a nav item points at the page you are already on.
Equal heights, properly
Cards in a row have to be the same height, with the footer pinned to the bottom of each. In CSS that is one line. In Figma it took a measured approach.
Let every card hug so you learn the true tallest. Set the row to a fixed height of that tallest plus the row's own padding, which I forgot the first time and left every card 13 pixels short. Then set each card and its internal flexible spacer to FILL.
The spacer only fills when the card is stretched. In a vertical list where cards hug, it stays fixed, otherwise it breaks the hug as above.
I verified the result by measuring rather than looking: three cards at 293 pixels, zero overflow.
What the system ended up as
34 components, including three role sidebars so 29 dashboard screens share one navigation rather than redrawing it. Buttons, stamps, filter chips, inputs, selects, radios, toggles, textareas, table rows, pagination, steppers, toasts, empty states, skeletons, modals, sheets, footers and a mobile tab bar.
The one I would keep in any project: the skeleton, built to match the exact shape of the loaded card. Shipping only the loaded state is how you get a design that jumps when real data arrives.
Next: the sitemap, and why 50 screens went on paper before any pixels.
Building scalable systems and developer-first tools. Lead Software Engineer at DSRPT.