The three-part model
Every RAD component is defined in three separate layers. Understanding the separation is what makes theming work — and what keeps it from becoming an excuse to strip out the accountability features that justify the system's existence.
Behavioral contracts
Required HTML structure, ARIA roles, keyboard behavior, and design rules. These are not theming targets. They define what the component must do to be trustworthy — its semantic structure, its accessibility requirements, its interaction model. An implementation that removes these is not a themed RAD component; it is something else.
CSS custom properties
The CSS variables the component reads. This is the public styling interface — the values you set in your theme to control surface colors, text colors, border treatments, and radii. Every visual decision in the Reference Implementation is exposed as a named token here. If you want to change how the component looks, this is where you do it.
Jackie's values
The specific token values that produce the RAD visual language — teal-on-dark, warm neutrals, Cormorant/DM Mono typography. This is one valid theme, not the only one. Override any token to produce a different visual treatment. The Core contracts remain unchanged regardless of what you override.
Shared semantic tokens
Some tokens apply across multiple components. These are the shared semantic tokens — the cross-component vocabulary. Set them once at :root and every component that reads them will inherit your values automatically.
They are named by role, not by color value. --rad-color-approve does not mean "green" — it means "the color used for affirmative, approval-signaling actions." What that color is depends on your theme.
| Token | What it controls | Reference value |
|---|---|---|
| --rad-surface-card | Card and panel background across all components | #ffffff |
| --rad-border-subtle | Default card border — low-emphasis container edge | rgba(0,0,0,0.08) |
| Token | What it controls | Reference value |
|---|---|---|
| --rad-color-approve | Approve buttons, active states, progress fills, cursor, focus indicators | #147060 |
| --rad-color-approve-hover | Approve button on hover | #0f5a4f |
| --rad-color-reject | Reject buttons, error borders, high-severity fills | #991b1b |
| --rad-color-reject-hover-bg | Reject button hover background tint | rgba(153,27,27,0.06) |
| --rad-color-focus | Focus ring on all interactive elements (minimum 3px, must pass contrast) | #147060 |
| Token | What it controls | Reference value |
|---|---|---|
| --rad-color-text-primary | Headings, titles, primary content | #111111 |
| --rad-color-text-secondary | Body copy, descriptions | #444444 |
| --rad-color-text-meta | Monospace labels, timestamps, IDs, scope strings | #595959 |
| --rad-color-emphasis | Inline strong emphasis — entity names, highlighted values | #92400e |
| Token | What it controls | Reference value |
|---|---|---|
| --rad-radius-card | Card and panel corner radius | 4px – 6px (varies by component) |
| --rad-radius-btn | Button corner radius | 4px |
Component-scoped tokens
Some visual decisions belong to a single component and don't make sense to share. These get component-scoped tokens — prefixed with the component identifier so it's clear where they apply.
For example, --rad-hitl-scope-bg controls the background tint of the scope line inside HITL Controls. No other component reads this token. You might never need to override it, but it's exposed so you can.
--rad-[component]-[property]. All tokens are documented in each component's Theming API section. If a token is not listed there, overriding it is unsupported — the component may not behave predictably.
Each component file documents its scoped tokens in two tables: one for shared tokens it reads, one for tokens unique to that component. Start with the shared tokens — getting those right affects everything at once. Layer in scoped overrides only when you need finer control.
Applying your theme
Theming RAD is three steps. You do not need a build tool, a preprocessor, or a framework.
-
Load the component CSSEach component's Reference Implementation CSS is written entirely against CSS custom properties. Copy the CSS into your project. It will render with no visible values until you define the tokens — which is intentional.
-
Define your theme valuesCreate a
:rootblock (or a scoped class) and set the shared semantic tokens. Start with the eight most impactful: surface, border, approve, reject, focus, and the three text levels. Every component will respond immediately. -
Override component-scoped tokens as neededOpen each component's Theming API section and override any scoped tokens you want to change. These are per-component fine-tuning — not required for a coherent theme, but available when you need them.
/* Step 1: Define shared semantic tokens at :root */ :root { /* Surface */ --rad-surface-card: #ffffff; --rad-border-subtle: rgba(0, 0, 0, 0.08); /* Actions */ --rad-color-approve: #147060; --rad-color-approve-hover: #0f5a4f; --rad-color-reject: #991b1b; --rad-color-reject-hover-bg: rgba(153, 27, 27, 0.06); --rad-color-focus: #147060; /* Text */ --rad-color-text-primary: #111111; --rad-color-text-secondary: #444444; --rad-color-text-meta: #595959; --rad-color-emphasis: #92400e; /* Shape */ --rad-radius-card: 4px; --rad-radius-btn: 4px; } /* Step 2: Add component-scoped overrides only where needed */ :root { --rad-hitl-scope-bg: transparent; --rad-hitl-defer-color: #595959; --rad-hitl-defer-border: rgba(0, 0, 0, 0.15); }
Re-theming HITL Controls
Below is RAD-02 Human-in-the-Loop Controls rendered twice: first with the Reference Implementation (light surface), then with a dark theme applied by overriding only the shared semantic tokens. The HTML structure is identical. The ARIA attributes are identical. The only difference is token values.
/* Scoped to a class so you can toggle it without affecting the rest of the page */ .rad-theme-dark { /* Surface: deep teal-tinted dark */ --rad-surface-card: #14201e; --rad-border-subtle: rgba(42, 181, 163, 0.15); /* Actions: lighter teal approve, soft red reject */ --rad-color-approve: #2ab5a3; --rad-color-approve-hover: #1d8a7a; --rad-color-reject: #f87171; --rad-color-reject-hover-bg: rgba(248, 113, 113, 0.08); --rad-color-focus: #2ab5a3; /* Text: warm light-on-dark palette */ --rad-color-text-primary: #e8e4dc; --rad-color-text-secondary: rgba(196, 192, 184, 0.7); --rad-color-text-meta: rgba(196, 192, 184, 0.35); --rad-color-emphasis: #e8a84a; }
role="dialog", aria-labelledby, data-approve, data-reject, and all aria-label values on the buttons are present in both. The contrast ratios meet WCAG AA in both. The focus ring is visible in both. The behavioral contracts did not move.
Scoping themes
You have three options for where to define your token values, depending on how your product handles theming.
/* One theme for the whole page. Works for most products. */ :root { --rad-surface-card: #ffffff; --rad-color-approve: #147060; /* ... */ }
/* Apply .rad-theme-dark to any container to re-theme its descendants */ .rad-theme-dark { --rad-surface-card: #14201e; --rad-color-approve: #2ab5a3; /* ... */ } /* Toggle it in JS */ document.getElementById('rad-container').classList.toggle('rad-theme-dark');
/* Automatically follow the OS preference */ @media (prefers-color-scheme: dark) { :root { --rad-surface-card: #14201e; --rad-color-approve: #2ab5a3; --rad-color-text-primary: #e8e4dc; /* ... */ } }
What you cannot theme
The theming API is intentionally bounded. The following are Core contracts — they exist to make RAD components trustworthy, not decorative. Overriding them produces something that looks like a RAD component but does not behave like one.
- ARIA roles and attributes.
role="dialog",aria-labelledby,aria-live,role="alert"— these are the semantic layer. Removing or changing them breaks assistive technology and removes accountability signals. - Focus ring visibility. You may change the color of
--rad-color-focus, but the outline must remain visible — minimum 3px, minimum 3:1 contrast against the adjacent surface. Settingoutline: noneis not a theme; it is a WCAG violation. - Button label specificity. Every action button in RAD requires an
aria-labelthat names the specific action being taken — not just "Approve" or "Reject." You can change button colors and border radii. You cannot make the labels generic. - Auto-approve on timer expiry. RAD-02 timed variant auto-cancels, never auto-approves. This is a design rule, not a visual property. No token controls it because it should not be configurable.
- Contrast ratios.
--rad-color-approveand--rad-color-rejectmust maintain 4.5:1 against your card surface on all interactive elements. If your chosen values fail this ratio, the theme is invalid — not just aesthetically, but as an accessibility contract. - Progressbar ARIA attributes.
role="progressbar"witharia-valuenow/min/maxmust be present on all confidence and impact bars. Color styling is a theming decision; the data attributes are a contract.