ADR component tokens for customization#7605
Conversation
…verrides Define component-level CSS custom properties on the root .SegmentedControl element so they can be overridden from parent elements or via inline styles: - --segmented-control-bgColor - --segmented-control-bgColor-hover - --segmented-control-bgColor-active - --segmented-control-borderColor - --segmented-control-borderRadius - --segmented-control-fgColor-icon - --segmented-control-fgColor-disabled - --segmented-control-fontWeight - --segmented-control-selected-bgColor - --segmented-control-selected-borderColor - --segmented-control-selected-fontWeight - --segmented-control-button-inner-padding - --segmented-control-button-bg-inset Also adds a CustomCSSTokens feature story demonstrating how to override tokens from a parent element. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… tokens CSS custom properties defined directly on an element always win over inherited values from parent elements. Switch to the var(--token, fallback) pattern so that tokens set on a parent element properly cascade down to the SegmentedControl. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add --segmented-control-selected-fgColor token to control the text and icon color of the selected segment. Update the CustomCSSTokens story to set it to white (--fgColor-onEmphasis) for contrast on the accent background. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Establishes the pattern for exposing overridable CSS custom properties as component tokens. Covers naming conventions, what to tokenize vs not, the var() fallback pattern for inheritance, and consumer usage examples. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
- Rename bgInset to trackPadding - Promote innerPadding and trackPadding to public tokens - Add bgColor-active token - Remove internal --iconWidth var, use direct width on .IconButton - Use camelCase naming throughout (--segmentedControl-*) - Separate iconColor / fgColor tokens for default and selected states - Add hover tokens for fgColor and iconColor - Update story with hover/active overrides using color-mix() - Update ADR-023 token table and code examples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Really like the I'm curious about the "escape hatch" framing. If we're establishing naming conventions and a breaking change policy, what else would be required to take this to a first-class public API? The main things I can see are documentation and fixing the stylelint config. What's the barrier to going all the way and dropping the escape hatch framing? As an aside, I think this ADR could also fit into the broader conversations I've seen around component composability; github/primer#6381, github/primer#6393. |
This framing is not meant because of technical quality issues, but rather to make folks aware that overwriting comes at costs, like inconsistency, it may not visually work with other components, etc.
Could you talk about more about it? Fit in there in a good or on a bad way? |
|
@lukasoppermann Cool that makes sense, thanks for clarifying. 👍 On the composability discussions — fit in in a good way I think. Those discussions are exploring whether components should expose their internal parts more directly (compound components, hooks, etc.), which would reduce the need for tokens over time because consumers could access and style the parts themselves. But that's a longer-term direction and doesn't change the value of tokens as a solution today. They're complementary, not in conflict. Just wanted to link to them here as, in my head at least, these are all working towards the same broader theme of making it easier for consumers to customise components. |
| - **Stable public API** — consumers can customize components without depending on internal class names. | ||
| - **Inheritance-friendly** — tokens set on a parent cascade down, enabling contextual theming. | ||
| - **Backward compatible** — existing components continue to work unchanged; tokens are purely additive. | ||
| - **Self-documenting** — the comment block in each component's CSS file serves as the token reference. |
There was a problem hiding this comment.
To be bit pedantic, requiring a comment block is the opposite of self-documenting; we're explicitly writing documentation.
We should figure out a way to extract & lint against our rules for these if we decide to go down that path: (e.g. stylelint to only allow undefined values if they follow a certain naming convention, postcss plugin to extract these into the docs.json metadata)
There was a problem hiding this comment.
Fine with me. We could also put them into the component's docs. Will update.
|
|
||
| ## Decision | ||
|
|
||
| Every component should expose a set of **component tokens** as CSS custom properties. These tokens are the public styling API of the component and follow a consistent pattern across all components. |
There was a problem hiding this comment.
How do we differentiate this with the component-tokens already defined within the primitives package? Should that not already serve as our public API surface for customizing a subset of the behavior of a component?
Could we take the same segmentedControl example you have below and author that within the primitives package, leveraging those tokens from the component implementation to provide the same level of customization?
There was a problem hiding this comment.
We could use them, yes. But we only use component tokens in primitives as escapes when semantic tokens don't work.
I am not a big fan of this, as it creates a tight coupling between primitives and each component. So I would not want to define this "API" in primitives. Does this make sense? Do you think about this diffently?
Remove the inline CSS comment listing all tokens. Instead, require component tokens to be documented in the component's docs.json file as a cssTokens array. Note future tooling opportunities: a stylelint plugin to enforce naming conventions and a PostCSS plugin to extract tokens into docs.json automatically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This ADR discusses an alternative to the proposed ADR to implement
data-slotattr for customization.This ADR focuses on using component tokens, which would allow for more control on our side, as we can clearly define what can and can not be overwritten.
The segmentedControl component implements this ADR (will be removed before merge, just as an example).
👀 View ADR and example code