-
Notifications
You must be signed in to change notification settings - Fork 77
Component Tokens
Component tokens define design pattern decisions at the component level. They meet user requirements for a clear and useful component theming API.
While it is possible to create a variable for each style property we need to balance customization with scale and maintainability. Allowing customer white-labeling that makes Calcite Components feel like a part of their apps while maintaining the underlying Calcite design patterns. To this end, all component tokens should reference/fallback to global tokens.
--calcite-[component]-[group]-[property]-[state]
State can be "hover", "pressed", "selected" or nothing.
- A state that is shared by
:hover
and:focus
should use-hover
- A state shared by
:focus
and:active
should use-pressed
- A state which includes
:active
and/or custom states like[selected]
and[checked]
should use-pressed
- An edge case design pattern may require a third variable state for components with unique styling when in a custom attribute state like [selected] and [checked] which is different than
-pressed
. This should use-selected
.
--calcite-button-background-color
--calcite-button-background-color-hover
--calcite-button-background-color-pressed
Due to the CSS cascade and how CSS variables can penetrate the shadowDOM it is not recommended you create tokens for sub-components or slotted components unless the wrapping component is overriding the component's pattern (i.e., overriding a token value). When a pattern is shared between related components a single token may be used.
flowchart TD
A1{<a target="_blank" href="https://github.com/Esri/calcite-design-system/blob/6ccb12a17ec371ba13fe144f8d37fdfb607dfce2/packages/calcite-components/stencil.config.ts#L17">Are components associated?</a>}
A2{Can it be used independently?}
B1{Look at the component in Figma.<br />Does it share a visual pattern<br />with associated components?}
C1{It can share tokens}
C2{It should have unique tokens}
A1 --> |Yes| A2
A1 -->|No| C2
A2 -->|No| C1
A2 -->|Yes| B1
B1 -->|Yes| C1
B1 -->|No| C2
Example:
--calcite-combobox-background-color
is used in both Combobox & ComboboxItem. This is documented in the JSDocs at the top of each component's SCSS file.
This is also the case for elements in the component which share a pattern. In most use-cases icons in the components should reference the -text-color
token. In rare cases when Icons should not follow the text color pattern, check if it can use the -indicator-color
pattern. If not, add an -icon-
element to the naming schema.
Some components in the epic/7180-component-tokens
branch are very out of date with the dev
branch. This makes sure the most recent JSX, resources and utils are being used.
- What components are related to this component?
- Are there shared interaction patterns?
- Are the use cases covered by
-hover
and-pressed
? - Is there an
indicator
pattern? This is usuallyborder-input
ortransparent
by default and-brand
,-brand-press
when:active
or:hovered
.
The most common token styles are colors, shadow, and radius. However some components may need to have additional tokens to meet user requirements. While some components already have tokens, many do not. Use the UIKit, your own intuition, as well as feedback from the team to decide how many tokens you need to create to apply styles to the following CSS properties across the component.
background-color
border-color
-
color
(text-color) -
box-shadow
(shadow) -
border-radius
(corner-radius)
Note
The custom property name does not need to match the corresponding CSS property. In the list above, the preferred name for the component token is suggested in parenthesis, allowing for more flexibility and contextual naming.
[property]: var([component-token], var([global-token/fallback]));
Sometimes it's useful to utilize -internal-
domain tokens. These are not considered official Calcite Design Tokens and are for developer convenience. If a pattern is observed in -internal-
tokens. Bring it up to the team for consideration to be move to a Calcite Design Token. Read more about internal tokens here.
- Use
themed
from common tests - If a token does not apply in the default context, add a new
describe
block - Use
html
formatter for non-default component tests
Starter template
describe("theme", () => {
describe("default", () => {
themed("calcite-component", {});
});
});
Example
describe("theme", () => {
describe("default", () => {
themed("calcite-component", {
// this is the name of the token
--calcite-component-background-color: {
// optional. This defaults to the first component in the test template.
selector: 'calcite-component',
// optional. The selector pattern to target a specific element within the shadow dom of the Selector component
shadowSelector: `.${CSS.container}`
// the CSS property you have applied a variable to. This must be written in camelCase format.
targetProp: "backgroundColor"
}
});
});
});
- Find or create a component file in
packages/calcite-components/src/custom-theme/[component]
; - Use named exports and tagged template literals with
html
formatting to define the test component templates. - Export an object of component tokens with the token name in camelCase as the key and an empty string as the value.
- Make sure the component templates are imported into
packages/calcite-components/src/custom-theme.stories.ts
and included in thekitchenSink
template and the default exportedargs
.
export const actionTokens = {
calciteActionIndicatorColor: "",
calciteActionBackgroundColor: "",
calciteActionBackgroundColorHover: "",
calciteActionBackgroundColorPressed: "",
calciteActionTextColor: "",
calciteActionTextColorPressed: "",
};
export const actionBarTokens = {
calciteActionBarExpandedMaxWidth: "",
calciteActionBarItemsSpace: "",
};
export const actionBar = html`<calcite-action-bar layout="horizontal" style="width:100%">
<calcite-action-group>
<calcite-action text="Add" icon="plus"> </calcite-action>
<calcite-action text="Save" icon="save"> </calcite-action>
<calcite-action text="Layers" icon="layers"> </calcite-action>
</calcite-action-group>
<calcite-action-group>
<calcite-action text="Add" icon="plus"> </calcite-action>
<calcite-action text="Save" active icon="save"> </calcite-action>
<calcite-action text="Layers" icon="layers"> </calcite-action>
</calcite-action-group>
<calcite-action slot="actions-end" text="hello world" icon="layers"> </calcite-action>
<!-- The "bottom-actions" slot is deprecated -->
<calcite-action slot="bottom-actions" text="hello world 2" icon="information"> </calcite-action>
</calcite-action-bar>`;
import {
actionBar,
actionTokens,
actionBarTokens,
} from "./custom-theme/actions";
// ...
const kitchenSink = (args: Record<string, string>, useTestValues = false) =>
html`<div style="${customTheme(args, useTestValues)}">
<div class="demo">
<div>${actionBar}</div>
</div>
</div>
</div>`;
export default {
title: "Theming/Custom Theme",
args: {
...actionTokens,
...actionBarTokens,
},
};
export const theming_TestOnly = (): string => {
return kitchenSink(
{
...actionTokens,
...actionBarTokens,
},
true,
);
};
<demo-theme tokens="--calcite-tokens, --calcite-tokens-as-list">
<!-- component HTML -->
</demo-theme>
Accordion & Accordion Item are directly related and so can be done together. After reviewing the Figma file it was determined that although there were several different instances of text-color and background-color being applied, only a few actual color values were used. This was simplified down to five tokens and applied to both components.
https://github.com/Esri/calcite-design-system/pull/9861
All Action components, Action, Action Bar, Action Group, Action Menu, and Action Pad share many of the same design patterns and therefor should share a lot of the base action tokens. However, several of the more complex action components have their own additional tokens on top of Action.
https://github.com/Esri/calcite-design-system/pull/10058
Icon is used in all kinds of places. Here most of the work was a quick find-replace of the old --calcite-ui-icon-color
token in favor of --calcite-icon-color
including in the Calcite Tailwind presets file. This also including removal of some overrides of the --calcite-icon-color
token where it was unnecessary and was blocking theming when Storybook tests were run.