Experience+ (Internal) — One: A Living Component Registry
Every Experience+ engagement starts on One: a production-ready design system of 30+ coded components across 8 categories — consistent tokens, accessible states, responsive patterns — already running the studio's own sites. Instead of paying for weeks of foundation work, clients get One themed to their brand and shipping from day one. It was born as a fix for the studio's own component drift, which is exactly why we know it holds up in production.
Role: Design Engineering
Overview
One is the design system Experience+ brings to every client engagement: a single, versioned registry of production-ready components — built on Next.js, TypeScript, Tailwind CSS v4, and shadcn/ui — with a full design token system and live theme customization (radius, scale, shadow, color). It is not a starter kit or a promise of future work; it is already coded, already deployed, and already powering the studio's own sites. When a project kicks off, the foundation layer most builds spend their first weeks assembling — buttons, forms, overlays, navigation, tokens, dark mode — is done. We theme it to the client's brand and start building the actual product.
What this means for a client
- A running head start: the component and token foundation exists on day one, so budget goes to the product, not to rebuilding primitives.
- Consistency by construction: every screen draws from the same registry, so spacing, color, and interactive states stay coherent as the build grows.
- Accessibility and responsiveness handled at the component level — focus states, contrast in both theme modes, and mobile behavior are baked in, not bolted on.
- Themeable to your brand: radius, scale, type, and color are token-driven, so the same components can carry an entirely different visual identity.
- Open source and portable: the registry lives in the open at github.com/RHI-Org/components, so nothing about your foundation is locked to us.
Why it exists: an honest origin story
One started as a fix for the studio's own problem. Across the studio's project history, each new build tended to start its own component library from scratch: its own button, its own dropdown, its own idea of what a card border-radius should be. That drift is invisible until you look at several sites side by side — small inconsistencies in color tokens, spacing, and interactive states that add up to duplicated effort and a less coherent brand. One replaced all of that with a shared, themeable foundation. The reason it makes a credible client offering is precisely that it wasn't built as a demo — it was built to run real sites, and it does.
What's in the registry
- 30+ components across 8 categories: General, Data Display, Forms, Feedback, Navigation, Overlay, Layout, and Typography/Colors foundations.
- A full design token system — IBM Plex type family, a Cambridge Green / Linen / Tea Rose brand palette, semantic tokens (success, warning, error, info), and an IBM Carbon-based data visualization palette.
- Live theme customization with persistence: radius, scale, padding, border width, shadow, and dark/light mode, saved to localStorage so preferences survive a refresh.
- A component-settings map so the theme panel only shows controls that actually apply to the component being viewed, instead of a generic panel for everything.
How it was built: an AI-amplified workflow
The registry was built using an AI-amplified workflow: a designer's eye for visual QA, paired with Claude Code for implementation, closed by verification against the live UI. In practice that looked like a repeating loop — spot a visual defect, describe the desired outcome in plain language, let the AI implement it, then check the result against every affected state before moving on. That loop caught defects that are easy for an automated test suite to miss entirely: a hover state with no visual feedback, an avatar with no visible boundary in dark mode, contextual demo text that shrank into illegibility under a scale transform. None of those break a unit test. All of them break trust in the UI.
Problems encountered — and what actually caused them
These are the kinds of defects a client project would otherwise hit mid-engagement, on their budget. One hit them first, on ours.
1. Hover states that silently did nothing
Sidebar navigation links showed zero visual feedback on hover — no way to tell which items were interactive. The Tailwind hover: classes were present in the code and simply weren't firing.
Root cause: the component set text and background color via an inline style attribute (style={{ color: isActive ? 'white' : 'gray' }}), and inline styles carry higher CSS specificity than class-based pseudo-selectors — so no amount of hover: tuning in Tailwind could ever override them. It's a common trap when inline styles and utility-first CSS get mixed in the same component.
Fix: migrate all conditional color and background logic out of inline style props and into Tailwind classes built with template literals, so the hover pseudo-classes could finally take effect. Restored expected interactive feedback across every navigation state.
2. Components that worked in light mode and broke in dark mode
Dropdown menus rendered with transparent backgrounds — text unreadable — and avatar fallbacks showed no circular boundary, just initials floating in space. Both issues appeared only in dark mode.
Root cause: Tailwind CSS v4 requires every semantic token a component references (bg-popover, bg-muted, etc.) to be explicitly defined as a CSS custom property. Some of those tokens were simply undefined; others had contrast that worked in light mode but collapsed in dark mode. The pattern repeated across components — a class with no effect always traced back to an undefined or under-specified token.
Fix: audit the token system for gaps and define missing tokens consistently across all three layers Tailwind v4 needs — the @theme block, :root (light), and .dark (dark) — plus a border fallback on the avatar for when background contrast alone isn't enough. This produced a repeatable diagnostic pattern for the rest of the build: when a utility class has no visible effect, trace it to its CSS variable and confirm it's defined in every theme mode.
3. Customizations that vanished on refresh
The theme panel let people adjust radius, scale, border width, and shadow intensity for component previews — but every setting reset the moment the page reloaded.
Root cause: theme state lived only in React useState, which is memory-only and disappears on refresh. There was no persistence layer reading from or writing to anything durable.
Fix: add localStorage persistence to the ThemeProvider — one effect hydrates state from storage on mount (with error handling for malformed JSON), another serializes state to storage on every change. Preferences now survive refreshes and browser restarts indefinitely.
4. A settings panel that offered controls that didn't apply
The theme panel showed the same full set of controls — radius, scale, padding, shadow — for every component, including ones the controls didn't apply to. A border-radius slider next to a Separator, which is just a line, is just noise.
Root cause: there was no mapping between a given component and which theme settings actually affect its appearance, so the panel rendered everything, always.
Fix: a typed componentSettingsMap (Record<string, SettingKey[]>) that the panel reads to conditionally render only the controls relevant to whatever component is on screen — some components, like Separator, Checkbox, and Switch, correctly show no settings at all, because their appearance is fixed. A related fix used the same pattern to exempt components with contextual demo text (like Separator's inline example copy) from a global scale transform that was otherwise shrinking that text into illegibility.
Key takeaways
- Starting an engagement on an existing, production-tested design system turns a recurring cost (every project re-solving the same button, dropdown, and token problems) into a foundation clients simply inherit.
- A design system is only as strong as its token coverage — most of these bugs traced back to a token that was undefined, under-specified, or only checked in one theme mode.
- Visual QA catches what automated tests don't: broken hover states, dark-mode-only regressions, and readability issues rarely show up as a failing assertion, but they show up immediately to a trained eye.
- Pairing design judgment with AI-driven implementation lets a small team close the loop — identify, direct, implement, verify — fast, without trading away design precision for velocity.