commit 57cb207db55a9625adbe937b2f43ca55cb95929d
parent 90ce611386b0d35a769d6835957e1e0bdb42ed8c
Author: Osmond Arnesto <oarnesto@mozilla.com>
Date: Wed, 22 Oct 2025 02:48:37 +0000
Bug 1979978 - stylelint rule for non-semantic usage of design tokens r=frontend-codestyle-reviewers,tgiles
Differential Revision: https://phabricator.services.mozilla.com/D266532
Diffstat:
8 files changed, 729 insertions(+), 3 deletions(-)
diff --git a/.stylelintrc.js b/.stylelintrc.js
@@ -278,6 +278,7 @@ module.exports = {
"stylelint-plugin-mozilla/use-space-tokens": true,
"stylelint-plugin-mozilla/use-text-color-tokens": true,
"stylelint-plugin-mozilla/use-box-shadow-tokens": true,
+ "stylelint-plugin-mozilla/no-non-semantic-token-usage": true,
},
overrides: [
@@ -426,6 +427,7 @@ module.exports = {
"stylelint-plugin-mozilla/use-space-tokens": false,
"stylelint-plugin-mozilla/use-text-color-tokens": false,
"stylelint-plugin-mozilla/use-box-shadow-tokens": false,
+ "stylelint-plugin-mozilla/no-non-semantic-token-usage": false,
},
},
{
@@ -440,6 +442,7 @@ module.exports = {
"stylelint-plugin-mozilla/use-border-radius-tokens": true,
"stylelint-plugin-mozilla/use-space-tokens": false,
"stylelint-plugin-mozilla/use-text-color-tokens": false,
+ "stylelint-plugin-mozilla/no-non-semantic-token-usage": false,
},
},
{
diff --git a/docs/code-quality/lint/linters/stylelint-plugin-mozilla/rules/no-non-semantic-token-usage.rst b/docs/code-quality/lint/linters/stylelint-plugin-mozilla/rules/no-non-semantic-token-usage.rst
@@ -0,0 +1,79 @@
+===========================
+no-non-semantic-token-usage
+===========================
+
+This rule checks that design tokens are only used with the appropriate CSS
+properties. This ensures that that design tokens are not used with
+unexpected properties, such as a font-size token to set a border-width.
+This rule allows for variables used with fallback values and variables
+used in shorthand properties.
+
+Examples of incorrect token usage for this rule:
+------------------------------------------------
+
+.. code-block:: css
+
+ .card {
+ background-color: var(--border-color);
+ }
+
+.. code-block:: css
+
+ .card {
+ padding: var(--size-item-small);
+ }
+
+.. code-block:: css
+
+ .card {
+ width: var(--space-small);
+ }
+
+.. code-block:: css
+
+ .button {
+ border: var(--border-width) solid var(--text-color);
+ }
+
+.. code-block:: css
+
+ :root {
+ --local-background-color: var(--text-color);
+ }
+
+ .button {
+ background-color: var(--local-background-color);
+ }
+
+Examples of correct code for this rule:
+---------------------------------------
+
+.. code-block:: css
+
+ .card {
+ background-color: var(--background-color-canvas);
+ }
+
+.. code-block:: css
+
+ .card {
+ background-color: var(--background-color-canvas, #fff);
+ }
+
+.. code-block:: css
+
+ .card {
+ background: repeat cover var(--background-color-canvas);
+ }
+
+.. code-block:: css
+
+ .card {
+ background: var(--background-color-canvas), #fff;
+ }
+
+.. code-block:: css
+
+ .button {
+ border: var(--border-width) solid var(--border-color);
+ }
diff --git a/stylelint-rollouts.config.js b/stylelint-rollouts.config.js
@@ -1468,4 +1468,87 @@ module.exports = [
"toolkit/themes/shared/tree/tree.css",
],
},
+ {
+ // stylelint fixes for this rule will be addressed in Bug 1992749
+ name: "rollout-no-non-semantic-token-usage",
+ rules: {
+ "stylelint-plugin-mozilla/no-non-semantic-token-usage": "null",
+ },
+ files: [
+ "browser/components/aboutlogins/content/components/input-field/input-field.css",
+ "browser/components/aboutlogins/content/components/login-list.css",
+ "browser/components/aboutlogins/content/components/login-message-popup.css",
+ "browser/components/aboutlogins/content/components/login-timeline.css",
+ "browser/components/aboutlogins/content/components/menu-button.css",
+ "browser/components/aboutwelcome/content-src/aboutwelcome.scss",
+ "browser/components/backup/content/backup-common.css",
+ "browser/components/backup/content/restore-from-backup.css",
+ "browser/components/genai/chat.css",
+ "browser/components/genai/content/link-preview-card.css",
+ "browser/components/profiles/content/profile-avatar-selector.css",
+ "browser/components/profiles/content/profile-card.css",
+ "browser/components/protections/content/protections.css",
+ "browser/components/sidebar/sidebar-main.css",
+ "browser/components/textrecognition/textrecognition.css",
+ "browser/extensions/newtab/content-src/components/Base/_Base.scss",
+ "browser/extensions/newtab/content-src/components/Card/_Card.scss",
+ "browser/extensions/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/_CardGrid.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/CardSections/_CardSections.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/InterestPicker/_InterestPicker.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/Navigation/_Navigation.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/PromoCard/_PromoCard.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/TopicSelection/_TopicSelection.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/TopicsWidget/_TopicsWidget.scss",
+ "browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/TrendingSearches/_TrendingSearches.scss",
+ "browser/extensions/newtab/content-src/components/Search/_Search.scss",
+ "browser/extensions/newtab/content-src/components/Sections/_Sections.scss",
+ "browser/extensions/newtab/content-src/components/TopSites/_TopSites.scss",
+ "browser/extensions/newtab/content-src/components/WallpaperCategories/_WallpaperCategories.scss",
+ "browser/extensions/newtab/content-src/components/Weather/_Weather.scss",
+ "browser/extensions/newtab/content-src/components/Widgets/FocusTimer/_FocusTimer.scss",
+ "browser/extensions/newtab/content-src/components/Widgets/Lists/_Lists.scss",
+ "browser/extensions/newtab/content-src/styles/_variables.scss",
+ "browser/themes/linux/browser.css",
+ "browser/themes/shared/addons/unified-extensions.css",
+ "browser/themes/shared/controlcenter/panel.css",
+ "browser/themes/shared/customizableui/panelUI-shared.css",
+ "browser/themes/shared/downloads/downloads.inc.css",
+ "browser/themes/shared/formautofill-notification.css",
+ "browser/themes/shared/identity-block/identity-block.css",
+ "browser/themes/shared/notification-icons.css",
+ "browser/themes/shared/pageInfo.css",
+ "browser/themes/shared/places/editBookmarkPanel.css",
+ "browser/themes/shared/preferences/containers-dialog.css",
+ "browser/themes/shared/sidebar.css",
+ "browser/themes/shared/tabbrowser/tabs.css",
+ "browser/themes/shared/toolbarbuttons.css",
+ "browser/themes/shared/translations/panel.css",
+ "browser/themes/shared/urlbar-dynamic-results.css",
+ "browser/themes/shared/urlbar-searchbar.css",
+ "browser/themes/shared/urlbarView.css",
+ "browser/themes/windows/browser.css",
+ "toolkit/components/printing/content/toggle-group.css",
+ "toolkit/components/satchel/megalist/content/megalist.css",
+ "toolkit/content/aboutGlean.css",
+ "toolkit/content/widgets/moz-box-common.css",
+ "toolkit/content/widgets/moz-button/moz-button.css",
+ "toolkit/content/widgets/moz-input-color/moz-input-color.css",
+ "toolkit/content/widgets/moz-input-text/moz-input-text.css",
+ "toolkit/content/widgets/moz-message-bar/moz-message-bar.css",
+ "toolkit/content/widgets/moz-page-nav/moz-page-nav.css",
+ "toolkit/content/widgets/moz-select/moz-select.css",
+ "toolkit/content/widgets/moz-toggle/moz-toggle.css",
+ "toolkit/content/widgets/moz-visual-picker/moz-visual-picker-item.css",
+ "toolkit/content/widgets/panel-list/panel-item.css",
+ "toolkit/content/widgets/panel-list/panel-list.css",
+ "toolkit/themes/shared/aboutReader.css",
+ "toolkit/themes/shared/design-system/tokens-table.css",
+ "toolkit/themes/shared/findbar.css",
+ "toolkit/themes/shared/global-shared.css",
+ "toolkit/themes/shared/in-content/common-shared.css",
+ "toolkit/themes/shared/menulist.css",
+ ],
+ },
];
diff --git a/tools/lint/stylelint/stylelint-plugin-mozilla/data.mjs b/tools/lint/stylelint/stylelint-plugin-mozilla/data.mjs
@@ -0,0 +1,205 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+export const BACKGROUND_COLOR = {
+ CATEGORIES: ["background-color"],
+ PROPERTIES: ["background", "background-color"],
+};
+
+export const BORDER_COLOR = {
+ CATEGORIES: ["border-color", "border", "outline"],
+ PROPERTIES: [
+ "border",
+ "border-color",
+ "outline",
+ "outline-color",
+ "border-top",
+ "border-right",
+ "border-bottom",
+ "border-left",
+ "border-top-color",
+ "border-right-color",
+ "border-bottom-color",
+ "border-left-color",
+ "border-block",
+ "border-block-color",
+ "border-block-start",
+ "border-block-start-color",
+ "border-block-end",
+ "border-block-end-color",
+ "border-inline",
+ "border-inline-color",
+ "border-inline-start",
+ "border-inline-start-color",
+ "border-inline-end",
+ "border-inline-end-color",
+ ],
+};
+
+export const BORDER_RADIUS = {
+ CATEGORIES: ["border-radius"],
+ PROPERTIES: [
+ "border-radius",
+ "border-top-left-radius",
+ "border-top-right-radius",
+ "border-bottom-right-radius",
+ "border-bottom-left-radius",
+ "border-start-start-radius",
+ "border-start-end-radius",
+ "border-end-start-radius",
+ "border-end-end-radius",
+ ],
+};
+
+export const BORDER_WIDTH = {
+ CATEGORIES: ["border-width"],
+ PROPERTIES: [
+ "border",
+ "border-width",
+ "border-top",
+ "border-top-width",
+ "border-block-start",
+ "border-block-start-width",
+ "border-right",
+ "border-right-width",
+ "border-inline-end",
+ "border-inline-width",
+ "border-bottom",
+ "border-bottom-width",
+ "border-block-end",
+ "border-block-end-width",
+ "border-left",
+ "border-left-width",
+ "border-inline-start",
+ "border-inline-start-width",
+ "outline",
+ "outline-width",
+ ],
+};
+
+export const FONT_SIZE = {
+ CATEGORIES: ["font-size", "heading-font"],
+ PROPERTIES: ["font-size", "font"],
+};
+
+export const FONT_WEIGHT = {
+ CATEGORIES: ["font-weight"],
+ PROPERTIES: ["font-weight", "font"],
+};
+
+export const ICON_COLOR = {
+ CATEGORIES: ["icon-color"],
+ PROPERTIES: ["color", "fill", "stroke"],
+};
+
+export const SIZE = {
+ CATEGORIES: ["size", "icon-size"],
+ PROPERTIES: [
+ "width",
+ "min-width",
+ "max-width",
+ "height",
+ "min-height",
+ "max-height",
+ "inline-size",
+ "min-inline-size",
+ "max-inline-size",
+ "block-size",
+ "min-block-size",
+ "max-block-size",
+ "inset",
+ "inset-block",
+ "inset-block-end",
+ "inset-block-start",
+ "inset-inline",
+ "inset-inline-end",
+ "inset-inline-start",
+ "flex",
+ "flex-basis",
+ "grid",
+ "grid-template-rows",
+ "grid-template-columns",
+ "grid-auto-rows",
+ "grid-auto-columns",
+ "background-size",
+ "transform",
+ ],
+};
+
+export const OPACITY = {
+ CATEGORIES: ["opacity"],
+ PROPERTIES: ["opacity"],
+};
+
+export const SPACE = {
+ CATEGORIES: ["space"],
+ PROPERTIES: [
+ "margin",
+ "margin-top",
+ "margin-right",
+ "margin-bottom",
+ "margin-left",
+ "margin-block",
+ "margin-block-start",
+ "margin-block-end",
+ "margin-inline",
+ "margin-inline-start",
+ "margin-inline-end",
+ "padding",
+ "padding-top",
+ "padding-right",
+ "padding-bottom",
+ "padding-left",
+ "padding-block",
+ "padding-block-start",
+ "padding-block-end",
+ "padding-inline",
+ "padding-inline-start",
+ "padding-inline-end",
+ "inset",
+ "inset-block",
+ "inset-block-end",
+ "inset-block-start",
+ "inset-inline",
+ "inset-inline-end",
+ "inset-inline-start",
+ "gap",
+ "row-gap",
+ "column-gap",
+ "scroll-margin",
+ "scroll-margin-top",
+ "scroll-margin-right",
+ "scroll-margin-bottom",
+ "scroll-margin-left",
+ "scroll-margin-block",
+ "scroll-margin-block-start",
+ "scroll-margin-block-end",
+ "scroll-margin-inline",
+ "scroll-margin-inline-start",
+ "scroll-margin-inline-end",
+ "scroll-padding",
+ "scroll-padding-top",
+ "scroll-padding-right",
+ "scroll-padding-bottom",
+ "scroll-padding-left",
+ "scroll-padding-block",
+ "scroll-padding-block-start",
+ "scroll-padding-block-end",
+ "scroll-padding-inline",
+ "scroll-padding-inline-start",
+ "scroll-padding-inline-end",
+ "border-spacing",
+ ],
+};
+
+export const TEXT_COLOR = {
+ CATEGORIES: ["text-color", "link"],
+ PROPERTIES: ["color", "fill", "stroke"],
+};
+
+export const BOX_SHADOW = {
+ CATEGORIES: ["box-shadow"],
+ PROPERTIES: ["box-shadow", "filter", "backdrop-filter"],
+};
diff --git a/tools/lint/stylelint/stylelint-plugin-mozilla/helpers.mjs b/tools/lint/stylelint/stylelint-plugin-mozilla/helpers.mjs
@@ -52,7 +52,6 @@ export const ALLOW_LIST = [
* @param {string[]} additionalAllows to be appended to our list
* @returns {string[]}
*/
-
export const createAllowList = (additionalAllows = []) => {
return [...ALLOW_LIST, ...additionalAllows];
};
@@ -67,7 +66,7 @@ export const createTokenNamesArray = tokenCategoriesArray =>
tokenCategoriesArray
.flatMap(category => tokensTable[category])
.reduce((acc, token) => {
- if (token.name) {
+ if (token?.name) {
return [...acc, `var(${token.name})`];
}
return acc;
@@ -356,7 +355,6 @@ export const isValidLocalProperty = (value, cssCustomProperties, tokenCSS) => {
* @param {string} value some CSS declaration to match
* @returns {string}
*/
-
export const trimValue = value => String(value).trim();
/**
diff --git a/tools/lint/stylelint/stylelint-plugin-mozilla/rules/index.mjs b/tools/lint/stylelint/stylelint-plugin-mozilla/rules/index.mjs
@@ -14,6 +14,7 @@ import useSpaceTokens from "./use-space-tokens.mjs";
import useBackgroundColorTokens from "./use-background-color-tokens.mjs";
import useTextColorTokens from "./use-text-color-tokens.mjs";
import useBoxShadowTokens from "./use-box-shadow-tokens.mjs";
+import noNonSemanticTokenUsage from "./no-non-semantic-token-usage.mjs";
export default {
"no-base-design-tokens": noBaseDesignTokens,
@@ -26,4 +27,5 @@ export default {
"use-background-color-tokens": useBackgroundColorTokens,
"use-text-color-tokens": useTextColorTokens,
"use-box-shadow-tokens": useBoxShadowTokens,
+ "no-non-semantic-token-usage": noNonSemanticTokenUsage,
};
diff --git a/tools/lint/stylelint/stylelint-plugin-mozilla/rules/no-non-semantic-token-usage.mjs b/tools/lint/stylelint/stylelint-plugin-mozilla/rules/no-non-semantic-token-usage.mjs
@@ -0,0 +1,177 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+import stylelint from "stylelint";
+import valueParser from "postcss-value-parser";
+import {
+ getLocalCustomProperties,
+ namespace,
+ createTokenNamesArray,
+ isWord,
+ isVariableFunction,
+} from "../helpers.mjs";
+import {
+ BACKGROUND_COLOR,
+ BORDER_COLOR,
+ BORDER_RADIUS,
+ BORDER_WIDTH,
+ FONT_SIZE,
+ FONT_WEIGHT,
+ ICON_COLOR,
+ SIZE,
+ OPACITY,
+ SPACE,
+ TEXT_COLOR,
+ BOX_SHADOW,
+} from "../data.mjs";
+
+const {
+ utils: { report, ruleMessages, validateOptions },
+} = stylelint;
+
+const ruleName = namespace("no-non-semantic-token-usage");
+
+const messages = ruleMessages(ruleName, {
+ rejected: token =>
+ `Unexpected usage of \`${token}\`. Design tokens should only be used with properties matching their semantic meaning.`,
+});
+
+const meta = {
+ url: "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/stylelint-plugin-mozilla/rules/no-non-semantic-token-usage.html",
+ fixable: false,
+};
+
+const backgroundColorTokens = createTokenNamesArray(
+ BACKGROUND_COLOR.CATEGORIES
+);
+const borderColorTokens = createTokenNamesArray(BORDER_COLOR.CATEGORIES);
+const borderRadiusTokens = createTokenNamesArray(BORDER_RADIUS.CATEGORIES);
+const borderWidthTokens = createTokenNamesArray(BORDER_WIDTH.CATEGORIES);
+const fontSizeTokens = createTokenNamesArray(FONT_SIZE.CATEGORIES);
+const fontWeightTokens = createTokenNamesArray(FONT_WEIGHT.CATEGORIES);
+const iconColorTokens = createTokenNamesArray(ICON_COLOR.CATEGORIES);
+const sizeTokens = createTokenNamesArray(SIZE.CATEGORIES);
+const opacityTokens = createTokenNamesArray(OPACITY.CATEGORIES);
+const spaceTokens = createTokenNamesArray(SPACE.CATEGORIES);
+const textColorTokens = createTokenNamesArray(TEXT_COLOR.CATEGORIES);
+const boxShadowTokens = createTokenNamesArray(BOX_SHADOW.CATEGORIES);
+
+// Get allowed properties by token category
+const getAllowedProps = token => {
+ let tokenProperties = null;
+ switch (true) {
+ case backgroundColorTokens.includes(token):
+ tokenProperties = BACKGROUND_COLOR.PROPERTIES;
+ break;
+ case borderColorTokens.includes(token):
+ tokenProperties = BORDER_COLOR.PROPERTIES;
+ break;
+ case borderRadiusTokens.includes(token):
+ tokenProperties = BORDER_RADIUS.PROPERTIES;
+ break;
+ case borderWidthTokens.includes(token):
+ tokenProperties = BORDER_WIDTH.PROPERTIES;
+ break;
+ case fontSizeTokens.includes(token):
+ tokenProperties = FONT_SIZE.PROPERTIES;
+ break;
+ case fontWeightTokens.includes(token):
+ tokenProperties = FONT_WEIGHT.PROPERTIES;
+ break;
+ case iconColorTokens.includes(token):
+ tokenProperties = ICON_COLOR.PROPERTIES;
+ break;
+ case sizeTokens.includes(token):
+ tokenProperties = SIZE.PROPERTIES;
+ break;
+ case opacityTokens.includes(token):
+ tokenProperties = OPACITY.PROPERTIES;
+ break;
+ case spaceTokens.includes(token):
+ tokenProperties = SPACE.PROPERTIES;
+ break;
+ case textColorTokens.includes(token):
+ tokenProperties = TEXT_COLOR.PROPERTIES;
+ break;
+ case boxShadowTokens.includes(token):
+ tokenProperties = BOX_SHADOW.PROPERTIES;
+ break;
+ default:
+ break;
+ }
+
+ return tokenProperties;
+};
+
+// Get all design tokens in CSS declaration value
+const getAllTokensInValue = value => {
+ const parsedValue = valueParser(value).nodes;
+
+ const allTokens = parsedValue
+ .filter(node => isVariableFunction(node))
+ .map(functionNode => {
+ const variableNode = functionNode.nodes.find(
+ node => isWord(node) && node.value.startsWith("--")
+ );
+ return variableNode ? variableNode.value : null;
+ })
+ .filter(Boolean);
+
+ return allTokens;
+};
+
+const ruleFunction = primaryOption => {
+ return (root, result) => {
+ const validOptions = validateOptions(result, ruleName, {
+ actual: primaryOption,
+ possible: [true],
+ });
+ if (!validOptions) {
+ return;
+ }
+
+ const cssCustomProperties = getLocalCustomProperties(root);
+
+ root.walkDecls(declaration => {
+ const { prop, value } = declaration;
+
+ const tokens = getAllTokensInValue(value);
+
+ tokens.forEach(token => {
+ // If local CSS custom variable declaration, skip
+ if (prop in cssCustomProperties) {
+ return;
+ }
+
+ // `var(--token-name)` mirrors shape received from `createTokenNamesArray()`
+ let varifiedToken = `var(${token})`;
+ let allowedProps = null;
+
+ if (cssCustomProperties[token]) {
+ // `cssCustomProperties[token]` already in desired shape
+ varifiedToken = cssCustomProperties[token];
+ allowedProps = getAllowedProps(cssCustomProperties[token]);
+ } else {
+ allowedProps = getAllowedProps(varifiedToken);
+ }
+
+ if (allowedProps && !allowedProps.includes(prop)) {
+ report({
+ message: messages.rejected(varifiedToken),
+ node: declaration,
+ result,
+ ruleName,
+ });
+ }
+ });
+ });
+ };
+};
+
+ruleFunction.ruleName = ruleName;
+ruleFunction.messages = messages;
+ruleFunction.meta = meta;
+
+export default ruleFunction;
diff --git a/tools/lint/stylelint/stylelint-plugin-mozilla/tests/no-non-semantic-token-usage.mjs b/tools/lint/stylelint/stylelint-plugin-mozilla/tests/no-non-semantic-token-usage.mjs
@@ -0,0 +1,179 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// eslint-disable-next-line import/no-unresolved
+import { testRule } from "stylelint-test-rule-node";
+import stylelint from "stylelint";
+import noNonSemanticTokenUsage from "../rules/no-non-semantic-token-usage.mjs";
+
+let plugin = stylelint.createPlugin(
+ noNonSemanticTokenUsage.ruleName,
+ noNonSemanticTokenUsage
+);
+let {
+ ruleName,
+ rule: { messages },
+} = plugin;
+
+testRule({
+ plugins: [plugin],
+ ruleName,
+ config: true,
+ fix: false,
+ accept: [
+ // * allowed token usage
+ {
+ code: ".a { background-color: var(--background-color-canvas); }",
+ description:
+ "Using the canvas background color token with the background-color property is valid.",
+ },
+ {
+ code: ".a { background-color: var(--background-color-canvas, #fff); }",
+ description:
+ "Using the canvas background color token with the background-color property is valid.",
+ },
+ {
+ code: ".a { background-color: var(--background-color-canvas), #fff; }",
+ description:
+ "Using the canvas background color token with the background-color property is valid.",
+ },
+ {
+ code: ".a { background: repeat cover var(--background-color-canvas); }",
+ description:
+ "Using the canvas background color token with the background property is valid.",
+ },
+ {
+ code: ".a { border-color: var(--border-color); }",
+ description:
+ "Using the border color token with the border-color property is valid.",
+ },
+ {
+ code: ".a { border: 2px solid var(--border-color); }",
+ description:
+ "Using the border color token with the border shorthand property is valid.",
+ },
+ {
+ code: ".a { border: var(--border-width) solid var(--border-color); }",
+ description:
+ "Using the border width and border color tokens with the border shorthand property are valid.",
+ },
+ {
+ code: ".a { border-radius: var(--border-radius-small, 0.25rem); }",
+ description:
+ "Using the small border radius token with the border-radius property is valid.",
+ },
+ {
+ code: ".a { font-size: var(--font-size-small); }",
+ description:
+ "Using the small font size token with the font-size property is valid.",
+ },
+ {
+ code: ".a { font: bold var(--font-size-small) 'Open Sans' sans-serif; }",
+ description:
+ "Using the small font size token with the font shorthand property is valid.",
+ },
+ {
+ code: ".a { font-weight: var(--font-weight-bold); }",
+ description:
+ "Using the bold font size token with the font-weight property is valid.",
+ },
+ {
+ code: ".a { font: var(--font-weight-bold) 16px 'Open Sans' sans-serif; }",
+ description:
+ "Using the bold font weight token with the font shorthand property is valid.",
+ },
+ {
+ code: ".a { fill: var(--icon-color); }",
+ description:
+ "Using the icon color token with the fill property is valid.",
+ },
+ {
+ code: ".a { height: var(--input-text-min-height); }",
+ description:
+ "Using the input text minimum height token with the height property is valid.",
+ },
+ {
+ code: ".a { inset: var(--size-item-small) var(--size-item-small) var(--size-item-large) var(--size-item-xlarge); }",
+ description:
+ "Using the small, large, and xlarge size tokens with the inset property is valid.",
+ },
+ {
+ code: ".a { opacity: var(--button-opacity-disabled); }",
+ description:
+ "Using the disabled button opacity token with the opacity property is valid.",
+ },
+ {
+ code: ".a { padding: var(--space-small) 4px; }",
+ description:
+ "Using the small space token with the padding property is valid.",
+ },
+ {
+ code: ".a { color: var(--text-color); }",
+ description:
+ "Using the text color token with the color property is valid.",
+ },
+ {
+ code: ".a { color: var(--button-text-color); }",
+ description:
+ "Using the button text color token with the color property is valid.",
+ },
+ {
+ code: ".a { box-shadow: var(--box-shadow-level-3); }",
+ description:
+ "Using the 3rd-level box shadow token with the box-shadow property is valid.",
+ },
+ {
+ code: ".a { box-shadow: var(--box-shadow-level-2), var(--box-shadow-level-4); }",
+ description:
+ "Using the 2nd-level and 4th-level box shadow tokens with the box-shadow property is valid.",
+ },
+ {
+ code: `
+ button {
+ background-color: var(--background-color-canvas);
+ color: var(--text-color);
+ border: 2px solid var(--border-color);
+ }
+ `,
+ description:
+ "Using the canvas background color token with the background-color property, the text color token with the color property, and the border color token with the border shorthand property are valid.",
+ },
+ ],
+ reject: [
+ {
+ code: ".a { background: var(--border-color); }",
+ message: messages.rejected("var(--border-color)"),
+ description:
+ "Unexpected usage of `var(--border-color)`. Design tokens should only be used with properties matching their semantic meaning.",
+ },
+ {
+ code: ".a { border: var(--font-size-xsmall) solid var(--border-color); }",
+ message: messages.rejected("var(--font-size-xsmall)"),
+ description:
+ "Unexpected usage of `var(--font-size-xsmall)`. Design tokens should only be used with properties matching their semantic meaning.",
+ },
+ {
+ code: ".a { border: var(--border-width) solid var(--background-color-canvas); }",
+ message: messages.rejected("var(--background-color-canvas)"),
+ description:
+ "Unexpected usage of `var(--background-color-canvas)`. Design tokens should only be used with properties matching their semantic meaning.",
+ },
+ {
+ code: `
+ :root { --local-background-color: var(--border-color); }
+ .a { background: var(--local-background-color); }
+ `,
+ message: messages.rejected("var(--border-color)"),
+ description:
+ "Unexpected usage of `var(--border-color)`. Design tokens should only be used with properties matching their semantic meaning.",
+ },
+ {
+ code: ".a { padding: var(--space-small) 4px var(--size-item-large) var(--space-small); }",
+ message: messages.rejected("var(--size-item-large)"),
+ description:
+ "Unexpected usage of `var(--size-item-large)`. Design tokens should only be used with properties matching their semantic meaning.",
+ },
+ ],
+});