Sovereign Ministry Digital Transformation
Twelve vendor-built platforms. One government. No shared definition of a button.
This is the system that replaced them — eleven component families, bilingual by construction, and specified to the pixel. Every control below is rebuilt in code from that specification, so you can measure it rather than take my word for it.
- Role
- Lead UX Designer — system architecture, accessibility, bilingual model, governance
- Client
- Government ministry. Under NDA.
- Reach
- 12 inherited platforms → 4 product teams in the first quarter
- Library
- 11 component families · 5 token namespaces · 11 type tokens · E100–E500
What you are looking at. No client screen, asset, or identity appears on this page. Each specimen is a code reconstruction from the library's own bound variables — the same values the engineering teams built against.
The condition
The users navigating these platforms are ministry employees and citizens alike — the civil servant filing a procurement approval in the morning, and the citizen registering a commercial permit on a public portal by afternoon. Over a decade, the ministry accumulated twelve standalone platforms, each commissioned through separate vendors and vendor contracts. While functionally operational, a button or form field on one portal shared zero visual or behavioral logic with the next — different heights, radii, color hexes, and terminology for identical actions. Every cross-portal transition incurred real cognitive strain and user re-learning.
The cost extended far beyond aesthetics. Every new feature launch triggered recurring debates over basic UI patterns, with no canonical design system to arbitrate decisions. Retro-fitting WCAG 2.1 accessibility compliance across twelve distinct, vendor-forked codebases was cost-prohibitive without a unified architecture.
Geometry here illustrates the drift rather than transcribing twelve audited platforms; the specified state is the real one.
4 button heights, 3 border radii, 3 disconnected hex reds for identical primary actions.
1 canonical fill (Chestnut Rose), 2px institutional radius, namespaced token variables.
Foundations
Four foundations sit under every component: colour, type, space and elevation. Each is namespaced, so a value's name states its job — and a developer reaching for the wrong one has to do it on purpose.
2.1Colour
Where interaction lives. Chestnut Rose carries nearly every selected, active and destructive state in the library.
#EB5A3CPrimary action & selection
Primary button fill, selected tab, active page, chip, checked box, switch knob.
#B41932Emphasis action
The deeper red button variant, and the hover state documented for the primary.
#91B9B4Icon tiles & fills
Backs the icon-with-text component. Never carries a label.
#FAC39BSurface tint
Card grounds and soft separators.
#6E1946Headers & key graphics
Ceremonial weight for banners and section headers.
{
"color": {
"primary": { "mirage": { "$type": "color", "$value": "#0F2837" } },
"secondary": { "chestnutRose": { "$type": "color", "$value": "#EB5A3C" } }
}
}2.2Type, space, elevation
Eleven type tokens on one family, a twelve-step ramp on a 16px base, and a five-step elevation ladder. What matters is not that they exist — every system has them — but that each one names its job rather than its value. A button label is not “14px” but Effra Regular 14 / 18 · 2%; E300 is not “medium shadow” but hover and dropdown menu. Naming the whole thing is what stops it drifting one property at a time.
Type — 4 of 11 tokens
Space — 4px to 96px
Elevation — E100 to E500
E100 Field / basic shadow · E200 Resting card · E300 Hover · dropdown menu · E400 Menu · E500 Pop-up
Effra is licensed and not embedded; sizes, weights and tracking are the specified values in a stand-in face. The ladder is specified on dark as well as light — only E300 carries a published numeric value, the rest are shown at their relative step.
Components
Eleven families. The two below are the ones the whole system leans on: the button family, where five fills share one geometry, and the input family, the largest and the most thoroughly bilingual thing in the library.
3.1Buttons
Five fills, one geometry
- Height
- 48px, constant. Width hugs the label — never fixed.
- Gutter
- 24px between label and edge.
- Radius
- 2px — near-square, deliberately unfashionable and unmistakably institutional.
- Label
- Effra Regular 14 / 18, tracking 2%. Identical at both heights: the control scales, the text does not.
- Fills
- 5, sharing one geometry. Choosing a button is choosing a meaning, not a shape.
- Contrast
- White label on each fill —Chestnut Rose3.47:1Mirage15.23:1Stiletto6.72:1· see §5.
3.2Controls
Every selected state in this set is carried by two signals — the colour and a shape. A rule under the tab, a tick in the box, a knob that travels, an underline beneath the page number, a dismiss target on the chip. That redundancy is deliberate, and Section 5 (Accessibility Ledger) is why.
Tabs — colour + a 5px rule
Selectors — colour + a check
Switch — colour + knob position
3.3Patterns & assemblies
Components assemble into repeatable enterprise patterns. Below, an abstract service queue table combines inputs, status pills, primary buttons, and pagination into one cohesive, accessible workflow.
| Reference ID | Service Category | Status | Filing Date | Action |
|---|---|---|---|---|
| REQ-2026-081 | Commercial Permit Renewal | Active | 08 Aug 2026 | |
| REQ-2026-079 | Trade License Modification | Under Review | 07 Aug 2026 | |
| REQ-2026-074 | Vendor Registration Entry | Completed | 05 Aug 2026 |
- Assembly Rule
- Components share logical spacing tokens: 16px cell padding, 56px row height, 40px field height.
- Accessibility
- Status indicators combine pill ground, text color, and bullet dot shape signal.
- Bilingual Parity
- Table layout mirrors automatically under dir="rtl" with right-aligned headers.
Beyond these sit nine more families — inputs, dropdowns, navigation, toasts, sliders, process and card sets, pop-ups, footers and the elevation ladder — across a component sheet roughly 18,000 pixels wide. Breadth is what decides whether teams adopt a system or route around it, and most of that breadth is unremarkable by design.
Bilingual parity
Arabic is not English with different words in it. The reading axis inverts, so the layout has to invert with it: primary actions move, margins swap sides, directional icons reverse, and the same string sets a fifth wider. The common approach — a second stylesheet — was the one thing this system could not afford, because it doubles the surface where the two languages drift apart.
Flip the specimen below. Nothing re-renders from a different file; the same components resolve against a different dir.
Please ensure that the required field is filled correctly.
Field validated
- Mechanism
- margin-inline / padding-inline / inset-inline — logical properties, resolved by the document’s dir.
- Stylesheets
- One. There is no ar.css and no .rtl override block.
- In the library
- Arabic variants are authored components sitting beside their English twins — dropdowns, inputs, pagination and headers all ship as pairs, so a reviewer sees both without switching files.
- Icon direction
- Directional glyphs swap at the component boundary. Search and add never flip.
- What never mirrors
- Numerals, logos, media transport controls, and progress mapped to real-world time.
- Why height is fixed
- Arabic sets the same string 20–30% wider. A fixed-width control is a bug in any bilingual system; it just does not present as one until translation.
The accessibility ledger
Most design systems claim a badge. A badge is not useful to a product team at the moment they are choosing a fill. What is useful is a grade: the largest text this colour may carry, stated next to the colour.
5.1Where the identity orange landed
The ministry's identity orange, #FF9619, is the colour the institution is recognised by. The instinct — mine included — was to make it the primary action.
It measures 2.18:1 on white. AA asks 4.5:1 of a 14px label. No arrangement of that hue on that ground reaches it, and darkening it far enough produces a colour nobody in the building would accept as theirs.
So the token kept the brand and lost the job. Orange owns identity, graphics and large display moments, where 3:1 applies and it clears comfortably. Interaction moved to the Secondary namespace. Writing that into the token layer — rather than a guideline document — is the part that mattered: a guideline is forgotten by the third team; a namespace is a thing you have to deliberately misuse.
5.2The ledger
Every interactive pairing in the library, measured. This is the honest version, and it is the more useful one: two of the button fills carry a 14px label at AA and two do not, which is a fact a team needs in front of them rather than behind a compliance claim.
| Pairing | Ratio | Body 4.5:1 | Large 3:1 | How the system copes |
|---|---|---|---|---|
| White on Mirage | 15.23:1 | Pass | Pass | Default for dense UI. |
| White on Stiletto | 6.72:1 | Pass | Pass | Cleared for any label size. |
| White on Chestnut Rose | 3.47:1 | — | Pass | Large-text grade. Paired with a shape cue everywhere it signals state. |
| Chestnut Rose on white | 3.47:1 | — | Pass | Selected tab, active page — always doubled by a rule or underline. |
| Mirage on white | 15.23:1 | Pass | Pass | All body copy. |
| Sub_header on white | 6.78:1 | Pass | Pass | Helper text, descriptions. |
| Sub_header 2 on white | 3.13:1 | — | Pass | Meta only — never load-bearing text. |
| Error on white | 4.87:1 | Pass | Pass | Validation message, doubled by a warning glyph. |
| Success on white | 5.06:1 | Pass | Pass | Confirmation, doubled by a tick. |
5.3Why the ledger is publishable
Because nothing in the system rests meaning on colour alone — that is the rule Section 3.2 (Controls) demonstrates, and it satisfies WCAG 1.4.1. The numbers above describe a colour, not the system's ability to communicate. Which holds for a user with low vision, a user with a colour deficiency, and a user on a washed-out screen in daylight; in a public-service context, that is most of them.
5.4The remediation
Redundancy makes the shipped system usable. It does not make the token pass. So the proposal is a darker Chestnut Rose — hue and saturation locked to the original, HSL lightness moved 57.8% → 43.9%. Nothing else about the colour changes.
The binding constraint is not white; it is the chip tint. Darkening only until the swatch passes on white lands at #DB3816, which still reads at 4.05:1 on a chip — a fix that holds everywhere the colour is looked at and fails where it is actually read. Grading against the worst ground, not the easiest one, is the whole discipline.
| Ground | As shipped #EB5A3C | Proposed #CC3414 | AA body |
|---|---|---|---|
| White | 3.47:1 | 5.16:1 | Clears |
| Field_BG #F4F4F4 | 3.15:1 | 4.69:1 | Clears |
| Chip tint #FDEEEB | 3.07:1 | 4.57:1 | Clears |
Darkening has one cost, and it is worth stating plainly. Chestnut Rose and Stiletto #B41932 — the primary and the destructive fill — move from 1.94:1 apart to 1.30:1. At that separation a Submit and a Delete button are no longer reliably tellable apart, which in a public-service system is a safety problem rather than a cosmetic one. There is no darker Stiletto that fixes it; the room has run out.
So the answer is the one the system already uses everywhere else: stop asking colour to carry the distinction alone. Destructive keeps its fill and gains a glyph.
Colour only
Colour + glyph
Left: two fills 1.30:1 apart. Right: the same two fills, now separable without relying on the eye to grade a red.
5.5Focus indicators & assistive technology
Visible focus rings & touch targets
Every interactive control carries an explicit 2px solid #0F2837 focus ring with a 2px offset. Touch targets across mobile and desktop viewports maintain a strict 44×44px minimum bounding box.
Bilingual screen reader validation
Toasts and field validation messages utilize aria-live="polite" regions. Form controls were validated using VoiceOver (macOS/iOS) and NVDA in both Arabic and English text-to-speech engines.
Governance
A library is easy. Keeping it true is the job. Three things decided whether this survived past handover.
Documentation drifts from components — plan for it
The printed spec sheets and the built component library disagree in places: the sheets document the primary button as #E30327 with a #C4C4C4 disabled fill, while the components resolve to Chestnut Rose #EB5A3C and Neutral 1 / Disabled. Neither is wrong; the components moved on and the prose did not. This page treats the component library as the source of truth, because that is what engineering consumes. The durable fix is to stop hand-writing the spec: generate the sheets from the bound variables, so drift becomes impossible rather than merely discouraged.
Bilingual variants are authored, not derived
Arabic components sit beside their English twins in the same file rather than being produced by mirroring at build time. It costs more to maintain and it is the right trade: a reviewer sees both at once, and the cases that do not simply mirror — numerals, a date field, an icon that must not flip — are visible as design decisions instead of surfacing as bugs in production.
Two dated footers, kept side by side
The library holds a 2025 and a 2026 footer rather than one overwritten component. Small thing, correct instinct: a government platform cannot cut over everywhere at once, so the system has to be able to hold two truths while a migration runs. A system that can only express the newest state forces teams to fork it.
Vendor enablement & contribution model
The system shipped as an npm token package (@ministry/tokens) and a centralized Figma sync library. Third-party vendors building platforms were provided with clear component submission guidelines and weekly governance office hours, preventing vendor-level forks while enabling teams to request new pattern additions.
Outcome & reflection
The system shipped as a Figma library and a token package consumed by the front-end teams, so a change to a variable reached staging without anyone re-typing a hex.
4 product teams in the first quarter
Ahead of the two-team target set at kickoff.
~30% less UI build time on new features
Measured against the teams’ own pre-system estimates.
Arabic and English from one component set
No parallel stylesheet, no forked components.
Graded, not asserted
Contrast resolved in the token layer, and every colour-bearing state doubled with a shape cue so meaning never depends on the hue.
One system to learn, not twelve
The same HR request and procurement submission from Section 1 (The Condition) now share a button, a form field and a validation message — the relearning cost at every login is the thing that actually went away.
What I would do differently
I would generate the specification sheets from the bound variables on day one. The drift in Section 6 (Governance) is small and harmless today, but it is the mechanism by which every design system eventually stops being believed — someone builds against the prose, ships something that does not match, and concludes the library is unreliable.
And I would run the contrast ledger before choosing the interaction colour, not after. We arrived at the right answer — redundant shape cues on every state — but we arrived at it defensively, working around a colour that was already load-bearing. Running the numbers first turns the same conversation from a negotiation into a constraint everyone can see.
The transferable part: accessibility arguments are won with measurements and lost with opinions. Once the ratios were on the wall next to the swatches, the conversation stopped being about taste and became about which job each colour was going to do.