Re: [community-group] How to model a true 2-dimensional modifier (brand × theme) (#381)

I would like to see how the single file version of this works. For me, I
haven't found a way other than cross producting it yet at least in Figma.

On Wed, 14 Jan 2026 at 01:47, lab9 ***@***.***> wrote:

> *lab9fr* created an issue (design-tokens/community-group#381)
> <https://github.com/design-tokens/community-group/issues/381>
> Before starting
>
>    - I have searched existing issues
>    <https://github.com/design-tokens/community-group/issues> and there is
>    not already an open issue on the subject.
>
> Summary
>
> Hello Everyone,
>
> I’m trying to model a common design-system scenario using the W3C Design
> Tokens *Resolver* specification: combining a *theme modifier* (light /
> dark) with a *brand modifier* (red / blue) so that a single semantic
> token (e.g. semantic.color.primary) resolves to one of four values.
>
> Because modifiers are not allowed to reference other modifiers, I’ve
> implemented this by having the *theme modifier define the full light/dark
> structure*, including brand-specific slots (red and blue), and then using
> the *brand modifier to select the appropriate branch* of that structure.
>
> The goal of this issue is to confirm whether this pattern is the *intended
> and recommended way* to model multi-dimensional concerns (like theme and
> brand) with the resolver, or whether there are other approaches supported
> or envisioned by the specification.
> Version
>
> Third Editor’s Draft
> Problem & motivation
>
> The Resolver specification states:
>
>
>
> *“A modifier MAY reference a set inside a context value. However a
> modifier MUST NOT reference any other modifier, not even another context
> inside the same modifier.”*
>
> I’m trying to stay strictly within this rule while modeling a common
> design-system case: combining *theme* (light/dark) and *brand* (red/blue)
> to resolve a single semantic color.
> Resolver setup
>
> I have two independent modifiers:
>
>    - *theme*: light | dark
>    - *brand*: red | blue
>
> They are composed using resolutionOrder, without any modifier referencing
> another modifier.
>
> // tokens.resolver.json// Goal: compose theme (light/dark) and brand (red/blue)
> {
>   "$schema": "https://www.designtokens.org/schemas/2025.10/resolver.json",
>   "version": "2025.10",
>   "sets": {
>     "color": {
>       "sources": [
>         { "$ref": "base.json" },
>         { "$ref": "semantic.json" }
>       ]
>     }
>   },
>   "modifiers": {
>     "theme": {
>       "description": "Color Theme",
>       "contexts": {
>         "light": [{ "$ref": "theme/light.json" }],
>         "dark": [{ "$ref": "theme/dark.json" }]
>       },
>       "default": "light"
>     },
>     "brand": {
>       "description": "Brand",
>       "contexts": {
>         "red": [{ "$ref": "red.json" }],
>         "blue": [{ "$ref": "blue.json" }]
>       },
>       "default": "red"
>     }
>   },
>   "resolutionOrder": [
>     { "$ref": "#/sets/color" },
>     { "$ref": "#/modifiers/theme" },
>     { "$ref": "#/modifiers/brand" }
>   ]
> }
>
> *Intent:*
> Choosing { theme, brand } should result in one of four values:
>
>    - red–light
>    - red–dark
>    - blue–light
>    - blue–dark
>
> Base colors
>
> These are simple primitives, independent of both theme and brand.
>
> // base.json
> {
>   "$schema": "https://www.w3.org/TR/design-tokens/",
>   "ns": {
>     "base": {
>       "color": {
>         "red": {
>           "1": { "$type": "color", "$value": "#ff0000" },
>           "2": { "$type": "color", "$value": "#800000" }
>         },
>         "blue": {
>           "1": { "$type": "color", "$value": "#0000ff" },
>           "2": { "$type": "color", "$value": "#000080" }
>         }
>       }
>     }
>   }
> }
>
> Semantic token
>
> This is the token consumers use.
> It’s expected to resolve differently depending on theme and brand.
>
> // semantic.json
> {
>   "$schema": "https://www.w3.org/TR/design-tokens/",
>   "ns": {
>     "semantic": {
>       "color": {
>         "primary": {
>           "$type": "color",
>           "$value": "{ns.theme.primary}"
>         }
>       }
>     }
>   }
> }
>
> Theme modifier sources
>
> In this model, *themes own the light/dark logic and expose brand slots*.
> Each theme defines how red and blue should look in that theme.
>
> // theme/light.json// Light theme defines light versions of each brand color
> {
>   "$schema": "https://www.w3.org/TR/design-tokens/",
>   "ns": {
>     "theme": {
>       "primary": {
>         "red": {
>           "$type": "color",
>           "$value": "{ns.base.color.red.1}"
>         },
>         "blue": {
>           "$type": "color",
>           "$value": "{ns.base.color.blue.1}"
>         }
>       }
>     }
>   }
> }
>
> // theme/dark.json// Dark theme defines dark versions of each brand color
> {
>   "$schema": "https://www.w3.org/TR/design-tokens/",
>   "ns": {
>     "theme": {
>       "primary": {
>         "red": {
>           "$type": "color",
>           "$value": "{ns.base.color.red.2}"
>         },
>         "blue": {
>           "$type": "color",
>           "$value": "{ns.base.color.blue.2}"
>         }
>       }
>     }
>   }
> }
>
> At this stage, the theme defines *what “primary” means* for each brand
> color.
> Brand modifier sources
>
> The brand modifier now simply selects which branch of the theme structure
> to use and extends the semantic intent.
>
> // red.json// Brand = red
> {
>   "$schema": "https://www.w3.org/TR/design-tokens/",
>   "ns": {
>     "semantic": {
>       "color": {
>         "primary": {
>           "$type": "color",
>           "$value": "{ns.theme.primary.red}"
>         }
>       }
>     }
>   }
> }
>
> // blue.json// Brand = blue
> {
>   "$schema": "https://www.w3.org/TR/design-tokens/",
>   "ns": {
>     "semantic": {
>       "color": {
>         "primary": {
>           "$type": "color",
>           "$value": "{ns.theme.primary.blue}"
>         }
>       }
>     }
>   }
> }
>
> Here, the brand modifier does *not* reference the theme modifier itself —
> only token paths that already exist after theme resolution.
>
> This approach might technically works and should respects the rule that:
>
> *modifiers must not reference other modifiers*
>
> I’d like to clarify:
>
>    - Is this pattern (themes defining brand slots, brands selecting them)
>    the *intended way* to model such a system ?
>    - Are there other recommended approaches within the resolver model to
>    express a true 2-dimensional choice like this ?
>    - Or is explicitly encoding the cross-product (one way or another) an
>    expected limitation of the resolver?
>
> I’m mainly looking to understand *intent and best practice*, rather than
> proposing a change to the spec.
> Prior art
>
> *No response*
> Pros & cons
>
> *No response*
> Alternatives
>
> To help clarify how the resolver is intended to be used in practice, it
> would be extremely valuable to have one or more complete sample projects
> included with the specification or repository.
> Required
>
>    - I have read and agree to abide by the CODE_OF_CONDUCT
>    <https://github.com/design-tokens/community-group/blob/CODE_OF_CONDUCT.md>
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/design-tokens/community-group/issues/381>, or
> unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AEKS36FMCXLMVAZUVUZQHML4GXYANAVCNFSM6AAAAACRUMBA6CVHI2DSMVQWIX3LMV43ASLTON2WKOZTHAYTEMBRGE4TGNI>
> .
> You are receiving this because you are subscribed to this thread.Message
> ID: ***@***.***>
>


-- 
GitHub Notification of comment by nesquarx
Please view or discuss this issue at https://github.com/design-tokens/community-group/issues/381#issuecomment-3753237409 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Thursday, 15 January 2026 07:29:36 UTC