B
B
Back to Projects

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
30%
Faster UI Delivery
12 → 1
Unified System Spec
4 Teams
Adopted in Q1
100%
WCAG Redundant Cues

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.

Submit request
Buttons · main · primary · state: resting — 48 × auto, gutter 24, radius 2

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.

Submit
Submit
Submit
Submit
Submit
Submit
Submit
Submit
Submit
Submit
Submit
Submit
Twelve platforms, twelve definitions of “button”

Geometry here illustrates the drift rather than transcribing twelve audited platforms; the specified state is the real one.

Legacy (4 Vendor Platforms)
Vendor A · 32pxVendor B · 44pxVendor C · 50px

4 button heights, 3 border radii, 3 disconnected hex reds for identical primary actions.

Unified Standard (Sovereign System)
Submit request · 48px

1 canonical fill (Chestnut Rose), 2px institutional radius, namespaced token variables.

Legacy Vendor Fragmentation vs Sovereign Design System Standard

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.

Chestnut Rose#EB5A3C
3.47 white4.39 ink

Primary action & selection

Primary button fill, selected tab, active page, chip, checked box, switch knob.

Stiletto#B41932
6.72 white2.27 ink

Emphasis action

The deeper red button variant, and the hover state documented for the primary.

Summer Green#91B9B4
2.14 white7.11 ink

Icon tiles & fills

Backs the icon-with-text component. Never carries a label.

Zinnwaldite#FAC39B
1.57 white9.68 ink

Surface tint

Card grounds and soft separators.

Tawny Port#6E1946
11.11 white1.37 ink

Headers & key graphics

Ceremonial weight for banners and section headers.

Secondary Colors — 5 tokens · ratios computed live
Design-to-Code Token Sync Architecture
{
  "color": {
    "primary": { "mirage": { "$type": "color", "$value": "#0F2837" } },
    "secondary": { "chestnutRose": { "$type": "color", "$value": "#EB5A3C" } }
  }
}
Tokens · W3C Standard JSON Spec

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

Page titlesEffra Medium · 36 / auto
Running body copyEffra Regular · 16 / 24
All button labelsEffra Regular · 14 / 18 · 2%
Captions, legendsEffra Regular · 10 / auto · 1%

Space — 4px to 96px

4
8
12
16
20
24
32
40
48
64
80
96

Elevation — E100 to E500

E100
E200
E300
E400
E500

E100 Field / basic shadow · E200 Resting card · E300 Hover · dropdown menu · E400 Menu · E500 Pop-up

Type · space · elevation — specified values, shown at true scale

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

48px
Label
24px24px
r 2px

Five fills, one geometry

Buttons · main · height 48px · gutter 24px · radius 2px
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

Controls · selection carried by colour and by shape

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.

Assembly: Data Table + Input + Filters + Buttons
Search by reference number or service...
Reference IDService CategoryStatusFiling DateAction
REQ-2026-081Commercial Permit RenewalActive08 Aug 2026
REQ-2026-079Trade License ModificationUnder Review07 Aug 2026
REQ-2026-074Vendor Registration EntryCompleted05 Aug 2026
Showing 1–3 of 48 service requests
Prev12Next
Pattern Assembly · Service Queue Table · 56px row height · 16px cell padding
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.

Field label
Input text
Field label
Input text
Field label *
Input text

Please ensure that the required field is filled correctly.

Field label
Input text

Field validated

Field label
Input text
Field label
Input text
Input family · dir="ltr" · one component set, one stylesheet
Sign out
Chrome and CTA links · dir="ltr"
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.

Submit request
Identity orange2.18:1
Submit request
Chestnut Rose3.47:1
Same label, two fills

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.

PairingRatioBody 4.5:1Large 3:1How the system copes
White on Mirage15.23:1PassPassDefault for dense UI.
White on Stiletto6.72:1PassPassCleared for any label size.
White on Chestnut Rose3.47:1PassLarge-text grade. Paired with a shape cue everywhere it signals state.
Chestnut Rose on white3.47:1PassSelected tab, active page — always doubled by a rule or underline.
Mirage on white15.23:1PassPassAll body copy.
Sub_header on white6.78:1PassPassHelper text, descriptions.
Sub_header 2 on white3.13:1PassMeta only — never load-bearing text.
Error on white4.87:1PassPassValidation message, doubled by a warning glyph.
Success on white5.06:1PassPassConfirmation, doubled by a tick.
Contrast ledger — computed at render time from the bound token values

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.

GroundAs shipped #EB5A3CProposed #CC3414AA body
White3.47:15.16:1Clears
Field_BG #F4F4F43.15:14.69:1Clears
Chip tint #FDEEEB3.07:14.57:1Clears
Proposed — Chestnut Rose darkened, hue and saturation unchanged

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

SubmitDelete

Colour + glyph

SubmitDelete

Left: two fills 1.30:1 apart. Right: the same two fills, now separable without relying on the eye to grade a red.

Primary beside destructive — darkened, then disambiguated

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.

Adoption

4 product teams in the first quarter

Ahead of the two-team target set at kickoff.

Delivery time

~30% less UI build time on new features

Measured against the teams’ own pre-system estimates.

Languages

Arabic and English from one component set

No parallel stylesheet, no forked components.

Accessibility

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.

For the employee

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.