Roman Kamushken
A UI kit becomes valuable when it removes ambiguity.
Developers want predictable APIs and stable styling hooks.
Product managers want fewer regressions and faster iteration.
Design system architects want a component library that scales across teams, themes, and releases.
This post is an engineering-style guide to building a production-ready UI kit foundation for a SaaS UI kit or enterprise UI kit.
By the end, you will have four concrete artifacts you can ship as part of your design system architecture:
➀ A design tokens model with a naming convention that supports theming and dark mode
➁ A shared UI component states matrix for core controls
➂ A component contract template, plus two filled examples you can copy into docs or Storybook
➃ A Figma component library structure that maps to real implementation constraints

Scope and terminology
A short alignment saves weeks later.
Style guide
→ visual rules and primitives: color ramps, typography scale, spacing, elevation, borders, icon rules
→ the “grammar” that components inherit
UI kit
→ reusable UI components: buttons, inputs, navigation, feedback, overlays, data display
→ variants, states, and usage patterns
Design system
→ operating model around the library: governance, contribution workflow, versioning, documentation, QA practices
This guide focuses on UI kit foundation work that directly impacts implementation speed, consistency, and long-term maintainability.
Start with interface grammar before components
If you build UI components first, you freeze hidden decisions inside each component. Later you discover five paddings, three baselines, and inconsistent focus styling.
Interface grammar is a small set of constraints that every component uses.
Spacing scale and layout rhythm
Choose one spacing system and treat it as an API, not a suggestion.
❶ Pick a base step (4 or 8 is common)
❷ Define a finite set of spacing tokens derived from it
❸ Define where exceptions are allowed and what approves them
❹ Define vertical rhythm rules for forms, tables, and dense screens
A practical spacing token set for a UI kit foundation:
· space.1 = 4
· space.2 = 8
· space.3 = 12
· space.4 = 16
· space.6 = 24
· space.8 = 32
· space.10 = 40
These values are less important than two properties:
→ the set is small
→ components stick to the set
☞ If your product includes admin screens or data-heavy views, define density rules now. Dense enterprise UI forces spacing exceptions later unless you plan for it.
Control heights, hit targets, and baselines
Developers will implement these as constants. Make them explicit.
❶ Define control heights as tokens (S, M, L)
❷ Define minimum hit targets where touch matters
❸ Define baseline alignment rules for label, value, and helper text
❹ Define icon sizes and baseline alignment relative to text
Example control sizing tokens:
· control.height.s = 32
· control.height.m = 40
· control.height.l = 48
And the rule that prevents layout drift:
→ the text baseline aligns across inputs, selects, and buttons for the same size preset
☞ Baseline alignment becomes visible only when you assemble real forms. Test with long labels, helper text, and mixed icon presence.
Radius, borders, and elevation
Small sets scale. Large sets fragment.
❶ Pick 2 to 4 radii values
❷ Map each radius to a UI meaning
❸ Define border roles as tokens
❹ Define elevation levels and which components may use them
Example radii mapping:
· radius.1 for small controls (chips, toggles)
· radius.2 for inputs and buttons
· radius.3 for cards and panels
· radius.4 for modals and large surfaces
Elevation levels that remain implementable:
· elevation.0 canvas and flat surfaces
· elevation.1 raised card, subtle hover lift
· elevation.2 popovers and dropdown menus
· elevation.3 modal dialogs
☞ Use borders and elevation together. In many SaaS UI kits, a subtle border plus a mild shadow reads cleaner than heavy shadows.
What to verify early
These checks catch most “foundation drift” before the component library grows.
❶ Place a button, input, select, and icon button on one row for each size preset
❷ Add label and helper text under the input and select
❸ Toggle hover and focus styles
❹ Confirm there is no layout shift across states and sizes
This single assembly is a fast quality gate for a UI kit foundation.

Design tokens that survive theming, dark mode, and scale
Design tokens are the contract between visual intent and implementation.
If tokens are treated as palette names, theming becomes fragile.
If tokens are treated as semantic roles, theming becomes mapping.
A scalable token system uses three layers:
→ Primitive tokens (raw ramps and constants)
→ Semantic tokens (meaningful roles)
→ Component tokens (targeted overrides under strict rules)
Primitive tokens
Primitive tokens represent raw values. Keep them stable and descriptive.
· color.neutral.0 … color.neutral.900
· color.accent.100 … color.accent.900
· color.success.*, color.warning.*, color.danger.*
· opacity.*
· shadow.* (if you store shadow primitives)
Primitive tokens should rarely appear in component styles directly.
Semantic tokens
Semantic tokens represent UI roles. This is where your design system architecture gets leverage.
Examples that cover most UI kits:
· text.primary, text.secondary, text.muted, text.inverse
· bg.canvas, bg.surface, bg.surfaceRaised
· border.default, border.subtle, border.divider
· accent.primary, accent.primaryHover, accent.primaryPressed
· selection.bg, selection.text
· focus.ring, focus.ringInner
· state.success, state.warning, state.danger
The key property:
→ semantic token names remain identical across themes
→ only the mapping changes
☞ If you want reliable dark mode, build it as a semantic token mapping problem. This avoids maintaining two parallel UI kits.
Component tokens under strict rules
Component tokens are justified when semantic roles cannot express a repeated visual requirement.
Use a clear threshold so token sprawl is controlled:
❶ Introduce a component token only if the exception repeats on at least 3 key screens or appears in at least 2 different components
❷ Tie the component token to a stable UI meaning, not a one-off layout
❸ Document where it is allowed and where it is forbidden
❹ Ensure the token works under theming without manual fixes
Examples:
· table.header.bg
· table.row.hover.bg
· toast.bg (if it differs from normal surfaces)
· input.border.error (when the error behavior is standardized)
☞ If a designer wants a one-off treatment for a single screen, treat it as a screen style decision, not a design token decision.
Token naming convention
A naming convention is part of your UI kit foundation. It impacts discoverability and the ability to automate.
A practical structure:
→ primitives: color.<family>.<step>
→ semantics: <category>.<role>[.<state>]
→ components: <component>.<part>[.<state>]
Examples:
· color.neutral.700
· bg.surfaceRaised
· text.primary
· accent.primaryHover
· button.bg.pressed
· input.border.focus
· select.option.bg.highlighted
This is readable for engineers and stable for design systems.
A small, implementation-ready token excerpt
This is the level of specificity developers expect when building a component library.
{
"color": {
"neutral": { "0": "#FFFFFF", "900": "#0B0F14" },
"accent": { "600": "#2563EB", "700": "#1D4ED8" },
"danger": { "600": "#DC2626" }
},
"semantic": {
"bg": {
"canvas": "{color.neutral.0}",
"surface": "{color.neutral.0}",
"surfaceRaised": "{color.neutral.0}"
},
"text": {
"primary": "{color.neutral.900}",
"secondary": "{color.neutral.900}",
"muted": "{color.neutral.900}"
},
"border": {
"default": "{color.neutral.900}",
"subtle": "{color.neutral.900}"
},
"accent": {
"primary": "{color.accent.600}",
"primaryHover": "{color.accent.700}"
},
"state": {
"danger": "{color.danger.600}"
}
}
}
The exact colors will differ by brand. The structure scales.
☞ If you plan to connect tokens to code, keep the token tree stable and avoid frequent renames. Versioning design tokens is harder than versioning components.

Color rules that remain readable under real data
Color problems in a UI kit usually come from missing state rules and missing surface rules.
Neutral ramp strategy
A neutral ramp must support four distinct tasks:
· readable text across surfaces
· subtle borders and dividers
· elevation separation
· disabled and muted content
A frequent failure in enterprise UI is using the same neutral for text and borders across every surface. This reduces hierarchy and makes tables hard to scan.
☞ Validate your neutral ramp using actual components, not swatches. Put neutrals under text, inside tables, inside modal overlays, and inside dropdowns.
Accent behavior across states
Define state deltas, then implement them consistently across the component library.
❶ Default uses accent.primary
❷ Hover uses accent.primaryHover
❸ Pressed uses accent.primaryPressed
❹ Focus uses focus.ring and focus.ringInner, not ad hoc styling
❺ Disabled has no hover and no pressed behavior
Then apply the same logic to secondary and ghost variants.
☞ The UI component states matrix must avoid layout shifts. Colors and shadows can change, padding and border widths should remain stable.
Status and semantic meaning
Status colors need both foreground and background roles, especially for alerts, badges, and form errors.
Define pairs:
· danger.fg, danger.bg, danger.border
· success.fg, success.bg, success.border
· warning.fg, warning.bg, warning.border
Then map them to component parts:
→ input.border.error = danger.border
→ input.helper.error = danger.fg
→ alert.danger.bg = danger.bg
☞ Use status with redundancy in critical flows. Combine color with icon and text, and define where status messaging lives so it does not jump layouts.
Theming and dark mode parity
Dark mode fails when the token model duplicates intent.
A stable approach:
❶ Keep semantic token names identical
❷ Map semantic tokens to theme-specific primitives
❸ Validate contrast and hierarchy using the same component assemblies in both themes
❹ Confirm selection, focus, and error treatments remain recognizable
This is the difference between “dark UI kit” as a duplicate design and “theming” as a controlled mapping.

To be continued tomorrow...



.avif)
.avif)

.avif)
.avif)



.avif)
.avif)


.jpg)
.avif)


.avif)
.avif)
.avif)


.avif)






%20(1).avif)

%20(1).avif)
.avif)
.avif)