tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit dbcbb7b754563a373dd04186faf06bf0e2d289c2
parent 5696e84aa1bf6a80d96866b5a3548421630e84f5
Author: dwhisman <dwhisman@mozilla.com>
Date:   Thu, 13 Nov 2025 19:31:32 +0000

Bug 1988873 - Allow link colors, black, and white for text-color linting rule r=desktop-theme-reviewers,frontend-codestyle-reviewers,mkennedy,hjones

Differential Revision: https://phabricator.services.mozilla.com/D270394

Diffstat:
Mdocs/code-quality/lint/linters/stylelint-plugin-mozilla/rules/use-text-color-tokens.rst | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/themes/shared/design-system/config/tokens-config.js | 9++++++++-
Mtoolkit/themes/shared/design-system/dist/tokens-table.mjs | 76+++++++++++++++++++++++++++++++++++++---------------------------------------
Mtools/lint/stylelint/stylelint-plugin-mozilla/rules/use-text-color-tokens.mjs | 29+++++++++++++++++++++++++++--
Mtools/lint/stylelint/stylelint-plugin-mozilla/tests/use-text-color-tokens.tests.mjs | 49+++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 211 insertions(+), 42 deletions(-)

diff --git a/docs/code-quality/lint/linters/stylelint-plugin-mozilla/rules/use-text-color-tokens.rst b/docs/code-quality/lint/linters/stylelint-plugin-mozilla/rules/use-text-color-tokens.rst @@ -103,3 +103,93 @@ The rule also allows these values non-token values: .current-text-color { color: currentColor; } + +.. code-block:: css + + .current-text-color { + color: white; + } + +.. code-block:: css + + .current-text-color { + color: black; + } + +Autofix functionality +--------------------- + +This rule can automatically fix some violations by replacing hex color values with +appropriate color names. Examples of autofixable violations: + +.. code-block:: css + + /* Before */ + .a { + color: #fff; + } + + /* After autofix */ + .a { + color: white; + } + +.. code-block:: css + + /* Before */ + .a { + color: #ffffff; + } + + /* After autofix */ + .a { + color: white; + } + +.. code-block:: css + + /* Before */ + .a { + color: #FFF; + } + + /* After autofix */ + .a { + color: white; + } + +.. code-block:: css + + /* Before */ + .a { + color: #FFFFFF; + } + + /* After autofix */ + .a { + color: white; + } + +.. code-block:: css + + /* Before */ + .a { + color: #000; + } + + /* After autofix */ + .a { + color: black; + } + +.. code-block:: css + + /* Before */ + .a { + color: #000000; + } + + /* After autofix */ + .a { + color: black; + } diff --git a/toolkit/themes/shared/design-system/config/tokens-config.js b/toolkit/themes/shared/design-system/config/tokens-config.js @@ -482,7 +482,6 @@ function formatTokensTableData(tokensData) { const SINGULAR_TABLE_CATEGORIES = [ "button", "color", - "link", "size", "space", "opacity", @@ -510,6 +509,14 @@ function getTableName(tokenName) { return "font-weight"; } + if (tokenName.includes("link-color")) { + return "text-color"; + } + + if (tokenName.includes("outline-offset")) { + return "outline"; + } + let replacePattern = /^(button-|input-text-|input-|focus-|checkbox-|table-row-|attention-dot-|promo-)/; if (tokenName.match(replacePattern)) { diff --git a/toolkit/themes/shared/design-system/dist/tokens-table.mjs b/toolkit/themes/shared/design-system/dist/tokens-table.mjs @@ -916,6 +916,42 @@ export const tokensTable = { }, { value: { + forcedColors: "LinkText", + brand: { default: "var(--color-accent-primary)" }, + platform: { default: "LinkText" }, + }, + name: "--link-color", + }, + { + value: { + forcedColors: "LinkText", + brand: { default: "var(--color-accent-primary-hover)" }, + platform: { + default: "color-mix(in srgb, black 10%, var(--link-color))", + }, + }, + name: "--link-color-hover", + }, + { + value: { + forcedColors: "ActiveText", + brand: { default: "var(--color-accent-primary-active)" }, + platform: { + default: "color-mix(in srgb, black 20%, var(--link-color))", + }, + }, + name: "--link-color-active", + }, + { + value: { + forcedColors: "var(--link-color)", + brand: { default: "var(--link-color)" }, + platform: { default: "var(--link-color)" }, + }, + name: "--link-color-visited", + }, + { + value: { prefersContrast: "CanvasText", brand: { light: "var(--color-gray-100)", @@ -980,6 +1016,7 @@ export const tokensTable = { }, { value: "2px", name: "--focus-outline-offset" }, { value: "2px", name: "--focus-outline-width" }, + { value: "1px", name: "--link-focus-outline-offset" }, { value: { light: "var(--color-red-70)", @@ -998,45 +1035,6 @@ export const tokensTable = { { value: "var(--size-item-medium)", name: "--icon-size-large" }, { value: "var(--size-item-large)", name: "--icon-size-xlarge" }, ], - link: [ - { - value: { - forcedColors: "LinkText", - brand: { default: "var(--color-accent-primary)" }, - platform: { default: "LinkText" }, - }, - name: "--link-color", - }, - { - value: { - forcedColors: "LinkText", - brand: { default: "var(--color-accent-primary-hover)" }, - platform: { - default: "color-mix(in srgb, black 10%, var(--link-color))", - }, - }, - name: "--link-color-hover", - }, - { - value: { - forcedColors: "ActiveText", - brand: { default: "var(--color-accent-primary-active)" }, - platform: { - default: "color-mix(in srgb, black 20%, var(--link-color))", - }, - }, - name: "--link-color-active", - }, - { - value: { - forcedColors: "var(--link-color)", - brand: { default: "var(--link-color)" }, - platform: { default: "var(--link-color)" }, - }, - name: "--link-color-visited", - }, - { value: "1px", name: "--link-focus-outline-offset" }, - ], "box-shadow": [ { value: diff --git a/tools/lint/stylelint/stylelint-plugin-mozilla/rules/use-text-color-tokens.mjs b/tools/lint/stylelint/stylelint-plugin-mozilla/rules/use-text-color-tokens.mjs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import stylelint from "stylelint"; +import valueParser from "postcss-value-parser"; import { namespace, createTokenNamesArray, @@ -25,7 +26,7 @@ const messages = ruleMessages(ruleName, { const meta = { url: "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/stylelint-plugin-mozilla/rules/use-text-color-tokens.html", - fixable: false, + fixable: true, }; const INCLUDE_CATEGORIES = ["text-color"]; @@ -33,10 +34,17 @@ const INCLUDE_CATEGORIES = ["text-color"]; const tokenCSS = createTokenNamesArray(INCLUDE_CATEGORIES); // Allowed text-color values in CSS -const ALLOW_LIST = createAllowList(["currentColor"]); +const ALLOW_LIST = createAllowList(["currentColor", "white", "black"]); const CSS_PROPERTIES = ["color"]; +const VIOLATION_AUTOFIX_MAP = { + "#fff": "white", + "#ffffff": "white", + "#000": "black", + "#000000": "black", +}; + const ruleFunction = primaryOption => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { @@ -76,6 +84,23 @@ const ruleFunction = primaryOption => { node: declarations, result, ruleName, + fix: () => { + const val = valueParser(declarations.value); + let hasFixes = false; + val.walk(node => { + if (node.type == "word") { + const token = + VIOLATION_AUTOFIX_MAP[node.value.trim().toLowerCase()]; + if (token) { + hasFixes = true; + node.value = token; + } + } + }); + if (hasFixes) { + declarations.value = val.toString(); + } + }, }); }); }; diff --git a/tools/lint/stylelint/stylelint-plugin-mozilla/tests/use-text-color-tokens.tests.mjs b/tools/lint/stylelint/stylelint-plugin-mozilla/tests/use-text-color-tokens.tests.mjs @@ -31,6 +31,10 @@ testRule({ description: "Using text color token for color is valid.", }, { + code: ".a { color: var(--link-color); }", + description: "Using link text color token for color is valid.", + }, + { code: ".a { color: var(--text-color, #000); }", description: "Using text color token with fallback value for color is valid.", @@ -106,3 +110,48 @@ testRule({ }, ], }); + +testRule({ + plugins: [plugin], + ruleName, + config: true, + fix: true, + reject: [ + { + code: ".a { color: #fff; }", + fixed: ".a { color: white; }", + message: messages.rejected("#fff"), + description: "#fff should be fixed to white.", + }, + { + code: ".a { color: #ffffff; }", + fixed: ".a { color: white; }", + message: messages.rejected("#ffffff"), + description: "#ffffff should be fixed to white.", + }, + { + code: ".a { color: #FFF; }", + fixed: ".a { color: white; }", + message: messages.rejected("#FFF"), + description: "#FFF should be fixed to white.", + }, + { + code: ".a { color: #FFFFFF; }", + fixed: ".a { color: white; }", + message: messages.rejected("#FFFFFF"), + description: "#FFFFFF should be fixed to white.", + }, + { + code: ".a { color: #000; }", + fixed: ".a { color: black; }", + message: messages.rejected("#000"), + description: "#000 should be fixed to black.", + }, + { + code: ".a { color: #000000; }", + fixed: ".a { color: black; }", + message: messages.rejected("#000000"), + description: "#000000 should be fixed to black.", + }, + ], +});