window.__mfConstitution
, which is available at runtime on every page that has loaded a carrier widget. Any agent working on the theme subsystem who needs to verify a constitutional rule should read it directly from that object — not by opening another widget's source file. Reaching into another widget's widget.html
to read SFL rules is not an acceptable approach: it introduces a dependency on a separate file read, which is unreliable and untestable without a FAH-1 fetch-and-hold test and dual-track verification. The global objects are self-describing and always the authoritative source.window
and available to any widget JavaScript running on the same page. Access them as follows: -
window.__mfConstitution— constitutional rules, amendments, authority hierarchy, and all SFL standing constraints. Start here to verify any rule. -
window.__mfRegistry— all registered SFL objects, their tiers, and their current carrier widgets. Use this to check registration status before assuming an object is available. -
window.__mfVersionRouter— versioning, routing logic, and version arbitration for federated objects. -
window.__mfTheme— the theme subsystem object governed by this spec. API defined in §6 of this document. -
window.__mfThemeBridge— the optional migration aid. Planned; not yet registered. Checkwindow.__mfRegistrybefore use.
window
, the relevant carrier widget has not yet loaded on this page — that is a deployment state issue, not a reason to skip rules or assume the object does not exist.SFL Framework (constitutional authority: window.__mfConstitution)
│
├── Versioning & sync subsystem ← 6dbf3a has spec authority
│ window.__mfVersionRouter, window.__mfWidgetVersioning,
│ window.__mfReleaseState, window.__mfConstitution, window.__mfRegistry
│
└── Theme subsystem ← 7430a6 has spec authority (this document)
window.__mfTheme (required)
window.__mfThemeBridge (optional)
Both subsystems are fully federated SFL objects. They obey all SFL rules (registration, carrier index, tier system, sync prompts, Amendments 1–8). The distinction is spec authority, not a special code-level privilege. 7430a6 is the spec home by convention — standard highest-version-wins federation applies.
The theme subsystem comprises exactly the following. Nothing outside this list is governed by this spec.
-
__mfTheme— the required SFL object that writes CSS custom properties to:rootand persists the active theme to localStorage -
__mfThemeBridge— the optional SFL migration-aid object for widget-by-widget token adoption - All CSS custom properties in the
--mfw-*namespace — every token defined in §5 of this spec - All palette objects — the 18 named palettes in §3 plus any future palettes added via the full workflow
- The token writing logic in widget
7430a6(hub carrier for__mfTheme— see TH-20) - This specification document (widget 7430a6
widget.html) and any downstream spec references to it
Not in scope: the platform's native theme settings (site header, footer, navigation colours) — those are managed separately and require manual entry. The versioning & sync subsystem — that is 6dbf3a's domain.
| Object | Tier | Layer | Status |
|---|---|---|---|
__mfTheme
|
required | utilities | Registered; 7430a6 v1.0 (hub carrier — see TH-20) |
__mfThemeBridge
|
optional | utilities | Concept only; no @mf-lib block exists yet |
| TH-32 | Popup token layer added to §5 Layer 3 (v2.5 — pre-release).
Five --mfw-popup-*
tokens added: --mfw-popup-overlay
(scrim behind popup), --mfw-popup-bg
(main panel background — mirrors --mfw-surface
), --mfw-popup-border
(panel border), --mfw-popup-header-bg
(draggable header bar — primary chromatic statement; follow --mfw-table-col-header-bg
logic), --mfw-popup-header-text
(text on header bar). --mfw-shadow-lg
already exists and covers the panel drop-shadow — no new shadow token required. Rationale: draggable display popups (profile cards, contact log entries) and input form popups (profile edit, registration) currently have no dedicated surface tokens; they fall through to generic --mfw-surface
or use hardcoded values. Pre-release status: breaking changes to token catalogue explicitly accepted at this stage. |
||
| TH-31 | Font Hierarchy theme type implemented (v2.3).
9 Google Font pairings added as FONT_PRESETS
constant in widget.js
. Named after classic novels: Typhoon (default — pairs with The Shipping Forecast palette), Moby Dick, To Kill A Mockingbird, The Great Gatsby, Nineteen Eighty-Four, Brave New World, A Room With A View, The Hitchhiker's Guide, Rebecca. Each preset defines: heading font family, body font family, UI/table/label font family, and a scale factor (admin-set at save time, pre-baked into token — not a runtime slider). 4 tokens: --mfw-font-family-heading
, --mfw-font-family-body
, --mfw-font-family-ui
, --mfw-font-scale
. Font loading: widget-managed Google Fonts <link>
injection/swap via _loadGoogleFonts()
helper on apply; migrates to Duda platform site fonts once configured there. ThemeSeeder extended to seed all 9 Font rows (default: typhoon
). Font tab added to CRUD tab bar. FONT_PRESETS
added to TH-30 retirement scope. |
- This spec operates entirely under the SFL framework — not a parallel framework
- Code-driving spec changes require the full 9-step SFL workflow (Amendment 7/8) — see §P2 Rule 1 for the full step list
- Purely descriptive spec changes require user confirmation before writing
- Persistence uses localStorage with
mf_key prefix (SFL standing constraint)
__mfConstitution
. An agent that proceeds without reading and following every rule in this section is operating in violation of this specification. Any work performed in violation of these rules is invalid and must be reversed.
There is no "I'll fix it later" option. Violated work is undone, not patched. Scope:
These rules apply to every agent reading, interpreting, writing, or referencing any part of the theme subsystem — including __mfTheme
, __mfThemeBridge
, any --mfw-*
token, any palette name, any SFL object in a widget consuming theme tokens, and this specification itself. The precise boundary of "the theme subsystem" is defined in §P1. There are no exceptions and no partial compliance.
Authority:
These rules operate under
__mfConstitution
— where they conflict, the constitution takes precedence. In all other cases, these rules are the ceiling, not a floor. They cannot be relaxed by any agent acting alone, regardless of circumstances, time pressure, or apparent obviousness of the change.
Every change to the theme subsystem — including changes to this specification — requires the full 9-step SFL workflow. There is no "minor edit" exemption. There is no "obvious fix" shortcut. There is no "just updating a comment" bypass. If a change touches any part of the theme subsystem (see §P1 boundary), the workflow applies from Step 1 for that change. Each discrete change requires its own pass through the workflow — steps cannot be shared across changes.
- Step 1 — User prompt triggers but authorizes nothing: The user's message initiates the workflow. No analysis, spec change, or code action may be taken yet. The agent reads and acknowledges the request only.
- Step 2 — AI structured analysis: The agent produces a structured analysis of the proposed change — covering pros, cons, risks, alternatives, and a recommendation — without writing or modifying any code or spec. This analysis is presented to the user for review.
- Step 3 — Dialogue to explicit agreement: The agent and user engage in dialogue until the user explicitly agrees on the final approach. No ambiguity is carried forward. The agreed approach is stated clearly before proceeding.
- Step 4 — AI writes the agreed change into the SFL Functional Specification: The agent updates this specification (widget.html) to describe the agreed change as a persisted file change. The spec must be written before any code is touched.
- Step 5 — User reviews and explicitly approves the written spec: The agent presents the written spec update to the user. The user must explicitly approve it before any code writing begins. See Rule 2 for what constitutes valid approval.
- Step 6 — AI derives implementation plan from approved spec and checks for drift: The agent derives a concrete implementation plan from the approved spec and checks all affected files for drift — any pre-existing divergence between spec and code that must be resolved before the new change is applied.
- Step 7 — Incremental code implementation in small non-breaking checkpoints with user pauses: The agent implements the change in small, independently non-breaking increments. After each checkpoint the agent pauses and the user confirms before the next increment begins.
- Step 8 — Consequential changes in other widgets are echoed as ready-to-use prompts; no direct edits: If the change has consequences for other widgets outside the theme subsystem, the agent identifies those widgets and provides ready-to-use prompts the user can apply to each one. The agent does not make direct edits to widgets outside its current scope.
- Step 9 — Update the changelog: Add a §11 changelog entry with the spec version, date, and a plain-language description of the change.
This specification (Step 4) must be updated — and the update explicitly confirmed by the user (Step 5) — before any code change is written (Step 7). An agent that writes code and then updates the spec afterwards is acting in violation of this document and the SFL constitution. That work is invalid.
What Step 5 confirmation looks like: The user must explicitly approve the proposed spec change in a message that is clearly responsive to the agent's Step 4 presentation. Acceptable: "yes", "confirmed", "go ahead", "looks good", or any message that unambiguously approves the specific change proposed. Not acceptable: silence, a previous approval for a different change, or the agent inferring approval from context. If there is any doubt, ask again. One word of explicit approval is all that is required — but it is always required.
The 18 palette names and their machine keys in §3 are locked. No palette may be renamed, removed, re-keyed, reordered (except as required by Rule 9's adjacent-pair constraint), or supplemented without explicit user agreement and a spec update that precedes any code change. Index numbers are not part of the spec — they were a working device only and must not be used as canonical references in code or spec.
Code prohibition: No code may instantiate, register, or reference a palette object that is not listed by name in §3. Palette objects exist in code if and only if they exist in §3. An agent may not create a palette object as a "placeholder", "test palette", or "example" without a confirmed §3 entry. This applies to partial objects, stub objects, and objects in comments.
All token names in §5 are locked. Renaming a token is a breaking change across every widget consuming it — it is not a local change. No token may be renamed, removed, or repurposed without the full workflow and a spec update that precedes code. New tokens must be assigned to a layer in §5 with their default value documented before any implementation begins.
All palette token values are pre-calculated and stored as resolved values (hex or hsl). There is no runtime OKLCH computation, no runtime colour derivation, no slot-reference indirection, no dynamic generation of any kind. This is a deliberate architectural decision (TH-6) — it is not an oversight, not a temporary constraint, and not an invitation for a future agent to "improve" it. The wishlist items in §3 are explicitly and permanently deferred until the user explicitly reopens them. They cannot be reopened by agent initiative.
Sections §1, §2, and §3 are Layer 1 — they define what the system does for users. These sections must not be changed without an explicit user instruction. An agent may not reinterpret, consolidate, paraphrase, expand, or "improve" Layer 1 content unilaterally, even if the change appears cosmetic or editorial.
What "explicit user instruction" means: The user must have said, in a message in this session, words that clearly direct the specific change being made. "Can you update the spec" is not an explicit instruction for a Layer 1 change unless it specifies what to change. "Add a note to §3 that says X" is an explicit instruction. "Make it clearer" applied to a Layer 1 section is not — ask which part and what the intended meaning is. When in doubt, ask. One clarifying question costs nothing. An unauthorised Layer 1 change costs trust.
Every entry in §4 (Technical Design Decisions) must be traceable to a specific Layer 1 decision. An agent may not add a Layer 2 design decision that cannot be directly traced to a user functionality intent in §1, §2, or §3. Surface to the user only if a genuine impactful design choice must be made — do not ask about implementation details.
Layer 3 and Layer 4 concerns are the agent's responsibility. The user must not be asked about CSS specificity strategy, token naming conventions, storage key formats, SFL registration details, or any other implementation detail unless the decision has genuine, direct, user-visible site-wide impact that the user has not already delegated. The user hired an agent precisely so they do not have to think about those things. Asking about them wastes the user's time and signals that the agent has not read this spec.
Every CSS reference to a theme token in every widget must use the three-level fallback pattern: var(--mfw-token, var(--mf-token, #hardcoded-fallback))
. This is the dual-namespace resilience strategy documented in §4. A publish failure at any single level must not break rendering. The hardcoded fallback must be a valid, sensible default for the property — not initial
, not inherit
, not a blank string. A widget that references a theme token without all three levels is incorrectly implemented and must be corrected before it can be considered migrated.
Pattern:
color: var(--mfw-text, var(--mf-text, #1a1a1a));
var(--mfw-token)
reference with no fallback. DO NOT omit the hardcoded third level. DO NOT accept a two-level fallback as complete.If you are tempted to skip any of this because the change looks small, obvious, or urgent: that is precisely the situation these rules were written for. "Small and obvious" is how breaking changes sneak in. "Urgent" is how workflow discipline collapses. The rules are short. The workflow is nine steps. Reading §P1 and §P2 takes three minutes. The cost of getting this wrong is measured in broken widgets across the entire site. Do the three minutes.
Before you make any change: read §P1. Read §P2 (this section, in full). Read the Governing Article. Read the relevant body sections. Follow the 9-step workflow. Get Step 5 confirmation before writing code. If you are uncertain whether something counts as an "explicit user instruction" — ask. One clarifying question is always better than an unauthorised change.
This specification follows a decision hierarchy. Every section belongs to one of four layers, and decisions in higher layers constrain and inform decisions in lower layers. A future agent reading this document must respect that ordering — a technical implementation decision cannot override a user functionality decision.
What the system does for users. What they see, choose, and control. These decisions are owned by the user. Agents do not make them unilaterally. No user functionality decision requires technical justification to be valid.
The design decisions the user delegates to the agent. These are genuinely design — they affect how the system looks and behaves — but they are not interesting to the user and the user should not be asked about them. Examples: which DOM groupings receive which token layer, how the colour scale maps to semantic roles, specificity strategy, spacing scale rationale. Documented here so future agents can see the reasoning and not reinvent it. Each entry is traceable to the user intent that drove it.
How the system will be built, in what order, with what migration path. ThemeBridge phasing, widget-by-widget rollout, test theme sequencing. Technical but not code.
API contracts, token names, object structures, storage keys. Constrained by all layers above. Changes here require consistency with the layers above — not the other way around.
A user functionality decision flows downward through all layers automatically. If a user decides to add a new palette, that decision propagates: it requires a Technical Design Decision (what is the palette's colour character and scale mapping), which requires an Implementation Strategy entry (when and how it gets built), which requires Code (the token values). The agent handles layers 2–4. The user owns layer 1.
- Layer 1 section — do not change without an explicit, specific user instruction in this session that names or clearly identifies what to change. A general "can you update the spec" is not sufficient for Layer 1. If uncertain, ask.
- Layer 2 section — changes require a pre-write declaration and must be traceable to a named Layer 1 decision; surface to the user only if a genuine impactful choice must be made that the user has not already delegated
- Layer 3 or 4 section — standard SFL workflow applies; user is not asked about implementation details unless a decision has direct, site-wide, user-visible impact
- Never promote a Layer 3 or 4 concern into a Layer 1 conversation — the user hired an agent precisely so they don't have to think about those things
This site uses a single, coherent visual language across all its custom widgets. The Theme Control system is how that visual language is defined, stored, and changed. Instead of hunting through individual widgets to adjust colours or spacing, a user selects from a set of named, pre-designed themes — each with its own distinct personality — and the change applies everywhere at once.
The system is designed to make professional-quality design decisions accessible without requiring design expertise: the hard decisions are pre-made, the names tell you what you're getting, and the result looks intentional.
The theme system gives users six independent controls. Each can be set separately — pick any combination. Together, the selections define the complete visual character of the site's custom widgets.
A named, pre-designed set of colours with a defined personality. Covers all foreground colours, backgrounds, tints, and overlays across every widget. Choosing a palette is choosing the mood of the entire site's custom UI. There are 18 palettes in v1.0 — see §3.
Set via:
Design panel — palette dropdown. Writes all Layer 2 colour tokens and Layer 3 shadow tokens to :root
.
A named font pairing covering display headings (H1, H2), body text, and UI / table / label text. Changing the font hierarchy changes the typographic character of all widgets simultaneously. Font pairings are pre-selected for legibility and character — the user picks a name, not a font file. Each pairing also carries a scale factor(set by the admin at save time) for optical size compensation — some fonts appear larger or smaller than their nominal size, and the scale factor corrects this at the token level without changing font-size values across widgets.
Set via: Design panel — font hierarchy dropdown.
The nuances of the UI's physical feel — corner radius on buttons and cards, table line weights, form background shades, modal overlays. Selecting a surface style changes whether the UI feels sharp and precise, soft and friendly, or flat and minimal. A set of named presets covers the realistic range.
Set via: Design panel — surface style dropdown. The following variants are also set in the Design panel as independent controls on top of the selected preset:
- Shadow toggle
— shadows on or off site-wide. Default: off. When on,
--mfw-shadowand--mfw-shadow-lg(pre-calculated per palette character) are active. When off both tokens resolve tonone. Set via Design panel checkbox. - Group-By display option
— three states controlling how table group-by sections are rendered. Set via Design panel dropdown:
- "With Section Header Only" — group-by sections show a section header; no column headers repeated
- "With Section and Column Headers" — group-by sections repeat the column header row (default)
- "Treat Sections as Separate Tables" — group-by sections visually separated as distinct sub-tables
- Badge style per categorical scale — each of the three categorical scales (Scale 1, Scale 2, Scale 3) has its own badge style/feel. Dimensions: corner radius, border treatment (filled / outlined / subtle background only), size/padding, font weight. Initial values reverse-engineered from existing badges in widgets 85ecf1 and f28ff9. Set via Design panel. To be refined after initial implementation.
How quickly and smoothly elements respond to interaction — hover states, panel opens, page transitions. A named motion preset covers duration and easing as a matched set. "Snappy" feels immediate and precise; "Fluid" feels considered and calm; "None" removes all animation for accessibility or preference.
Set via: Design panel — motion preset dropdown. Writes all Layer 4 duration and easing tokens.
An independent control — not derived from or tied to the active colour palette. Governs four semantic status states: success, warning, danger, and neutral. Two named presets: Vivid(strong, saturated colours) and Muted(softened, desaturated variants). For each preset, each state has a pre-calculated background colour and a readable text-on-background colour. Anchor colours for each state are selected from the 12 pre-calculated anchor colours defined in §5.
Set via: Design panel — status palette dropdown (Vivid / Muted). Writes the 8 status tokens.
Three named categorical scales — Scale 1, Scale 2, Scale 3 — for colour-coding arbitrary user-defined label sets (e.g. attendance status, interest level, relationship type). Each scale has 8 colour slots. Per slot, the user selects one of 12 pre-calculated anchor colours. Vivid or Muted applies across all slots of a scale simultaneously, controlling the background and text shading of every slot's badge.
Set via: Design panel — per-scale Vivid/Muted dropdown and per-slot anchor colour dropdowns. Writes the 48 categorical tokens.
Each scale also has a user-editable label (e.g. "Attendance Status") and per-slot labels (what displays on the badge) and data values (the machine value the slot maps to). These are managed in the site configuration UI — a separate tab or popup within this widget — as a one-time site setup. This is Content panel territory, not theme-token territory.
--mfw-*
tokens with their current resolved values — useful for implementation-phase palette tweaking). There are no interactive editing controls in the widget UI. No save/rollback/restoreDefaults toolbar. No live preview toggle. The preview reflects CSS custom properties as written by the Design panel.allowPublicUI
gate and its associated Content panel toggle are removed. The preview UI is always shown. See TH-24.These are the authoritative palette names and characters for this site. The name is what the user sees and selects. The character description defines the palette's personality — it is the brief that the colour values must fulfil. No palette may be renamed or removed without explicit user agreement and a spec update.
| Name | Palette character |
|---|---|
| Thursday | Mission Impossible tactical. Deadpan name, dramatic palette. Deep charcoal, near-black, cool mid-grey, pure white, cold electric blue or hard steel accent. Something serious is happening. Nobody is reacting. |
| Nobody Move | Frozen. Ice blue, stark white, a single alarming red. Something has been broken. |
| Not My Department | Corporate beige. The colour of a form in triplicate. Institutional and indifferent. |
| It's Complicated | Warm but unresolved. Conflicting tones that shouldn't work together but somehow do. |
| Leaves On The Line | Autumnal amber, rust, wet tarmac grey. Genuinely beautiful. Genuinely the problem. |
| The NHS Waiting Room | Wipe-clean teal, magnolia, plastic chair blue. Strangely comforting. |
| Mostly Harmless | Friendly, unassuming, slightly washed out. Douglas Adams would approve. |
| Mind The Gap | London Underground roundel red and blue. Iconic. No argument. |
| Keep Calm And Carry On | Crown red, white, authoritative. Wartime poster energy. |
| Four O'Clock Tea Time | Warm cream, biscuit, bone china blue, a hint of Earl Grey. |
| Jumping The Queue | Polished, orderly, slightly passive-aggressive. British racing green, cream, burgundy. |
| Marmite Not Bovril | Dark brown, deep gold, sharp yellow. Divisive by design — you know immediately which side you're on. |
| Mostly Cloudy With Sunny Spells | 80% grey, 20% unexpected warmth breaking through. The national weather in a palette. |
| Strictly Come Dancing | Sequin gold, spray-tan orange, hot magenta, rhinestone white. Aggressively hideous. Unapologetically so. Also the diagnostic test theme candidate — see §9. |
| The Shipping Forecast | Slate, fog, deep sea navy, the faintest lighthouse amber. Oddly soothing. |
| Could Be Worse | Muted, resigned, oddly pleasant. The colour of low expectations gently exceeded. |
| Sheer Volume Of Traffic | Brake-light red, tarmac grey, white line yellow, fluorescent orange cone. |
| We Are Working Out Of Sight | Hi-vis yellow, traffic cone orange, grey concrete — and then absolutely nothing happening. |
is_default
flags. If Airtable and §3 ever conflict on a palette name or character, §3 wins. §3 does not need to be updated when token values are tweaked in Airtable — only name or character changes require a §3 spec update via the full 9-step workflow.Machine keys are kebab-case versions of the display names (e.g. the-shipping-forecast
). No index numbers — those were a working device used only during the design investigation phase and are not part of the spec. Palette object structure and token mapping are defined in §5.
These are design decisions delegated entirely to the agent. They affect how the system looks and behaves but are not decisions the user needs to be consulted on. Each is documented here so future agents can see the reasoning and not reinvent it. Every entry is traceable to a Layer 1 user intent.
| DOM grouping | Token layer | Example tokens |
|---|---|---|
| Page / widget background | Layer 2 — surface | --mfw-surface
, --mfw-surface-raised
|
| Primary headings (H1, H2) | Layer 2 — brand / text | --mfw-brand
, --mfw-text
|
| Secondary headings (H3, H4) | Layer 2 — text | --mfw-text
, --mfw-text-muted
|
| Body / paragraph text | Layer 2 — text | --mfw-text
|
| Helper / caption / muted text | Layer 2 — text-muted | --mfw-text-muted
|
| Primary action buttons | Layer 3 — btn | --mfw-btn-bg
, --mfw-btn-text
|
| Form inputs | Layer 3 — input | --mfw-input-bg
, --mfw-input-border
|
| Table headers | Layer 2 — brand / surface-raised | --mfw-brand
, --mfw-surface-raised
|
| Table rows (alternating) | Layer 2 — surface / surface-subtle | --mfw-surface
, --mfw-surface-subtle
|
| Card / panel containers | Layer 2 — surface-raised | --mfw-surface-raised
, --mfw-border
|
| Dividers / separators | Layer 2 — divider / border | --mfw-divider
, --mfw-border
|
| Focus rings | Layer 2 — focus | --mfw-focus
|
| Accent / highlight elements | Layer 2 — accent | --mfw-accent
|
| All interactive transitions | Layer 4 — motion | --mfw-duration-base
, --mfw-easing-standard
|
Each palette has a 10-step lightness scale (col1 lightest → col10 darkest). Semantic roles are mapped to scale steps at authoring time as follows:
| Scale steps | Typical semantic role |
|---|---|
| col1 – col2 | Page surfaces, backgrounds, lightest tints |
| col3 – col4 | Raised surfaces, subtle backgrounds, input backgrounds |
| col5 – col6 | Brand / primary action, accent, interactive elements |
| col7 – col8 | Border colours, dividers, muted text |
| col9 – col10 | Primary text, headings, deep backgrounds |
Palettes with inverted character (e.g. dark-background themes) may reassign steps — the mapping above is a convention, not a constraint. The palette character description in §3 is the authoritative brief.
!important
and CSS variable resolution!important
to override platform-injected styles)CSS custom properties resolve at paint time as value substitution — they do not participate in the specificity cascade. A var(--mfw-brand)
reference in any rule resolves to whatever value was last written to :root
, regardless of the rule's specificity. This sidesteps the platform specificity war entirely for token-consuming widgets.
The !important
pattern is therefore unnecessary for any widget that has fully migrated to --mfw-*
token consumption. It remains acceptable as a targeted override for specific platform conflicts that cannot be resolved via token substitution.
--mfw-*
bridge over --mf-*
All widget CSS references tokens as var(--mfw-token, var(--mf-token, #hardcoded-fallback))
. Three levels of resolution: bridge layer → underlying layer → hardcoded fallback. A publish failure at any single level does not break rendering. This three-level pattern is mandatory for every theme token reference in every widget — see §P2 Rule 10. A two-level or bare reference is a defect.
Four spacing steps ( --mfw-space-1
through --mfw-space-4
) cover the practical range from tight inline padding to generous section spacing. Steps are not mathematically fixed — they are chosen per palette to suit the palette's character. A spacious, airy palette (e.g. Four O'Clock Tea Time) will use larger step values than a dense, institutional palette (e.g. Not My Department).
Three radius tokens: --mfw-radius-sm
(tags, pills, small chips), --mfw-radius
(buttons, inputs, standard cards), --mfw-radius-lg
(modals, large panels). All three scale together — the surface style preset sets all three as a matched set. A "Sharp" preset sets all three to 0 or near-0; a "Rounded" preset sets generous values across all three.
--mfw-shadow
and --mfw-shadow-lg
are placed in §5 Layer 3 (Component Surfaces) alongside the radius family. Values are pre-calculated per palette character — flat/institutional palettes (e.g. Not My Department) receive near-zero or none
shadow values; warmer/softer palettes (e.g. Four O'Clock Tea Time) receive more lift. Shadow is a Control 3 variant (on/off toggle) rather than a preset property because it is orthogonal to the radius/spacing character — a Sharp preset may be used with or without shadows. When the shadow toggle is off, both tokens resolve to none
. These tokens must only be applied to box-shadow
properties — not to other properties.
Status colours (success, warning, danger, neutral) are an independent fifth control with their own Vivid / Muted presets. Placed in §5 Layer 2 (Semantic Roles) because they are semantic meanings applied to content states — not component-surface properties. Independence from the main palette is mandatory: tying status colours to the main palette would risk illegible or contradictory signals (e.g. a red-dominant palette producing a green success badge that reads as an accent, not a positive state). Anchor colours for each status state are selected from the shared 12-colour anchor set defined in §5.
12 agent-generated anchor colours covering the colour wheel, each with Vivid and Muted variants pre-calculated at design time (background shade + readable text shade). Users select one of the 12 anchors per slot; Vivid / Muted applies across the whole scale. All values are pre-resolved — fully Rule 5 compliant. 48 categorical tokens total: 3 scales × 8 slots × 2 (background + text). Token naming: --mfw-cat-S-N-bg
and --mfw-cat-S-N-text
where S = scale number (1–3) and N = slot number (1–8).
Scale labels (e.g. "Attendance Status"), slot labels (what displays on the badge), and slot data values (machine matching values) are housed in a separate tab or popup within the theme widget. Placing them in the theme controls would conflate theme-switching with data configuration. The separation makes the distinction explicit to the user: the theme widget's main controls govern appearance; the configuration UI governs meaning.
Badge style tokens ( --mfw-badge-S-radius
, --mfw-badge-S-border
, --mfw-badge-S-padding
, --mfw-badge-S-font-weight
for S = 1–3) are placed in §5 Layer 3 alongside other surface tokens. Each scale gets its own badge style so that visually distinct scale types can coexist on the same page without ambiguity. Initial values reverse-engineered from existing badge implementations in widgets 85ecf1 (Profile Card) and f28ff9 (Contact Log). Refinement deferred post initial implementation.
--mfw-table-col-header-bg
, --mfw-table-col-header-text
, --mfw-table-group-header-bg
, and --mfw-table-group-header-text
are placed in §5 Layer 3 (Component Surfaces). These tokens are more specific than the general --mfw-surface-raised
/ --mfw-brand
tokens — consuming widgets must use these specific tokens for table column and group-by headers post-migration. The three-state group-by display option is a Control 3 variant (orthogonal to the radius/spacing preset) because layout decisions for table sections are independent of the overall surface style. The "Treat Sections as Separate Tables" state may require JS rendering changes in consuming widgets — flagged as a Step 8 concern for f05985 migration.
Four layers. All values stored pre-calculated — no runtime resolution, no slot-reference indirection. YAGNI applies throughout. Every token reference in widget CSS must use the three-level fallback: var(--mfw-token, var(--mf-token, #hardcoded-fallback))
— see §P2 Rule 10 and §4 namespace decision.
Raw colour scale, 10 slots per palette. col1
= lightest → col10
= darkest. No separate accent slots — accents are semantic role mappings pointing at specific scale steps.
{
id: 'thursday',
name: 'Thursday',
palette: { col1:'#…', col2:'#…', … col10:'#…' },
tokens: { '--mfw-brand':'#…', '--mfw-surface':'#…', … },
meta: { author:'system', created:'YYYY-MM-DD', tags:[] }
}
| Token | Purpose |
|---|---|
--mfw-brand
|
Brand / primary action colour |
--mfw-brand-text
|
Contrast text on brand background |
--mfw-accent
|
Secondary accent colour |
--mfw-surface
|
Base page / widget surface |
--mfw-surface-raised
|
Elevated card or panel background |
--mfw-surface-subtle
|
Muted / tinted section background |
--mfw-text
|
Primary body text |
--mfw-text-muted
|
Secondary / hint / caption text |
--mfw-text-on-brand
|
Text on brand-coloured backgrounds |
--mfw-border
|
Standard border and separator |
--mfw-divider
|
Lighter interior divider |
--mfw-focus
|
Keyboard focus ring |
| Status tokens — independent of main palette; values pre-calculated per Vivid / Muted preset (Control 5) | |
--mfw-status-success
|
Success state background colour |
--mfw-status-success-text
|
Readable text on success background |
--mfw-status-warning
|
Warning state background colour |
--mfw-status-warning-text
|
Readable text on warning background |
--mfw-status-danger
|
Danger / error state background colour |
--mfw-status-danger-text
|
Readable text on danger background |
--mfw-status-neutral
|
Neutral / informational state background colour |
--mfw-status-neutral-text
|
Readable text on neutral background |
| Token | Purpose |
|---|---|
--mfw-input-bg
|
Form input background |
--mfw-input-text
|
Form input text colour |
--mfw-input-border
|
Form input border |
--mfw-btn-bg
|
Button background |
--mfw-btn-text
|
Button text colour |
--mfw-btn-border
|
Button border |
--mfw-radius
|
Standard corner radius |
--mfw-radius-sm
|
Small corner radius (tags, pills) |
--mfw-radius-lg
|
Large corner radius (modals, cards) |
--mfw-space-1
… --mfw-space-4
|
Spacing scale — tightest to loosest |
Shadow tokens — pre-calculated per palette character; resolve to none
when shadow toggle is off. Apply to box-shadow
only. |
|
--mfw-shadow
|
Standard box shadow for raised surfaces |
--mfw-shadow-lg
|
Stronger box shadow for modals and overlays |
| Table header tokens — use these for column and group-by headers, not the general surface-raised / brand tokens, post-migration | |
--mfw-table-col-header-bg
|
Table column header background |
--mfw-table-col-header-text
|
Table column header text colour |
--mfw-table-group-header-bg
|
Group-by section header background |
--mfw-table-group-header-text
|
Group-by section header text colour |
| Badge style tokens — per categorical scale (S = 1, 2, 3); initial values reverse-engineered from 85ecf1 and f28ff9 | |
--mfw-badge-1-radius
|
Corner radius for Scale 1 badges |
--mfw-badge-1-border
|
Border treatment for Scale 1 badges (e.g. filled / outlined) |
--mfw-badge-1-padding
|
Padding for Scale 1 badges |
--mfw-badge-1-font-weight
|
Font weight for Scale 1 badges |
--mfw-badge-2-radius
|
Corner radius for Scale 2 badges |
--mfw-badge-2-border
|
Border treatment for Scale 2 badges |
--mfw-badge-2-padding
|
Padding for Scale 2 badges |
--mfw-badge-2-font-weight
|
Font weight for Scale 2 badges |
--mfw-badge-3-radius
|
Corner radius for Scale 3 badges |
--mfw-badge-3-border
|
Border treatment for Scale 3 badges |
--mfw-badge-3-padding
|
Padding for Scale 3 badges |
--mfw-badge-3-font-weight
|
Font weight for Scale 3 badges |
Popup tokens — surfaces for draggable display popups and input form popups. --mfw-shadow-lg
applies to the panel itself (already in Layer 3 above). Pre-release: breaking changes accepted. |
|
--mfw-popup-overlay
|
Overlay / scrim behind the popup panel |
--mfw-popup-bg
|
Main popup panel background (mirrors --mfw-surface
) |
--mfw-popup-border
|
Popup panel border |
--mfw-popup-header-bg
|
Draggable header bar background — primary chromatic statement of the popup; should follow the same logic as --mfw-table-col-header-bg
|
--mfw-popup-header-text
|
Text on the popup header bar |
12 agent-generated anchor colours covering the colour wheel. Each anchor has two pre-calculated variants. All values are pre-resolved — fully Rule 5 compliant. Used by both Control 5 (status palette anchor selection) and Control 6 (categorical scale slot selection).
Vivid: Saturated background shade + high-contrast text shade. Muted: Desaturated, softened background shade + readable text shade.
| Slot | Colour name | Anchor hex | Vivid bg | Vivid text | Muted bg | Muted text |
|---|---|---|---|---|---|---|
| 1 | Red | #ef4444 | #fca5a5 | #7f1d1d | #fee2e2 | #991b1b |
| 2 | Orange | #f97316 | #fdba74 | #7c2d12 | #ffedd5 | #9a3412 |
| 3 | Amber | #f59e0b | #fcd34d | #78350f | #fef3c7 | #92400e |
| 4 | Yellow | #eab308 | #fde047 | #713f12 | #fefce8 | #854d0e |
| 5 | Lime | #84cc16 | #bef264 | #1a2e05 | #f7fee7 | #365314 |
| 6 | Green | #22c55e | #86efac | #14532d | #dcfce7 | #166534 |
| 7 | Teal | #14b8a6 | #5eead4 | #134e4a | #ccfbf1 | #115e59 |
| 8 | Cyan | #06b6d4 | #67e8f9 | #164e63 | #cffafe | #155e75 |
| 9 | Blue | #3b82f6 | #93c5fd | #1e3a8a | #dbeafe | #1e40af |
| 10 | Indigo | #6366f1 | #a5b4fc | #312e81 | #e0e7ff | #3730a3 |
| 11 | Purple | #a855f7 | #d8b4fe | #4a044e | #f3e8ff | #6b21a8 |
| 12 | Pink | #ec4899 | #f9a8d4 | #831843 | #fce7f3 | #9d174d |
For each scale S (1–3) and each slot N (1–8). Token values resolve to the Vivid or Muted pre-calculated values of the anchor colour selected for that slot. Total: 3 × 8 × 2 = 48 tokens.
| Token pattern | Purpose |
|---|---|
--mfw-cat-S-N-bg
|
Background colour for scale S, slot N (resolves to Vivid or Muted bg of selected anchor) |
--mfw-cat-S-N-text
|
Text colour for scale S, slot N (resolves to Vivid or Muted text of selected anchor) |
Example: --mfw-cat-1-3-bg
= Scale 1, Slot 3 background. --mfw-cat-2-7-text
= Scale 2, Slot 7 text colour.
| Token | Purpose / example |
|---|---|
--mfw-duration-fast
|
e.g. 120ms |
--mfw-duration-base
|
e.g. 220ms |
--mfw-duration-slow
|
e.g. 380ms |
--mfw-easing-standard
|
e.g. cubic-bezier(0.4, 0, 0.2, 1) |
--mfw-easing-enter
|
Element entering the viewport |
--mfw-easing-exit
|
Element leaving the viewport |
--mfw-font-family-heading
, --mfw-font-family-body
, --mfw-font-family-ui
, and --mfw-font-scale
— are now implemented in Layer 5 (v2.3). See TH-31 in §10 and the Font preset table in §3 (forthcoming in v2.3 spec revision). Extended typography tokens ( --mfw-font-size-base
, --mfw-font-scale-ratio
, --mfw-font-weight-heading
etc.) remain deferred to a future revision.| Method | Signature & contract |
|---|---|
apply(tokens?)
|
Writes CSS custom properties to :root
. tokens
optional — if omitted, applies currently saved theme. Called by widget JS on boot and whenever Design panel values change. Returns void. |
save({ tokens })
|
Persists token map to localStorage. Stores previous state for rollback. In v2.1, token persistence is handled by Duda's Design panel storage — this method is retained for programmatic/SFL use but is not exposed as a user-facing button in the widget UI. Returns void. |
rollback()
|
Restores and applies previous saved theme. Retained for programmatic/SFL use — not exposed as a user-facing button in the widget UI (v2.1). Returns { tokens }
or null. |
restoreDefaults()
|
Resets to built-in defaults and saves. Retained for programmatic/SFL use — not exposed as a user-facing button in the widget UI (v2.1). Returns { tokens }
. |
getDashboardPageUuid()
|
Historical placement. Dashboard routing utility — not first-class theme API. Migration candidate (TH-8). Returns string or null. |
mf_
prefix. Example: mf_theme.v1.tokens
· mf_theme.v1.previous
Migration and resilience aid. Allows widget-by-widget theme adoption without a big-bang CSS rewrite. Once a widget directly consumes --mfw-*
tokens, ThemeBridge is no longer needed for that widget — it is scaffolding, not a permanent fixture.
| Method | Signature & contract |
|---|---|
applyToElement(element, options)
|
Applies theme tokens scoped to a DOM element rather than :root
.options: { widget: { name, instanceId }, useGlobalTheme: boolean, overrides: object|null }
Returns void. Implementation must conform to this contract. |
Widget-by-widget migration using __mfThemeBridge
for continuity. No widget is ever in a broken state during migration. Big-bang migration is explicitly ruled out — platform publish unreliability makes it unacceptable.
| Phase | Name | What happens | What it finds |
|---|---|---|---|
| A | Audit & Standardise | All widgets audited. Existing local token systems mapped to --mfw-*
structure. No runtime changes. |
The full migration scope |
| B | Diagnostic — The Ugly Theme | Garish test palette applied site-wide. Widgets that don't respond are not wired to the token system — logged as migration targets. | Broken wiring |
| C | Harmony Test — Triadic Theme | Triadic colour scheme (120° apart) applied per token hierarchy. If it looks right, the system is ready for production palettes. | Broken hierarchy |
Candidate: strictly-come-dancing
. Every semantic layer gets a visually distinct, clashing colour. Broken wiring is immediately visible.
| Layer | Diagnostic colour |
|---|---|
| brand/primary | Hot magenta |
| surface | Sequin gold (pale) |
| text | Deep rhinestone contrast |
| accent | Spray-tan orange |
| border/divider | Rhinestone white |
| input background | A clearly distinct tint |
Three equidistant hues (120° apart) assigned to brand, accent, and surface roles. If the hierarchy is correct, the result looks intentionally designed. Any mis-mapped token breaks the triadic relationship visibly and localises the fault.
| Gap | Decision & rationale |
|---|---|
| TH-1 | Spec location: widget.html of 7430a6, modelled on 6dbf3a. Theme Editor is the natural carrier for its own spec. |
| TH-2 | Federation: standard highest-version-wins. 7430a6 is spec home by convention only — no canonical-source privilege. |
| TH-3 | __mfTheme
tier: required. Site-wide theme ambition requires it on every carrier. |
| TH-4 | __mfThemeBridge
tier: optional. Migration scaffolding only — not needed post-migration. |
| TH-5 | --mfw-*
bridge namespace retained. Platform publish unreliability justifies the extra resilience layer. |
| TH-6 | Token values: pre-calculated, pre-resolved only. No runtime OKLCH, no slot-reference indirection. YAGNI. |
| TH-7 | Palette generation parameters: not stored. Wishlist, deferred. Breaking-change risk accepted. |
| TH-8 | getDashboardPageUuid()
retained on __mfTheme
. Historical placement. Migration candidate — decision owned by 6dbf3a. |
| TH-9 | Palette slot count: 10 (col1–col10). No separate accent slots. Converges with Material, Radix, Tailwind. |
| TH-10 | Migration: widget-by-widget. Big-bang ruled out. Platform publish unreliability makes it unacceptable. |
| TH-11 | Index numbers on palette names: disposed of. Working device only — not part of the spec. |
| TH-13 | Three-level token fallback var(--mfw-*, var(--mf-*, #hardcoded))
mandated for all widget CSS references. Two-level or bare references are defects. Rule 10 added to §P2. |
| TH-14 | Shadow tokens placed in §5 Layer 3 (Component Surfaces) alongside radius family. Values pre-calculated per palette character. Shadow toggle is a Control 3 variant — orthogonal to radius/spacing presets. Default: off. Both tokens resolve to none
when shadow is off. Tokens apply to box-shadow
only. |
| TH-15 | Status colours are an independent fifth control (Control 5). Placed in §5 Layer 2 (Semantic Roles) because they carry semantic meaning applied to content states. Independence from main palette is mandatory for legibility consistency. Eight tokens total: four states × background + text. |
| TH-16 | Categorical scales use 12 agent-generated anchor colours with pre-calculated Vivid/Muted variants. Fully Rule 5 compliant — all values pre-resolved at design time. 48 categorical tokens total (3 scales × 8 slots × 2). Anchor colour table locked in §5 Layer 3. |
| TH-17 | Site configuration UI (scale labels, slot labels, data values) housed in a separate tab/popup within the theme widget. Separation prevents conflation of theme-switching with data configuration. |
| TH-18 | Badge styling tokens placed in §5 Layer 3 per scale (Scale 1, 2, 3). Initial values reverse-engineered from 85ecf1 and f28ff9. Refinement deferred post initial implementation. |
| TH-19 | Table group-by tokens placed in §5 Layer 3. Group-by display option is a three-state Control 3 variant. Default: "With Section and Column Headers". "Treat Sections as Separate Tables" state may require JS rendering changes in consuming widgets — flagged as Step 8 concern for f05985 migration. |
| TH-20 | Hub-and-spoke architecture for __mfTheme
; corrected carrier registration.
Prior spec versions incorrectly listed f28ff9
(Contact Log) as the carrier for __mfTheme
. This was an error: f28ff9
was never designed to host or maintain the theme library — only widget 7430a6
(Theme Editor) can edit __mfTheme
. Corrected: 7430a6
is now the hub carrier; consuming widgets (e.g. f28ff9
, f05985
, 85ecf1
) carry __mfTheme
as optional
tier once they adopt it. The hub-and-spoke model means the standard peer-federation sync assumption does not apply to __mfTheme
— it must be edited in 7430a6
only, not synced by the session-start procedure from another carrier. __mfTheme
is formally registered in __mfRegistry._libraries
on 7430a6
and carried as optional
in 7430a6
's SFL_CARRIER_INDEX entry. Sync limitation (accepted architectural constraint):
Theme edits do not propagate automatically to other pages without a full SFL sync or manual Dashboard sync. This is a known limitation of the hub-and-spoke model and is accepted without remediation in this spec version. |
| TH-21 | Design panel is the primary token-writing surface (v2.1).
All six theme controls (colour palette, font hierarchy, surface & form style, motion, status palette, categorical scales) are set through the widget's Design panel. The Design panel writes CSS custom properties directly — this is the canonical input surface. Prior versions expected the widget's JavaScript runtime to collect inputs from the widget UI and call __mfTheme.save()
. In v2.1, the Design panel is the input surface and token persistence is handled by Duda's native Design panel storage. The widget JavaScript is responsible only for reading the Design panel values (via data
) and applying them to :root
via __mfTheme.apply()
. |
| TH-22 | Widget UI is preview-only (v2.1). The widget's visible on-page display is an administrator aid — primarily visible in the editor. It shows the result of the current Design panel selections as a live preview. There are no interactive editing controls in the widget UI: no save/rollback/restoreDefaults toolbar, no live preview toggle, no token input fields, no advanced token editor with inputs. The preview is read-only and updates automatically as CSS custom properties change. This is a clean architectural separation: Design panel = input; widget UI = output/preview. |
| TH-23 | Read-only full token map included in preview UI (v2.1).
The preview UI includes a collapsible read-only section displaying all --mfw-*
tokens and their current resolved values (read from getComputedStyle(document.documentElement)
). No inputs, no editing. This is explicitly useful during implementation-phase palette tweaking — the administrator can see the exact resolved hex values of every token as they adjust palette selections in the Design panel. This section is collapsed by default to keep the preview clean; labelled "Token Map" or similar. |
| TH-24 | allowPublicUI gate retired (v2.1).
The allowPublicUI
flag, its Content panel toggle, and the gate UI element ( data-mf-theme-cc-gate
) are all removed. The preview UI is always shown — there is no conditional gate. Rationale: the preview is read-only and has no user-facing editing capability, so there is no risk to public visitors. The gate was designed to prevent accidental edits on published pages; with an entirely read-only UI, that concern no longer applies. |
| TH-25 | Content panel scope reduction (v2.1).
The Content panel is reduced to configuration-only items that do not write theme tokens: (a) default palette name (used when no saved theme is found on first load — this is a fallback hint for the JS, not a token write); (b) display mode toggles (verification mode, monitor mode); (c) identity fields (friendly name, published version). The showAdvancedOpen
and showRefPanel
toggles are removed (the advanced editor no longer exists; the ref panel is always shown). The allowPublicUI
toggle is removed (TH-24). No theme token selection belongs in the Content panel. |
| TH-26 | Airtable Themes table introduced (v2.2).
A new Airtable table named Themes
stores all theme rows. Fields: Theme Name
(text), Theme Type
(single select: Colour | Font | Badge | Transitions | Surface | Status | Categorical), Is Default
(boolean — one true per type, enforced by Theme BO setDefault()
), Theme Definition
(long text / JSON — holds input tokens/values for that type only; does not store derived or expanded token maps). This table is the runtime source of truth for all theme data. The in-code constants (PALETTES, SURFACE_PRESETS, etc.) become seed-only artefacts once Airtable is confirmed populated. |
| TH-27 | Theme Business Object introduced (v2.2).
A new BO named Theme
is implemented inside __mfBusinessObjects
v1.19. Public API: getByType(type)
, getDefault(type)
(returns null if no default — callers must handle), setDefault(themeId, type)
(two-write transaction: clear old default → set new), save(themeData)
, update(themeId, themeData)
, delete(themeId)
(refuses if is_default=true — caller must assign a new default first via setDefault). Layer: business-logic, optional, requires: [ __mfAirtable
]. |
| TH-28 | ThemeSeeder.run() seed function introduced (v2.2).
Implemented as __mfBusinessObjects.ThemeSeeder.run(constants)
. Reads all six in-code constant arrays (PALETTES, SURFACE_PRESETS, MOTION_PRESETS, STATUS_PRESETS, CAT_SCALES, BADGE_STYLE_DEFAULTS) and writes one Airtable row per entry. Default row assignment: Colour → the-shipping-forecast
; Surface → default
; Transitions → default
; Status → vivid
; Categorical → Scale 1 (id=1); Badge → single combined row (is_default=true). Exposed via window.__mfThemeSeeder.run()
for one-time browser-console execution. Purpose: validates the full write path. If this fails, no further Airtable work proceeds. |
| TH-29 | CRUD admin UI replaces preview-only UI (v2.2).
The widget's visible display is rebuilt as a simple admin CRUD interface (no public-facing display, no read-only preview mode). UI shape: type-filter tab bar (Colour | Surface | Transitions | Status | Categorical | Badge) → list of themes per type with name + is_default indicator → edit form on row click → Set as Default button per row → New Theme button → Delete button (disabled when is_default=true). No inline editing — form only. Admin-only access enforced by page-level access control; no in-widget is_admin check in this spec version. __mfTheme.apply()
called at preview time within the editor only. Implemented in Checkpoints CP-4a through CP-4e. |
| TH-30 | In-code constants formally retired as seed-only artefacts (v3.1 complete).
PALETTES, ELEMENT_VIEW_SECTIONS, SURFACE_PRESETS, MOTION_PRESETS, STATUS_PRESETS, CAT_SCALES, BADGE_STYLE_DEFAULTS, TABLE_HEADER_DEFAULTS, and FONT_PRESETS are all seed-only artefacts. All runtime rendering reads exclusively from THEME_MAP. These constants are not consumed by any rendering code path. Once Airtable seeding is confirmed and the Theme BO is live, comment them all out (do not delete — they are the documented source of truth and recovery reference). window.__mfThemeSeeder
and all seeder wiring also become inert after seeding is confirmed. |
| TH-33 | theme_map field added to Airtable Themes table and wired end-to-end (v3.0).
__mfAirtable
v1.15 adds theme_map → 'Theme Map'
to the Themes table schema. __mfBusinessObjects
v1.20 wires theme_map
in Theme._norm()
(parse JSON on read) and Theme._denorm()
(serialise JSON on write), mirroring theme_definition
handling. ThemeSeeder.run()
accepts a themeMap
parameter (the THEME_MAP
constant); for each Colour row it attaches a per-palette Theme Map entry (palette Layer 1+2 data + the full Layer 3 elements array) as theme_map
. Non-Colour rows do not receive a theme_map
value. window.__mfThemeSeeder.run()
now passes THEME_MAP
. Colour edit form gains a third sub-tab "Theme Map" (alongside "Element Table" and "Theme Definition") showing the raw theme_map
JSON from Airtable in a monospace textarea. Save button is context-aware: writes theme_definition
when the Theme Definition tab is active, writes theme_map
when the Theme Map tab is active, closes the form when the Element Table tab is active. |
| Version | Date | Notes |
|---|---|---|
| 4.0 | 2026-05-08 | Option A selector inclusion: body-font dudaClasses extended in ELEMENT_VIEW_SECTIONS to include p and li in the three remaining high-specificity Duda container selectors (p_hfcontainer, flex_hfcontainer, dm-title). Previously only dmContent and dmFooter carried p/li high-specificity selectors; Option A completes the set so paragraph and list text inside any Duda container consistently uses the body font. Stale-map detection extended: Font rows whose body-font pageElements lack *#dm div.p_hfcontainer p are now treated as stale and rewritten on next Reset All Themes. Surface lightening: all 18 palettes had --mfw-surface, --mfw-surface-raised, and --mfw-surface-subtle values brightened by ~3–6% lightness for a lighter, airier feel while staying within each palette's character brief. __mfBusinessObjects v1.33 unchanged. Commit 78. |
| 3.9 | 2026-05-08 | body-font dudaClasses expanded with high-specificity *#dm div.* container selectors for h3–h6, p, and li elements. These beat Duda's own page stylesheet rules (e.g. *#dm div.dmContent h3 { font-family: ... }) which were overriding the lower-specificity #dm h3 selectors added in v3.6. Five containers covered: dmContent, dmFooter, p_hfcontainer, flex_hfcontainer, dm-title — mirroring the heading-font pattern already in place for h1/h2. Stale-map detection extended: Font rows whose body-font pageElements lack *#dm div.dmContent h3 are treated as stale and rewritten on next Reset All Themes. __mfBusinessObjects v1.32 → v1.33. Commit 76. |
| 3.8 | 2026-05-08 | Font Theme Map stale-map detection extended to include a missing --btn-text-font-family check. Font rows in Airtable whose pageElements array lacks a --btn-text-font-family dudaVar entry are now treated as stale and rewritten on the next Reset All Themes. This ensures --btn-text-font-family is present in the cssVars output of every Font theme_definition alongside the other Duda button style variables. The ui-font row in ELEMENT_VIEW_SECTIONS already had dudaVar: '--btn-text-font-family' (added in Commit 59), but rows seeded before that commit were not being updated. __mfBusinessObjects v1.31 → v1.32. Commit 75. |
| 3.7 | 2026-05-06 | --color_7, --color_8, --color_9 restored to ELEMENT_VIEW_SECTIONS brand-colours section. Three rows added: body-text (--mfw-text → --color_7), muted-text (--mfw-text-muted → --color_8), text-on-brand (--mfw-text-on-brand → --color_9). These were incorrectly dropped when typography colour rows were retired in Commit 58 — text colour tokens are not typography-section concerns, they are core semantic colours and must be present in ELEMENT_VIEW_SECTIONS so themeMapBuildDefinition() writes them into the Airtable theme_definition cssVars output. No token renames, no schema changes, no SFL infrastructure touched. Reseed required. Commit 71. |
| 3.6 | 2026-05-06 | ELEMENT_VIEW_SECTIONS typography rows expanded with full Duda h1–h6, default text, and paragraph text selectors. heading-font row now targets #dm h1/h2 plus .dmBody and .dmRespRow and .dmNewParagraph variants for h1–h2. body-font row now targets #dm h3–h6 plus #dm p, li, .dmBody (default text wrapper), .dmBody h3–h6, .dmBody p/li, .dmNewParagraph and .dmNewParagraph p (paragraph text). ui-font row now populated with Duda UI chrome selectors: .unifiednav, .unifiednav__item, .dmButtonLink, .dmform label/input/textarea/select, #dm span, #dm label. Row labels updated to be descriptive (Heading Font H1–2, Body Font H3–6 + Text, UI Font Nav/Forms/Labels). desc updated. These dudaClasses flow automatically into Theme Definition selectors via _buildCssVarsFromThreeLayer() and themeMapBuildDefinition(), producing CSS rules such as #dm h1 { font-family: "Playfair Display", Georgia, serif }. No new tokens, no schema changes, no SFL infrastructure touched. Commit 70. |
| 3.5 | 2026-05-06 | sampleText added to Font Theme Definition as a top-level property. Font theme_map description already carried sampleText (fp.sampleText). Theme Definition (theme_definition field) is produced by _buildCssVarsFromThreeLayer() returning { cssVars, selectors }; it previously lacked sampleText. Fix: after building fontDef, fontDef.sampleText = fp.sampleText is set so consumers can render a typographic specimen directly from the Theme Definition without a separate Theme Map fetch. PATCH Rule 2 (both-new-format → wholesale replace) ensures the field is written on next Sync. __mfBusinessObjects v1.28→v1.29. Commit 69. |
| 3.4 | 2026-05-06 | ThemeSeeder rewired to produce { cssVars, selectors } theme_definition for all types. Shared helper _buildCssVarsFromThreeLayer(propertyValues, widgetTokens, pageElements) added: walks widgetTokens→propertyValues for cssVars; walks pageElements for selectors. Surface/Transitions/Status: { tokens } → { cssVars, selectors:{} }. Categorical: { scales } → { cssVars (16 slot CSS vars), selectors:{} }. Font: { headingFontName, tokens, ... } → { cssVars (four --mfw-font-* vars), selectors (Duda font-family class mappings) }. Badge: { tokens } → { cssVars (12 badge CSS vars), selectors:{} }. Colour unchanged (themeMapBuildDefinition). __mfBusinessObjects v1.26→v1.27. Commit 67. |
| 3.3 | 2026-05-06 | Theme Map three-layer pattern extended to Categorical, Font, and Badge. Categorical: propertyValues=resolved slot colours (slot-S-N-bg/text keys, pre-resolved hex); widgetTokens=CSS var→key; pageElements=[]. Font: description captures full preset metadata; propertyValues={fontHeading,fontBody,fontUi,fontScale}; widgetTokens maps four CSS vars; pageElements=fontElements (Duda selector mappings, non-empty). Font theme_map now always seeded (no longer gated on themeMap). Badge: theme_map added for first time; description={id:"default",name:"Default Badge Styles"}; propertyValues=12 semantic keys; widgetTokens maps 12 CSS vars; pageElements=[]. Stale-map detection and _isThemeMapType extended to all seven types. __mfBusinessObjects v1.25→v1.26. Commit 66. |
| 3.2 | 2026-05-06 | Theme Map three-layer pattern extended to Surface, Transitions, and Status theme types. SURFACE_PRESETS, MOTION_PRESETS, STATUS_PRESETS each gain propertyValues (semantic key → final string value with units) and widgetTokens (CSS var name → propertyValues key, indirection not direct values) alongside the existing tokens map. ThemeSeeder.run() Surface/Transitions/Status theme_map format changed from old { themeType, preset: { id, name, tokens }, elements: [] } to new { description: { id, name }, propertyValues, widgetTokens, pageElements: [] }. Stale-map detection extended to cover Surface/Transitions/Status rows (old format lacks description key — treated as stale and rewritten on next sync). Categorical rows retain slot-based structure. __mfBusinessObjects v1.24→v1.25. Commit 65. |
| 3.1 | 2026-05-05 | CP-TM1–CP-TM5: all palette view renderers re-engineered to read from THEME_MAP. CP-TM1: renderPaletteMatrixBody band-sort and tally loops now iterate THEME_MAP.palettes; brand hex sourced from rawColors (Layer 1) with tokens fallback. CP-TM2: renderPaletteElementTable outer palette loop iterates THEME_MAP.palettes; section data built inline by grouping THEME_MAP.elements by sectionId (same shape as ELEMENT_VIEW_SECTIONS). COL_ASSIGNMENTS indices unchanged. CP-TM3: renderPaletteFingerprint outer loop iterates THEME_MAP.palettes. CP-TM4: _etHtml inside openColourEditForm section indexing replaced with same THEME_MAP.elements groupBy pattern. PALETTES and ELEMENT_VIEW_SECTIONS retained as upstream seed inputs — not deleted. Commit count 51→55. Spec bumped to v3.1. |
| 3.1 | 2026-05-05 | CP-TM1–CP-TM5: all palette view renderers re-engineered to read from THEME_MAP — THEME_MAP is now the sole runtime data source for all rendered palette data. CP-TM1: Full Matrix (renderPaletteMatrixBody) reads THEME_MAP.palettes for band-sort and harmony tally; brand classification uses rawColors.brandHex (Layer 1). CP-TM2: Element Table outer loop reads THEME_MAP.palettes; section data built inline by grouping THEME_MAP.elements by sectionId (same shape as ELEMENT_VIEW_SECTIONS). CP-TM3: Fingerprint Cards card loop reads THEME_MAP.palettes. CP-TM4: _etHtml in Colour edit form reads THEME_MAP.elements (same groupBy). CP-TM5: Colour tab card grid (renderColourFingerprintTab) reads THEME_MAP.palettes — last direct read of PALETTES in any renderer removed. PALETTES and ELEMENT_VIEW_SECTIONS header comments updated: formally declared seed-only artefacts. TH-30 gap decision updated to include ELEMENT_VIEW_SECTIONS and FONT_PRESETS in retirement scope, and to confirm no rendering code reads from any in-code constant any longer. Commit 57. Spec bumped to v3.1. |
| 3.0 | 2026-05-05 | CP-1–CP-6: theme_map field added end-to-end (TH-33). __mfAirtable v1.14→v1.15: theme_map → 'Theme Map' added to Themes table schema. __mfBusinessObjects v1.19→v1.20: Theme._norm() parses theme_map JSON on read; Theme._denorm() serialises theme_map JSON on write; ThemeSeeder.run() accepts themeMap parameter and attaches per-palette Theme Map data (palette Layer 1+2 entry + Layer 3 elements) to each Colour seed row without overwriting user edits. window.__mfThemeSeeder.run() now passes THEME_MAP. Colour edit form: third sub-tab "Theme Map" added; Save button context-aware (writes theme_definition, theme_map, or closes depending on active sub-tab). Commit count incremented to 51. §10 TH-33 added. |
| 2.9 | 2026-05-05 | CP-A: Palette view switcher scaffold. Three named views: Full Matrix (live), Element Table (stub), Fingerprint Cards (stub). localStorage persistence. CP-B: ELEMENT_VIEW_SECTIONS constant added. CP-C: renderPaletteElementTable() fully implemented — 3-column grid, section tints, swatch rows. Commit 43–44. Spec header bumped to v2.9. |
| 2.8 | 2026-05-05 | Step F: Overhaul palette harmony match scores filled in. Composite health score algorithm added: five weighted dimensions (Harmony 25%, Personality 20%, Tint 20%, WCAG 20%, Completeness 15%). Traffic-light pill in Tier column. Spec bumped to v2.8. |
| 2.7 | 2026-05-05 | Step E: Tweak-tier palette snagging pass. Popup tokens added to 9 Tweak palettes. Four O'Clock Tea Time contrast fix. PALETTE_OVERHAUL_TIER changed to { tier, match } objects with match % for Tweak tier. Spec bumped to v2.7. |
| 2.6 | 2026-05-05 | Step D: Overhaul palette rebuild. All 7 Overhaul palettes rebuilt from scratch plus Sheer Volume Of Traffic surface-subtle fix. Spec bumped to v2.6. |
| 2.5 | 2026-05-05 | Step C: --mfw-popup-overlay
, --mfw-popup-bg
, --mfw-popup-border
, --mfw-popup-header-bg
, --mfw-popup-header-text
added to §5 Layer 3. TH-32 added to §10. Spec bumped to v2.5. |
| 2.4 | 2026-05-05 | Colour Theory Overhaul — Steps A & B. Palettes tab gains Tweak/Overhaul tier flag column and WCAG contrast-ratio column. §P2 Rule 9 adjacent-pair mandate removed. Spec bumped to v2.4. |
| 2.3 | 2026-05-03 | Font Hierarchy theme type implemented (TH-31). FONT_PRESETS constant (9 pairings). 4 font tokens. Google Fonts link injection. Font tab in CRUD shell. ThemeSeeder extended. Spec updated. TH-31 added to §10. |
| 2.2 | 2026-05-03 | Airtable-backed CRUD architecture. New Airtable Themes table (TH-26): Theme Name, Theme Type (single select), Is Default, Theme Definition (JSON). Theme Business Object introduced in __mfBusinessObjects
v1.19: getByType, getDefault, setDefault (two-write transaction), save, update, delete (refuses if is_default) (TH-27). ThemeSeeder.run() seeding all 6 type buckets from in-code constants; exposed via window.__mfThemeSeeder.run()
(TH-28). Widget UI rebuilt as admin CRUD shell: type-filter tabs, list/form pane, Set Default, New Theme, Delete (disabled when default) — Checkpoints CP-4a through CP-4e (TH-29). In-code constants (PALETTES, SURFACE_PRESETS, MOTION_PRESETS, STATUS_PRESETS, CAT_SCALES, BADGE_STYLE_DEFAULTS, TABLE_HEADER_DEFAULTS) retired to commented-out seed-only artefacts once Airtable seeding confirmed (TH-30). Constituent schema extended with theme_preferences field in __mfAirtable
v1.14. §3 governance note added: Airtable is the data implementation; §3 wins on any name or character conflict. §10 TH-26–TH-30 added. Step 4 Change Record comment removed — fully incorporated into spec. |
| 2.1 | 2026-04-20 | Second iteration — clarifications from session of 2026-04-20 incorporated. §2 (What Users Control) rewritten: Design panel is now the canonical input surface for all six theme controls; each control entry now states explicitly "Set via: Design panel". Content panel scope reduced to configuration-only items (default palette, display mode toggles, identity fields). Widget UI redefined as preview-only administrator aid — no interactive editing controls, no save/rollback/restoreDefaults toolbar, no live preview toggle, no advanced token editor with inputs. Read-only full token map display added to preview UI spec (TH-23). allowPublicUI gate retired — preview UI is always shown (TH-24). Content panel showAdvancedOpen, showRefPanel and allowPublicUI toggles removed (TH-25). §10 TH-21–TH-25 added. File header and in-document version bumped to 2.1. |
| 2.0 | 2026-04-16 | Corrected __mfTheme
carrier registration. Prior spec (v1.x) incorrectly listed f28ff9
as the carrier; corrected to 7430a6
(hub carrier). §P1 SFL object tiers table updated; §P1 theme subsystem boundary bullet corrected; §6 status row updated. __mfTheme
formally registered in __mfRegistry._libraries
on 7430a6
. __mfTheme
added to 7430a6
SFL_CARRIER_INDEX entry as optional
tier. __mfTheme
removed from f28ff9
SFL_CARRIER_INDEX entry. EXPECTED_SFL.__mfTheme
updated from 'not specified'
to '1.0'
. TH-20 added to §10 documenting the hub-and-spoke architecture, the legacy carrier error, and the accepted sync limitation. Spec version bumped to 2.0. |
| 1.9 | 2026-04-15 | CR-1: --mfw-shadow
and --mfw-shadow-lg
added to §5 Layer 3; shadow on/off toggle (default off) added to §2 Control 3 as a variant; §4 shadow rationale entry added; TH-14 added to §10. CR-2: System A — 8 status tokens ( --mfw-status-success/warning/danger/neutral
+ -text
variants) added to §5 Layer 2; Control 5 (Status Palette) added to §2; §4 status independence rationale added; TH-15 added to §10. System B — 12 agent-generated anchor colours with pre-calculated Vivid/Muted variants locked in §5 Layer 3; 48 categorical tokens ( --mfw-cat-S-N-bg/text
) added to §5 Layer 3; 12 badge style tokens added to §5 Layer 3; Control 6 (Categorical Scales) added to §2; site configuration UI defined; §4 categorical pre-calculation and site configuration rationale entries added; TH-16, TH-17, TH-18 added to §10. CR-4: --mfw-table-col-header-bg/text
and --mfw-table-group-header-bg/text
added to §5 Layer 3; three-state group-by display option added to §2 Control 3; §4 table group-by rationale entry added; TH-19 added to §10. §2 intro updated from four to six controls. Spec version bumped to 1.9. — Step 7 implementation complete (2026-04-15): all CR-1/CR-2/CR-4 controls are live in code — shadow toggle, group-by selector, status palette (Vivid/Muted), categorical scales (3×8 slots), badge style tokens, and table header tokens. Change closed. |
| 1.8 | 2026-04-14 | §12 replaced with Phase A Token Audit & Migration Scope List. Full CSS audit of all 6 carrier widgets; defect counts, migration actions, and recommended order recorded. |
| 1.7 | 2026-04-13 | §P2 Rule 1 — 9-step workflow list replaced with the SFL Change Management Process steps: (1) User prompt triggers but authorizes nothing; (2) AI structured analysis (pros/cons/risks/alternatives/recommendation, no code); (3) Dialogue to explicit agreement; (4) AI writes agreed change into SFL Functional Specification as a persisted file change; (5) User reviews and explicitly approves written spec before code writing; (6) AI derives implementation plan from approved spec and checks for drift; (7) Incremental code implementation in small non-breaking checkpoints with user pauses; (8) Consequential changes in other widgets echoed as ready-to-use prompts, no direct edits; (9) Update the changelog. Spec version bumped to 1.7. |
| 1.6 | 2026-04-13 | Full implementation pass — Checkpoints 1–9. Checkpoint 1: TOKEN_DEFS aligned to §5 (--mfw-* namespace, all Layer 2–4 tokens). Checkpoint 2: PALETTES constant with all 18 §3 palettes as pre-calculated data objects; adjacent-pair Rule 9 enforced by array order. Checkpoint 3: palette selector UI built in JS; token editor moved to collapsible advanced section; platform reference panel added. Checkpoint 4: SURFACE_PRESETS constant and surface style selector UI. Checkpoint 5: MOTION_PRESETS constant and motion preset selector UI. Checkpoint 6: content.json and design.json updated with configuration controls (default palette, advanced editor toggle, reference panel toggle, display mode, identity). Checkpoint 7: spec §11 and §P1 updated to v1.6, migration strategy noted. Checkpoint 8: platform reference panel enriched with per-token platform-field guidance and updated description text. Checkpoint 9: spec version bumped to 1.6 and this changelog entry added. All Rule 5 (pre-calculated values), Rule 9 (adjacent pair), and Rule 10 (three-level fallback) invariants preserved throughout. |
| 1.5 | 2026-04-13 | §P1: Removed the outward pointer to 6dbf3a's widget.html as the source of SFL constitutional rules. Replaced with self-contained SFL description and explicit runtime access pattern. Agents must read SFL rules from window.__mfConstitution
and sibling global objects — not from another widget's source file (which would require FAH-1 fetch-and-hold testing and dual-track verification, neither of which is in place). Layered model diagram updated to show window.*
prefixes throughout. Two new notice blocks in §P1: one explaining the rationale for removing the file reference; one documenting the full runtime access pattern for all SFL global objects. |
| 1.4 | 2026-04-13 | §P1: SFL defined; 6dbf3a pointer made explicit; theme subsystem boundary box added. §P2 comprehensively strengthened: violation = invalid work consequence added; "explicit user instruction" formally defined; 9-step workflow listed in full with named steps; each-change re-entry clause added; Step 5 confirmation defined; Rule 3 hardened with code prohibition on unlisted palette objects; Rule 5 hardened with deferred-wishlist lock; Rule 9 canonical adjacent-pair order locked (Sheer Volume first); Rule 10 (three-level fallback mandate) added. Governing Article "explicit instruction" definition tightened. §3 wishlist notice back-references Rule 5 with violation language. §4 namespace entry cross-references Rule 10. §5 Token Catalogue header cross-references Rule 10. Gap decision TH-13 added. Changelog updated. |
| 1.3 | 2026-04-13 | Architecture & Governance and Agent Rules renumbered §P1 and §P2. Body sections renumbered §1–§11 with no gaps (former §6–§13 become §5–§11). Body stub placeholders removed. All internal cross-references updated. Gap decision TH-12 added to record the renumbering. TOC corrected. Governing Article layer references updated. |
| 1.2 | 2026-04-13 | §5 Architecture & Governance and §11 Agent Rules promoted to document preamble. §11 rewritten as a CRITICAL MANDATE block — nine named, halt-enforced rules replacing the prior bullet list. Both sections retained original numbers (now corrected in v1.3). TOC updated. CSS updated for preamble, mandate, and cross-reference section styles. |
| 1.1 | 2026-04-13 | Restructured to four-layer decision hierarchy. Governing article added. §1 Vision & Purpose and §2 What Users Control written. §4 Technical Design Decisions added as explicit delegated layer. Palette canon promoted to §3 with user-facing content leading. All spec content carried forward unchanged. |
| 1.0 | 2026-04-13 | Initial specification. Four-layer token system, 18-palette canon, ThemeBridge migration strategy, test themes, agent operational rules, gap decisions TH-1 through TH-11. |
Method:
Full CSS read of all 6 carrier widgets. Each file examined for: (a) presence of any --mfw-* or --mf-* token references, (b) whether every reference uses the mandatory three-level fallback pattern var(--mfw-token, var(--mf-token, #hardcoded))
, and (c) what local token system (if any) exists.
Local token system found: Yes — a small bespoke set declared at widget scope:
--mfw-surface: var(--mf-modal-bg, #f2f4f8); --mfw-text: var(--mf-title-color, #1a1f2e); --mfw-muted: var(--mf-subtitle-color, #5a6278); --mfw-border: var(--mf-input-border-color, #c4c9da); --mfw-primary: #5566aa;
Token references in rules:
| Property | Value written | Rule 10 compliant? |
|---|---|---|
| .mf-card background | var(--mfw-surface, var(--mf-modal-bg)) | ❌ DEFECT — two levels; missing hardcoded third level |
| .mf-card-title color | var(--mfw-text, var(--mf-title-color)) | ❌ DEFECT — two levels; missing hardcoded third level |
| .mf-input border | var(--mfw-border, var(--mf-input-border-color)) | ❌ DEFECT — two levels; missing hardcoded third level |
| .mf-file-btn background | var(--mfw-primary, #5566aa) | ❌ DEFECT — one level; missing --mf-* middle level and relies on widget-scoped variable not :root |
| .mf-label color | var(--mf-label-color) | ❌ DEFECT — bare single --mf-* reference, no --mfw-* and no hardcoded fallback |
| .mf-label-hint color | var(--mf-subtitle-color) | ❌ DEFECT — bare --mf-*, no levels |
| .mf-input background | var(--mf-input-bg) | ❌ DEFECT — bare --mf-* |
| .mf-input color | var(--mf-input-text-color) | ❌ DEFECT — bare --mf-* |
| .mf-success-title color | var(--mf-title-color) | ❌ DEFECT — bare --mf-* |
| .mf-success-text color | var(--mf-subtitle-color) | ❌ DEFECT — bare --mf-* |
Rule 10 defect count: 10
Additional issue: The widget-scoped --mfw-* declarations at the top are NOT the :root-level --mfw-* tokens defined in §5. They are local aliases that map platform --mf-* variables through the widget class scope. This means even when __mfTheme is running and writing --mfw-brand, --mfw-surface etc. to :root, the widget-scoped declarations will shadow them — __mfTheme tokens will be invisible to this widget until those local overrides are removed and replaced with proper three-level fallbacks.
Migration actions required:
- Remove all widget-scoped --mfw-* declarations from .widget-303f63
- Replace every bare var(--mf-*) with var(--mfw-*, var(--mf-*, #hardcoded))
- Upgrade every two-level var(--mfw-*, var(--mf-*)) to include the hardcoded third level
- Map --mfw-primary usage to the correct §5 token (--mfw-brand)
Local token system found: None. Zero --mfw-* or --mf-* references anywhere in the CSS file.
All styling is hardcoded — hex values, rgba literals, and named colours directly in property values throughout. No CSS custom property is used for any theme-sensitive property (colours, borders, surfaces, text, spacing, radius).
Rule 10 defect count: 0(no references at all — the widget is entirely unwired from the theme system)
This is a Phase B target — it will not respond to any __mfTheme token write. The Phase B diagnostic (Strictly Come Dancing palette) will confirm this: the widget will remain visually unchanged while all wired widgets shift.
Migration actions required:
- Identify all hardcoded colour values (approximately 35–40 unique values across the file)
- Map each to the nearest §5 semantic token (--mfw-surface, --mfw-text, --mfw-border, --mfw-brand, etc.)
- Replace with three-level fallback pattern for every colour property
- This is the highest-effort migration on the site — it is a full CSS rewrite
Local token system found: Yes — a comprehensive bespoke namespace --rrt-* declared at widget scope (approximately 24 tokens), e.g.:
--rrt-bg: #f5f6fa; --rrt-surface: #ffffff; --rrt-border: #e0e4ef; --rrt-accent: #2d6cdf; --rrt-text-primary: #1a1f36; ...
Token references: All rules consume var(--rrt-*) values. There are zero --mfw-* or --mf-* references anywhere in the CSS.
Rule 10 defect count: 0(no --mfw-* references at all — the widget uses its own private namespace and is entirely unwired from the theme system)
The --rrt-* tokens are declared with hardcoded values at widget scope — they form a private design system that will not respond to __mfTheme. Like widget 2, this will be a Phase B non-responder.
Migration actions required:
- Map each --rrt-* token to its nearest §5 semantic equivalent (e.g. --rrt-surface → --mfw-surface, --rrt-accent → --mfw-brand, --rrt-text-primary → --mfw-text, --rrt-border → --mfw-border, etc.)
- At each CSS rule site, replace var(--rrt-token) with var(--mfw-token, var(--mf-token, #hardcoded))
- Keep --rrt-* declarations as fallback locals during migration, or remove them once the widget is fully wired — that is a migration-phase decision
- Widget-specific tokens with no §5 equivalent (e.g. --rrt-badge-contacted, --rrt-log-entry-bg) remain as locals and are not subject to Rule 10
Local token system found: Yes — a bespoke --rw-* namespace declared at widget scope (approximately 15 tokens), e.g.:
--rw-bg: #ffffff; --rw-border: #e2e8f0; --rw-header-bg: #1e3a5f; --rw-accent: #2563eb; --rw-text: #1e293b; --rw-muted: #64748b; ...
Token references: All rules consume var(--rw-*) values. There are zero --mfw-* or --mf-* references anywhere in the CSS.
Rule 10 defect count: 0(no --mfw-* references at all — unwired from theme system)
Same pattern as widget 3. The --rw-* namespace is private and hardcoded. Phase B will confirm no response to __mfTheme.
Migration actions required:
- Map --rw-* tokens to §5 equivalents (e.g. --rw-bg / --rw-surface → --mfw-surface, --rw-accent → --mfw-brand, --rw-text → --mfw-text, --rw-border → --mfw-border, --rw-muted → --mfw-text-muted, --rw-header-bg → --mfw-surface-raised or --mfw-brand, --rw-radius → --mfw-radius)
- Replace each var(--rw-token) call site with var(--mfw-token, var(--mf-token, #hardcoded))
- Tokens with no §5 equivalent (e.g. --rw-row-hover, --rw-row-active, --rw-row-odd, --rw-row-even) stay as locals; the question of whether to add §5 surface-state tokens for these requires a user decision (Rule 4 — new tokens need a §5 entry)
Local token system found: None. Zero --mfw-* or --mf-* references anywhere in the CSS file.
All styling is hardcoded — hex values throughout (#2e3a8c, #1a1f2e, #f0f2f7, etc.). The widget is a documentation and dashboard widget; its visual identity is intentionally fixed (navy/white SFL house style) and was never intended to theme-switch with the palette system.
Rule 10 defect count: 0(no references at all — widget is unwired by design)
Migration decision required (user input needed):
- Is this widget in scope for palette-driven theming, or should its fixed SFL house style be preserved?
- If in scope: map hardcoded values to --mfw-* tokens (substantial work — the file is large)
- If out of scope: formally document it as an intentional non-participant in §8 or §10 so future agents do not treat it as a defect
Local token system found: Yes — the widget consumes --mfw-* tokens natively as the theme control centre. It uses the three-level fallback pattern throughout.
Compliance check: All var(--mfw-*) references in widget.css follow the pattern correctly — every one includes the --mf-* middle level and a hardcoded fallback.
Examples (correct):
color: var(--mfw-text, var(--mf-text, #111827)); background: var(--mfw-surface, var(--mf-surface, #ffffff)); border: 1px solid var(--mfw-border, var(--mf-border, rgba(17, 24, 39, 0.16)));
Rule 10 defect count: 0 ✅
| Widget | Name | Rule 10 Defects | Status | Priority |
|---|---|---|---|---|
| 303f63 | Networking Profile Form | 10 | ❌ Partially wired, defective | High — fix before Phase B |
| 85ecf1 | Profile Card | 0 (unwired) | ❌ Zero token coverage | High — full rewrite |
| f28ff9 | Contact Log | 0 (unwired) | ❌ Private --rrt-* namespace | Medium — namespace migration |
| f05985 | Directory | 0 (unwired) | ❌ Private --rw-* namespace | Medium — namespace migration |
| 6dbf3a | SFL Dashboard | 0 (unwired) | ⚠️ Out of scope? Needs user decision | Pending decision |
| 7430a6 | Theme Editor | 0 | ✅ Compliant | Done |
Type A — Bare --mf-* references (no --mfw-*, no hardcoded fallback): 6 instances in 303f63. These are the most fragile — if the platform fails to inject its --mf-* variables, rendering breaks completely.
Type B — Two-level fallbacks (missing hardcoded third level): 4 instances in 303f63. These survive a platform failure but break if both --mfw-* and --mf-* are absent.
Type C — Widget-scoped --mfw-* shadow declarations: 1 widget (303f63) declares --mfw-surface, --mfw-text, --mfw-border at widget class scope, which shadows the :root values written by __mfTheme. This means __mfTheme is effectively invisible to this widget even when running correctly — a subtle but critical wiring defect.
Type D — Fully unwired (private namespace): 85ecf1, f28ff9, f05985. These widgets will not respond to Phase B or any __mfTheme palette switch until migrated.
Type E — Unwired by design (fixed house style): 6dbf3a. Requires an explicit user decision on whether to bring it into scope.
- 303f63 — fix first; it has active token references that are already broken (Type A + B + C defects). Correctness issue, not just a feature gap.
- f28ff9 — namespace migration; --rrt-* map is well-defined and 1:1 mappable to §5 tokens.
- f05985 — namespace migration; --rw-* map is also clean. Some tokens (--rw-row-hover, etc.) will need a user decision on whether to add §5 surface-state tokens.
- 85ecf1 — full rewrite; highest effort, no existing structure to leverage. Phase B will confirm scope visually.
- 6dbf3a — pending user decision on whether SFL dashboard house style participates in theming.
Select a type above to manage themes.