How it works

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.

Layer 01 Core

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.

Layer 02 Theming API

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.

Layer 03 Reference Implementation

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.

The rule You can change anything in the Theming API. You cannot remove anything from Core. Theming changes what a component looks like. Core defines what it does.
Token architecture

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.

Surface & Border
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)
Semantic Action Colors
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
Text
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
Shape
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
Token architecture

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.

Convention Component-scoped tokens follow the pattern --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.

Implementation

Applying your theme

Theming RAD is three steps. You do not need a build tool, a preprocessor, or a framework.

  1. Load the component CSS
    Each 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.
  2. Define your theme values
    Create a :root block (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.
  3. Override component-scoped tokens as needed
    Open 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.
Your theme — minimal setup
/* 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);
}
Worked example

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.

Reference Implementation
Approval Required RAD-02 · Human-in-the-Loop
Agent wants to archive 847 inactive records
This action will overwrite existing records in customer-database and cannot be automatically reversed.
Scope: customer-database · 847 records
Dark theme override
Approval Required RAD-02 · Human-in-the-Loop
Agent wants to archive 847 inactive records
This action will overwrite existing records in customer-database and cannot be automatically reversed.
Scope: customer-database · 847 records
Dark theme 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;
}
What did not change The HTML is identical. 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.
Advanced

Scoping themes

You have three options for where to define your token values, depending on how your product handles theming.

Option A — Global :root (simplest)
/* One theme for the whole page. Works for most products. */
:root {
  --rad-surface-card: #ffffff;
  --rad-color-approve: #147060;
  /* ... */
}
Option B — Scoped class (for multiple themes)
/* 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');
Option C — System dark mode (prefers-color-scheme)
/* Automatically follow the OS preference */
@media (prefers-color-scheme: dark) {
  :root {
    --rad-surface-card: #14201e;
    --rad-color-approve: #2ab5a3;
    --rad-color-text-primary: #e8e4dc;
    /* ... */
  }
}
Limits

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.

Intellectual Property

RAD is the original work of Jackie Curry. All rights reserved. No portion may be reproduced, adapted, or incorporated into any product or system without express written permission.

Permitted: citation in academic or editorial contexts with full attribution.

Licensing

© 2025 Jackie Curry. All rights reserved. Publication date: 2025.

For licensing inquiries, connect on LinkedIn →