From 0465acf4b16cf5603453e24cec68267a1b1d80fc Mon Sep 17 00:00:00 2001 From: Matt Gallagher <48596928+matgalla@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:21:03 -0400 Subject: [PATCH 1/7] add semantic focus color in light/dark themes --- .../src/tokens/semantic/color/dark.json | 7 +++++++ .../src/tokens/semantic/color/light.json | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/packages/calcite-design-tokens/src/tokens/semantic/color/dark.json b/packages/calcite-design-tokens/src/tokens/semantic/color/dark.json index 77b76929aa9..7653b9ec10e 100644 --- a/packages/calcite-design-tokens/src/tokens/semantic/color/dark.json +++ b/packages/calcite-design-tokens/src/tokens/semantic/color/dark.json @@ -311,6 +311,13 @@ "category": "color" } } + }, + "focus": { + "value": "{semantic.color.brand.default.default}", + "type": "color", + "attributes": { + "category": "color" + } } } } diff --git a/packages/calcite-design-tokens/src/tokens/semantic/color/light.json b/packages/calcite-design-tokens/src/tokens/semantic/color/light.json index 85e2835aae1..0dfb44755ef 100644 --- a/packages/calcite-design-tokens/src/tokens/semantic/color/light.json +++ b/packages/calcite-design-tokens/src/tokens/semantic/color/light.json @@ -315,6 +315,13 @@ "category": "color" } } + }, + "focus": { + "value": "{semantic.color.brand.default.default}", + "type": "color", + "attributes": { + "category": "color" + } } } } From 8a8e8d46dd4a704f3221a7d8a382db69082a7d1b Mon Sep 17 00:00:00 2001 From: JC Franco Date: Fri, 11 Apr 2025 10:04:24 -0700 Subject: [PATCH 2/7] fix: restore --calcite-color-focus --- .../src/build/utils/output-references.ts | 5 +++++ packages/calcite-design-tokens/src/config/color/dark.ts | 4 ++++ packages/calcite-design-tokens/src/config/color/light.ts | 4 ++++ packages/calcite-design-tokens/src/config/index.ts | 5 +++-- 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 packages/calcite-design-tokens/src/build/utils/output-references.ts diff --git a/packages/calcite-design-tokens/src/build/utils/output-references.ts b/packages/calcite-design-tokens/src/build/utils/output-references.ts new file mode 100644 index 00000000000..17f419db4be --- /dev/null +++ b/packages/calcite-design-tokens/src/build/utils/output-references.ts @@ -0,0 +1,5 @@ +import type { OutputReferences } from "style-dictionary/types"; + +export const primitiveValueOutputReferences: Exclude = (token) => { + return !!(token.type === "color" && token.path.includes("focus")); +}; diff --git a/packages/calcite-design-tokens/src/config/color/dark.ts b/packages/calcite-design-tokens/src/config/color/dark.ts index 09fd3c139f2..ca27a7a0e77 100644 --- a/packages/calcite-design-tokens/src/config/color/dark.ts +++ b/packages/calcite-design-tokens/src/config/color/dark.ts @@ -8,6 +8,7 @@ import { expandTypesMap as sdTypes } from "@tokens-studio/sd-transforms"; import type { Config } from "../../types/extensions.js"; import { transformers, filters, headers, formats } from "../../build/registry/index.js"; import { Platform } from "../../build/utils/enums.js"; +import { primitiveValueOutputReferences } from "../../build/utils/output-references.js"; const config: Config = { source: ["src/tokens/semantic/color/dark.json"], @@ -29,6 +30,7 @@ const config: Config = { platform: Platform.scss, fileExtension: ".scss", fileHeader: headers.HeaderDefault, + outputReferences: primitiveValueOutputReferences, }, }, [Platform.css]: { @@ -46,6 +48,7 @@ const config: Config = { platform: Platform.css, fileExtension: ".css", fileHeader: headers.HeaderDefault, + outputReferences: primitiveValueOutputReferences, }, }, [Platform.es6]: { @@ -57,6 +60,7 @@ const config: Config = { platform: Platform.es6, fileExtension: ".js", fileHeader: headers.HeaderDefault, + outputReferences: primitiveValueOutputReferences, }, files: [ { diff --git a/packages/calcite-design-tokens/src/config/color/light.ts b/packages/calcite-design-tokens/src/config/color/light.ts index ed59e819e7b..50cc7931333 100644 --- a/packages/calcite-design-tokens/src/config/color/light.ts +++ b/packages/calcite-design-tokens/src/config/color/light.ts @@ -8,6 +8,7 @@ import { expandTypesMap as sdTypes } from "@tokens-studio/sd-transforms"; import type { Config } from "../../types/extensions.js"; import { transformers, filters, headers, formats } from "../../build/registry/index.js"; import { Platform } from "../../build/utils/enums.js"; +import { primitiveValueOutputReferences } from "../../build/utils/output-references.js"; const config: Config = { source: ["src/tokens/semantic/color/light.json"], @@ -29,6 +30,7 @@ const config: Config = { platform: Platform.scss, fileExtension: ".scss", fileHeader: headers.HeaderDefault, + outputReferences: primitiveValueOutputReferences, }, }, [Platform.css]: { @@ -46,6 +48,7 @@ const config: Config = { platform: Platform.css, fileExtension: ".css", fileHeader: headers.HeaderDefault, + outputReferences: primitiveValueOutputReferences, }, }, [Platform.es6]: { @@ -57,6 +60,7 @@ const config: Config = { platform: Platform.es6, fileExtension: ".js", fileHeader: headers.HeaderDefault, + outputReferences: primitiveValueOutputReferences, }, files: [ { diff --git a/packages/calcite-design-tokens/src/config/index.ts b/packages/calcite-design-tokens/src/config/index.ts index 89e82970ec3..6cc376dfe01 100644 --- a/packages/calcite-design-tokens/src/config/index.ts +++ b/packages/calcite-design-tokens/src/config/index.ts @@ -11,6 +11,7 @@ import type { Config } from "../types/extensions.js"; import { preprocessors, transformers, filters, headers, formats } from "../build/registry/index.js"; import { isBreakpointExpand, isCornerRadius } from "../build/utils/token-types.js"; import { Platform } from "../build/utils/enums.js"; +import { primitiveValueOutputReferences } from "../build/utils/output-references.js"; const commonExpand = { include: ["color"], @@ -24,9 +25,9 @@ const commonExpand = { }, }; -const stylesheetOutputReferences: OutputReferences = (token) => { +const stylesheetOutputReferences: OutputReferences = (token, options) => { // output specific token references to match test output - return !!(isCornerRadius(token) && token.path.includes("default")); + return !!(isCornerRadius(token) && token.path.includes("default")) || primitiveValueOutputReferences(token, options); }; const config: Config = { From 194305554ee629e91fc9206b1c168ba36bbee02b Mon Sep 17 00:00:00 2001 From: JC Franco Date: Tue, 15 Apr 2025 12:56:57 -0700 Subject: [PATCH 3/7] update test snapshots --- .../spec/__snapshots__/index.spec.ts.snap | 82 ++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/packages/calcite-design-tokens/tests/spec/__snapshots__/index.spec.ts.snap b/packages/calcite-design-tokens/tests/spec/__snapshots__/index.spec.ts.snap index 96b3028222e..e429d00e985 100644 --- a/packages/calcite-design-tokens/tests/spec/__snapshots__/index.spec.ts.snap +++ b/packages/calcite-design-tokens/tests/spec/__snapshots__/index.spec.ts.snap @@ -899,12 +899,12 @@ exports[`generated tokens > CSS > dark should match 1`] = ` */ :root { + --calcite-color-foreground-current: #214155; --calcite-color-background: #353535; --calcite-color-background-none: rgba(255, 255, 255, 0); --calcite-color-foreground-1: #2b2b2b; --calcite-color-foreground-2: #202020; --calcite-color-foreground-3: #151515; - --calcite-color-foreground-current: #214155; --calcite-color-transparent: rgba(255, 255, 255, 0); --calcite-color-transparent-hover: rgba(255, 255, 255, 0.04); --calcite-color-transparent-press: rgba(255, 255, 255, 0.08); @@ -940,6 +940,7 @@ exports[`generated tokens > CSS > dark should match 1`] = ` --calcite-color-border-input: #757575; --calcite-color-border-ghost: rgba(117, 117, 117, 0.3); --calcite-color-border-white: #f8f8f8; + --calcite-color-focus: var(--calcite-color-brand); } " `; @@ -1058,6 +1059,7 @@ exports[`generated tokens > CSS > global should match 1`] = ` --calcite-z-index-modal: 800; --calcite-z-index-popup: 900; --calcite-z-index-tooltip: 901; + --calcite-color-focus: var(--calcite-color-brand); --calcite-corner-radius: var(--calcite-corner-radius-sharp); --calcite-shadow-sm: 0 2px 8px 0 rgba(0, 0, 0, 0.04), 0 4px 16px 0 rgba(0, 0, 0, 0.08); --calcite-shadow-md: 0 4px 20px 0 rgba(0, 0, 0, 0.08), 0 12px 30px rgba(0, 0, 0, 0.1); @@ -1115,6 +1117,7 @@ exports[`generated tokens > CSS > index should match 1`] = ` --calcite-color-foreground-1: #ffffff; --calcite-color-background-none: rgba(255, 255, 255, 0); --calcite-color-background: #f8f8f8; + --calcite-color-focus: var(--calcite-color-brand); } @media (prefers-color-scheme: light) { .calcite-mode-auto { @@ -1159,6 +1162,7 @@ exports[`generated tokens > CSS > index should match 1`] = ` --calcite-color-foreground-1: #ffffff; --calcite-color-background-none: rgba(255, 255, 255, 0); --calcite-color-background: #f8f8f8; + --calcite-color-focus: var(--calcite-color-brand); } } @media (prefers-color-scheme: dark) { @@ -1204,6 +1208,7 @@ exports[`generated tokens > CSS > index should match 1`] = ` --calcite-color-foreground-1: #2b2b2b; --calcite-color-background-none: rgba(255, 255, 255, 0); --calcite-color-background: #353535; + --calcite-color-focus: var(--calcite-color-brand); } } .calcite-mode-light { @@ -1248,6 +1253,7 @@ exports[`generated tokens > CSS > index should match 1`] = ` --calcite-color-foreground-1: #ffffff; --calcite-color-background-none: rgba(255, 255, 255, 0); --calcite-color-background: #f8f8f8; + --calcite-color-focus: var(--calcite-color-brand); } .calcite-mode-dark { --calcite-color-foreground-current: #214155; @@ -1291,6 +1297,7 @@ exports[`generated tokens > CSS > index should match 1`] = ` --calcite-color-foreground-1: #2b2b2b; --calcite-color-background-none: rgba(255, 255, 255, 0); --calcite-color-background: #353535; + --calcite-color-focus: var(--calcite-color-brand); } " `; @@ -1343,6 +1350,7 @@ exports[`generated tokens > CSS > light should match 1`] = ` --calcite-color-border-input: #949494; --calcite-color-border-ghost: rgba(0, 0, 0, 0.3); --calcite-color-border-white: #ffffff; + --calcite-color-focus: var(--calcite-color-brand); } " `; @@ -18756,6 +18764,32 @@ exports[`generated tokens > DOCS > global should match 1`] = ` "path": ["semantic", "color", "border", "white"], "key": "{semantic.color.border.white}" }, + { + "value": "{\\"light\\":\\"#007ac2\\",\\"dark\\":\\"#009af2\\"}", + "type": "color", + "attributes": { + "category": "color", + "type": "color", + "item": "focus", + "names": { + "scss": "$calcite-color-focus", + "css": "var(--calcite-color-focus)", + "js": "semantic.color.focus", + "docs": "semantic.color.focus", + "es6": "calciteColorFocus" + }, + "calcite-schema": { + "system": "calcite", + "tier": "color", + "type": "color" + } + }, + "filePath": "src/tokens/semantic/color/light.json", + "isSource": false, + "name": "Color Focus", + "path": ["semantic", "color", "focus"], + "key": "{semantic.color.focus}" + }, { "value": "0", "type": "dimension", @@ -29993,6 +30027,7 @@ export const calciteColorBorderGhost = { dark: "rgba(117, 117, 117, 0.3)", }; export const calciteColorBorderWhite = { light: "#ffffff", dark: "#f8f8f8" }; +export const calciteColorFocus = { light: "#007ac2", dark: "#009af2" }; export const calciteBorderWidthNone = "0"; export const calciteBorderWidthSm = "1px"; export const calciteBorderWidthMd = "2px"; @@ -30448,6 +30483,7 @@ export const calciteColorBorder3: { light: string; dark: string }; export const calciteColorBorderInput: { light: string; dark: string }; export const calciteColorBorderGhost: { light: string; dark: string }; export const calciteColorBorderWhite: { light: string; dark: string }; +export const calciteColorFocus: { light: string; dark: string }; export const calciteBorderWidthNone: string; export const calciteBorderWidthSm: string; export const calciteBorderWidthMd: string; @@ -54418,6 +54454,42 @@ export default { key: "{semantic.color.border.white}", }, }, + focus: { + value: { + light: "#007ac2", + dark: "#009af2", + }, + type: "color", + attributes: { + category: "color", + type: "color", + item: "focus", + names: { + scss: "$calcite-color-focus", + css: "var(--calcite-color-focus)", + js: "semantic.color.focus", + docs: "semantic.color.focus", + es6: "calciteColorFocus", + }, + "calcite-schema": { + system: "calcite", + tier: "color", + type: "color", + }, + }, + filePath: "src/tokens/semantic/color/light.json", + isSource: false, + original: { + value: "{semantic.color.brand.default.default}", + type: "color", + attributes: { + category: "color", + }, + }, + name: "Color Focus", + path: ["semantic", "color", "focus"], + key: "{semantic.color.focus}", + }, }, border: { width: { @@ -62657,6 +62729,7 @@ declare const tokens: { ghost: DesignToken; white: DesignToken; }; + focus: DesignToken; }; border: { width: { @@ -68616,12 +68689,12 @@ exports[`generated tokens > SCSS > dark should match 1`] = ` // Calcite Design System // Do not edit directly, this file was auto-generated. +$calcite-color-foreground-current: #214155; $calcite-color-background: #353535; $calcite-color-background-none: rgba(255, 255, 255, 0); $calcite-color-foreground-1: #2b2b2b; $calcite-color-foreground-2: #202020; $calcite-color-foreground-3: #151515; -$calcite-color-foreground-current: #214155; $calcite-color-transparent: rgba(255, 255, 255, 0); $calcite-color-transparent-hover: rgba(255, 255, 255, 0.04); $calcite-color-transparent-press: rgba(255, 255, 255, 0.08); @@ -68657,6 +68730,7 @@ $calcite-color-border-3: #404040; $calcite-color-border-input: #757575; $calcite-color-border-ghost: rgba(117, 117, 117, 0.3); $calcite-color-border-white: #f8f8f8; +$calcite-color-focus: $calcite-color-brand; " `; @@ -68772,6 +68846,7 @@ $calcite-z-index-overlay: 700; $calcite-z-index-modal: 800; $calcite-z-index-popup: 900; $calcite-z-index-tooltip: 901; +$calcite-color-focus: $calcite-color-brand; $calcite-corner-radius: $calcite-corner-radius-sharp; $calcite-shadow-sm: 0 2px 8px 0 rgba(0, 0, 0, 0.04), 0 4px 16px 0 rgba(0, 0, 0, 0.08); $calcite-shadow-md: 0 4px 20px 0 rgba(0, 0, 0, 0.08), 0 12px 30px rgba(0, 0, 0, 0.1); @@ -68829,6 +68904,7 @@ exports[`generated tokens > SCSS > index should match 1`] = ` --calcite-color-foreground-1: #ffffff; --calcite-color-background-none: rgba(255, 255, 255, 0); --calcite-color-background: #f8f8f8; + --calcite-color-focus: var(--calcite-color-brand); } @mixin calcite-mode-dark { --calcite-color-foreground-current: #214155; @@ -68872,6 +68948,7 @@ exports[`generated tokens > SCSS > index should match 1`] = ` --calcite-color-foreground-1: #2b2b2b; --calcite-color-background-none: rgba(255, 255, 255, 0); --calcite-color-background: #353535; + --calcite-color-focus: var(--calcite-color-brand); } " `; @@ -68922,6 +68999,7 @@ $calcite-color-border-3: #dfdfdf; $calcite-color-border-input: #949494; $calcite-color-border-ghost: rgba(0, 0, 0, 0.3); $calcite-color-border-white: #ffffff; +$calcite-color-focus: $calcite-color-brand; " `; From 852efcf30b1a09ffdfdf001964235684893804ce Mon Sep 17 00:00:00 2001 From: Matt Gallagher <48596928+matgalla@users.noreply.github.com> Date: Wed, 28 May 2025 20:23:12 -0400 Subject: [PATCH 4/7] add `.default` to focus token path and add `scope` attribute --- .../src/tokens/semantic/color/dark.json | 11 +++++++---- .../src/tokens/semantic/color/light.json | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/calcite-design-tokens/src/tokens/semantic/color/dark.json b/packages/calcite-design-tokens/src/tokens/semantic/color/dark.json index 7653b9ec10e..bc10e5e9258 100644 --- a/packages/calcite-design-tokens/src/tokens/semantic/color/dark.json +++ b/packages/calcite-design-tokens/src/tokens/semantic/color/dark.json @@ -313,10 +313,13 @@ } }, "focus": { - "value": "{semantic.color.brand.default.default}", - "type": "color", - "attributes": { - "category": "color" + "default": { + "value": "{semantic.color.brand.default.default}", + "type": "color", + "attributes": { + "category": "color", + "scope": "component" + } } } } diff --git a/packages/calcite-design-tokens/src/tokens/semantic/color/light.json b/packages/calcite-design-tokens/src/tokens/semantic/color/light.json index 0dfb44755ef..8b241646e15 100644 --- a/packages/calcite-design-tokens/src/tokens/semantic/color/light.json +++ b/packages/calcite-design-tokens/src/tokens/semantic/color/light.json @@ -317,10 +317,13 @@ } }, "focus": { - "value": "{semantic.color.brand.default.default}", - "type": "color", - "attributes": { - "category": "color" + "default": { + "value": "{semantic.color.brand.default.default}", + "type": "color", + "attributes": { + "category": "color", + "scope": "component" + } } } } From da2a8a4ba2e5c6311e634ed3c8638e435411472d Mon Sep 17 00:00:00 2001 From: JC Franco Date: Mon, 16 Jun 2025 23:22:50 -0700 Subject: [PATCH 5/7] chore(tokens): enable strict and drop ypescript-strict-plugin --- packages/calcite-design-tokens/tsconfig-base.json | 1 + packages/calcite-design-tokens/tsconfig.json | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/calcite-design-tokens/tsconfig-base.json b/packages/calcite-design-tokens/tsconfig-base.json index 9889a950b76..96db2992b23 100644 --- a/packages/calcite-design-tokens/tsconfig-base.json +++ b/packages/calcite-design-tokens/tsconfig-base.json @@ -11,6 +11,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "resolveJsonModule": true, + "strict": true, "target": "ESNext" }, "exclude": ["node_modules", "dist"] diff --git a/packages/calcite-design-tokens/tsconfig.json b/packages/calcite-design-tokens/tsconfig.json index cbad790ba5a..2a0eda3b0da 100755 --- a/packages/calcite-design-tokens/tsconfig.json +++ b/packages/calcite-design-tokens/tsconfig.json @@ -1,11 +1,4 @@ { - "compilerOptions": { - "plugins": [ - { - "name": "typescript-strict-plugin" - } - ] - }, "extends": "./tsconfig-base", "include": ["src"] } From 5d9279758555c9798a14d81ec52cc3dae2658621 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Mon, 16 Jun 2025 15:22:24 -0700 Subject: [PATCH 6/7] update build to generate component/host-specific stylesheet --- .../src/assets/styles/includes.scss | 1 + .../calcite-components/tailwind.config.ts | 23 +++++ .../src/build/format/component.ts | 85 +++++++++++++++++ .../src/build/format/index-file.ts | 21 ++--- .../src/build/format/index.ts | 3 + .../src/build/format/utils/index.ts | 25 ++++- .../src/build/overrides/index.ts | 45 ++++++++- .../src/build/utils/dictionary.ts | 15 ++- .../calcite-design-tokens/src/config/index.ts | 5 + .../spec/__snapshots__/index.spec.ts.snap | 94 +++++++++++++++++++ .../tests/spec/index.spec.ts | 5 +- packages/calcite-tailwind-preset/src/index.ts | 23 +---- packages/calcite-tailwind-preset/src/utils.ts | 21 +++++ .../calcite-tailwind-preset/tsconfig.json | 2 +- .../calcite-tailwind-preset/vite.config.mts | 17 ++-- 15 files changed, 338 insertions(+), 47 deletions(-) create mode 100644 packages/calcite-design-tokens/src/build/format/component.ts create mode 100644 packages/calcite-tailwind-preset/src/utils.ts diff --git a/packages/calcite-components/src/assets/styles/includes.scss b/packages/calcite-components/src/assets/styles/includes.scss index cdfeb844181..fc1641c06e8 100644 --- a/packages/calcite-components/src/assets/styles/includes.scss +++ b/packages/calcite-components/src/assets/styles/includes.scss @@ -1,3 +1,4 @@ +@import "@esri/calcite-design-tokens/dist/css/component.css"; @import "@esri/calcite-design-tokens/dist/scss/index"; @import "@esri/calcite-design-tokens/dist/scss/core"; diff --git a/packages/calcite-components/tailwind.config.ts b/packages/calcite-components/tailwind.config.ts index 07bf7afde80..60751861b15 100644 --- a/packages/calcite-components/tailwind.config.ts +++ b/packages/calcite-components/tailwind.config.ts @@ -1,5 +1,7 @@ import type { Config } from "tailwindcss"; import calcitePreset from "@esri/calcite-tailwind-preset"; +import { invert } from "@esri/calcite-tailwind-preset/dist/utils"; +import plugin from "tailwindcss/plugin"; const config: Config = { presets: [calcitePreset], @@ -17,6 +19,27 @@ const config: Config = { }, }, }, + plugins: [ + plugin(({ addUtilities }) => { + addUtilities({ + // we override preset focus utils to allow using the internal focus color + ".focus-base": { + outline: "2px solid", + "outline-color": "transparent", + "outline-offset": invert("2px", "--calcite-offset-invert-focus"), + }, + ".focus-normal": { + "outline-color": " var(--calcite-internal-color-focus)", + }, + ".focus-outset": { + "outline-color": " var(--calcite-internal-color-focus)", + }, + ".focus-inset": { + "outline-color": " var(--calcite-internal-color-focus)", + }, + }); + }), + ], }; export default config; diff --git a/packages/calcite-design-tokens/src/build/format/component.ts b/packages/calcite-design-tokens/src/build/format/component.ts new file mode 100644 index 00000000000..a2efd1d542a --- /dev/null +++ b/packages/calcite-design-tokens/src/build/format/component.ts @@ -0,0 +1,85 @@ +import prettierSync from "@prettier/sync"; +import type { FormatFn, TransformedToken } from "style-dictionary/types"; +import { fileHeader } from "style-dictionary/utils"; +import StyleDictionary from "style-dictionary"; +import { Config, PlatformConfig } from "../../types/extensions.js"; +import { RegisterFn, Stylesheet } from "../../types/interfaces.js"; +import { fromTokens } from "../utils/dictionary.js"; +import { light } from "../dictionaries/index.js"; +import { Platform } from "../utils/enums.js"; +import { createVarList } from "./utils/index.js"; + +export const FormatComponent = "calcite/format/component"; + +export const registerFormatComponent: RegisterFn = () => { + StyleDictionary.registerFormat({ + name: FormatComponent, + format: formatComponentFile, + }); +}; + +export const formatComponentFile: FormatFn = async (args) => { + const { file, options, platform } = args; + const platformConfig = platform as PlatformConfig; + + if (platformConfig.options.platform !== Platform.css) { + throw new Error("Only css platform is supported."); + } + + const header = await fileHeader({ file }); + const stylesheetFormat: Stylesheet = platformConfig.options.platform; + + const lightDictionary = await light.getPlatformTokens(options.platform, { cache: true }); + const filteredTokens: TransformedToken[] = []; + const componentTokens = lightDictionary.allTokens.filter((token) => { + const isComponent = token.attributes?.scope === "component"; + if (!isComponent) { + filteredTokens.push(token); + } + return isComponent; + }); + + const componentTokenDictionary = fromTokens(componentTokens, filteredTokens); + const componentVarDeclarations = createVarList(stylesheetFormat, componentTokenDictionary, { + ...args, + options: { + ...args.options, + // assumes all component tokens need to use a reference (custom CSS prop) as the value + outputReferences: true, + }, + }) + .split(";") + .filter(Boolean) + .map((varDeclaration) => varDeclaration.split(":").map((part) => part.trim())); + + const prefix = (options as Config).platforms[platformConfig.options.platform].prefix!; + const componentScopedVars = componentVarDeclarations.map(([name, value]) => { + // we define internal variables to allow overrides at a global or component level + const internalVarName = name.replace(prefix, `${prefix}-internal`); + + return `${internalVarName}: var(${name}, var(${getLegacyVarName(name, prefix)}, ${value.trim()}))`; + }); + + const content = `:host {${componentScopedVars.join(";\n")}}`; + + return prettierSync.format(`${header}${content}`, { + parser: stylesheetFormat, + }); +}; + +/** + * This is based on the focus color variable, but we could use additional token attributes to get the proper legacy var name + * + * @param name - The name of the token. + * @param prefix - The prefix used in the token name. + */ +function getLegacyVarName(name: string, prefix: string): string { + const legacyPrefix = `${prefix}-ui`; + const suffix = name + .slice(name.indexOf(prefix) + prefix.length) + .split("-") + .filter(Boolean) + .reverse() + .join("-"); + return `--${legacyPrefix}-${suffix}`; +} diff --git a/packages/calcite-design-tokens/src/build/format/index-file.ts b/packages/calcite-design-tokens/src/build/format/index-file.ts index 4a0161f422c..38b4bf9653d 100644 --- a/packages/calcite-design-tokens/src/build/format/index-file.ts +++ b/packages/calcite-design-tokens/src/build/format/index-file.ts @@ -1,6 +1,6 @@ import prettierSync from "@prettier/sync"; -import type { Dictionary, FormatFn, FormatFnArguments } from "style-dictionary/types"; -import { fileHeader, formattedVariables } from "style-dictionary/utils"; +import type { FormatFn } from "style-dictionary/types"; +import { fileHeader } from "style-dictionary/utils"; import StyleDictionary from "style-dictionary"; import { PlatformConfig } from "../../types/extensions.js"; import { RegisterFn, Stylesheet } from "../../types/interfaces.js"; @@ -8,6 +8,7 @@ import { fromTokens } from "../utils/dictionary.js"; import { isThemed } from "../utils/token-types.js"; import { dark, light } from "../dictionaries/index.js"; import { Platform } from "../utils/enums.js"; +import { createVarList } from "./utils/index.js"; export const registerFormatIndex: RegisterFn = () => { StyleDictionary.registerFormat({ @@ -37,12 +38,16 @@ export const formatIndexFile: FormatFn = async (args) => { const varLists = { light: createVarList( commonVarFormat, - fromTokens(lightDictionary.allTokens.filter((token) => isThemed(token, { theme: "light" }))), + fromTokens( + lightDictionary.allTokens.filter((token) => isThemed(token) && token.attributes?.scope !== "component"), + ), args, ), dark: createVarList( commonVarFormat, - fromTokens(darkDictionary.allTokens.filter((token) => isThemed(token, { theme: "dark" }))), + fromTokens( + darkDictionary.allTokens.filter((token) => isThemed(token) && token.attributes?.scope !== "component"), + ), args, ), } as const; @@ -72,12 +77,4 @@ function importUrl(fileName: string, fileExtension: string) { return `@import ${fileExtension === ".css" ? `url("./${fileBaseName}")` : `"./${fileBaseName}"`};`; } -function createVarList(format: Stylesheet, dictionary: Dictionary, args: FormatFnArguments) { - return formattedVariables({ - format, - dictionary, - ...args.options, - }); -} - export const FormatIndex = "calcite/format/index"; diff --git a/packages/calcite-design-tokens/src/build/format/index.ts b/packages/calcite-design-tokens/src/build/format/index.ts index f8caf3894c2..c388d62f162 100644 --- a/packages/calcite-design-tokens/src/build/format/index.ts +++ b/packages/calcite-design-tokens/src/build/format/index.ts @@ -2,15 +2,18 @@ import { registerFormatDocs } from "./docs.js"; import { registerFormatJs } from "./javascript.js"; import { registerFormatTypography } from "./typography.js"; import { registerFormatIndex } from "./index-file.js"; +import { registerFormatComponent } from "./component.js"; export function registerFormats(): void { registerFormatDocs(); registerFormatJs(); registerFormatTypography(); registerFormatIndex(); + registerFormatComponent(); } export { FormatCalciteJs } from "./javascript.js"; export { FormatCalciteDocs } from "./docs.js"; export { FormatTypography } from "./typography.js"; export { FormatIndex } from "./index-file.js"; +export { FormatComponent } from "./component.js"; diff --git a/packages/calcite-design-tokens/src/build/format/utils/index.ts b/packages/calcite-design-tokens/src/build/format/utils/index.ts index 2390582e8c8..3b5e62e4d86 100644 --- a/packages/calcite-design-tokens/src/build/format/utils/index.ts +++ b/packages/calcite-design-tokens/src/build/format/utils/index.ts @@ -1,9 +1,11 @@ -import type { TransformedToken } from "style-dictionary/types"; +import type { FormatFnArguments, Dictionary, TransformedToken } from "style-dictionary/types"; +import { formattedVariables } from "style-dictionary/utils"; +import { Stylesheet } from "../../../types/interfaces.js"; /** * Helper function to remove extraneous token attributes * - * Removal of these fields are to get output as similar as possible to production + * Removal of these fields is to get output as similar as possible to production * it can be removed afterward * * @param token @@ -31,3 +33,22 @@ export function cleanAttributes(token: TransformedToken): void { delete token.$extensions; } } + +/** + * Util to create a var list from a format's arguments + * + * @param format + * @param dictionary + * @param args + */ +export function createVarList( + format: Stylesheet, + dictionary: Dictionary, + args: FormatFnArguments, +): ReturnType { + return formattedVariables({ + format, + dictionary, + ...args.options, + }); +} diff --git a/packages/calcite-design-tokens/src/build/overrides/index.ts b/packages/calcite-design-tokens/src/build/overrides/index.ts index aee12e680cb..fb99c3c5db2 100644 --- a/packages/calcite-design-tokens/src/build/overrides/index.ts +++ b/packages/calcite-design-tokens/src/build/overrides/index.ts @@ -1,5 +1,5 @@ import StyleDictionary from "style-dictionary"; -import type { Transform } from "style-dictionary/types"; +import type { Config, Transform, TransformedToken, ValueTransform } from "style-dictionary/types"; import { alignTypes, excludeParentKeys } from "@tokens-studio/sd-transforms"; import { PlatformConfig } from "../../types/extensions.js"; import { isBreakpoint, isBreakpointRelated, isFontRelated } from "../utils/token-types.js"; @@ -73,8 +73,51 @@ function overrideTokenStudioPreprocessors(): void { } function overrideTokenStudioTransforms(): void { + function transformThemeColor( + theme: "light" | "dark", + target: any, + context: { + token: TransformedToken; + transform: ValueTransform; + config: PlatformConfig; + options: Config; + }, + ): void { + context.token.value.color = context.token.value[theme]; + target[theme] = ( + context.transform.transform(context.token, context.config, context.options) as { + color: string; + } + ).color; + delete context.token.value.color; + } + const sd = StyleDictionary; + overrideTransform("ts/color/css/hexrgba", sd, (ogTransform) => ({ + transform: (token, config, options) => { + const isLegacyThemeToken = typeof token.value === "object" && "light" in token.value; + if (isLegacyThemeToken) { + const ogType = token.type; + token.type = "shadow"; // force the transform to process object structure + const transformed = {}; + const context = { + token, + transform: ogTransform as ValueTransform, + config: config as PlatformConfig, + options, + } as const; + transformThemeColor("light", transformed, context); + transformThemeColor("dark", transformed, context); + token.type = ogType; + + return transformed; + } + + return ogTransform.transform(token, config, options); + }, + })); + overrideTransform("ts/size/px", sd, (ogTransform) => ({ filter: (token, options) => { const shouldSkip = token.isSource && isBreakpointRelated(token); diff --git a/packages/calcite-design-tokens/src/build/utils/dictionary.ts b/packages/calcite-design-tokens/src/build/utils/dictionary.ts index 8e50181766a..67b986f2b75 100644 --- a/packages/calcite-design-tokens/src/build/utils/dictionary.ts +++ b/packages/calcite-design-tokens/src/build/utils/dictionary.ts @@ -1,10 +1,21 @@ import type { Dictionary } from "style-dictionary/types"; import { convertTokenData } from "style-dictionary/utils"; -export function fromTokens(tokens: Parameters[0]): Dictionary { - return { +export function fromTokens( + tokens: Parameters[0], + unfilteredTokens?: Parameters[0], +): Dictionary { + const dictionary: Dictionary = { tokens: convertTokenData(tokens, { output: "object" }), allTokens: convertTokenData(tokens, { output: "array" }), tokenMap: convertTokenData(tokens, { output: "map" }), }; + + if (unfilteredTokens) { + dictionary.unfilteredTokens = convertTokenData(unfilteredTokens, { output: "object" }); + dictionary.unfilteredAllTokens = convertTokenData(tokens, { output: "array" }); + dictionary.unfilteredTokenMap = convertTokenData(tokens, { output: "map" }); + } + + return dictionary; } diff --git a/packages/calcite-design-tokens/src/config/index.ts b/packages/calcite-design-tokens/src/config/index.ts index 931cc2ac8e2..d24114b2120 100644 --- a/packages/calcite-design-tokens/src/config/index.ts +++ b/packages/calcite-design-tokens/src/config/index.ts @@ -129,6 +129,11 @@ const config: Config = { imports: ["semantic", "classes"], }, }, + { + destination: "component.css", + format: formats.FormatComponent, + filter: filters.FilterLightOrDarkColorTokens, + }, ], expand: { ...commonExpand, diff --git a/packages/calcite-design-tokens/tests/spec/__snapshots__/index.spec.ts.snap b/packages/calcite-design-tokens/tests/spec/__snapshots__/index.spec.ts.snap index 70d78db7ff1..cc39cf5f01d 100644 --- a/packages/calcite-design-tokens/tests/spec/__snapshots__/index.spec.ts.snap +++ b/packages/calcite-design-tokens/tests/spec/__snapshots__/index.spec.ts.snap @@ -309,6 +309,21 @@ exports[`generated tokens > CSS > classes should match 1`] = ` " `; +exports[`generated tokens > CSS > component should match 1`] = ` +"/** + * Calcite Design System + * Do not edit directly, this file was auto-generated. + */ + +:host { + --calcite-internal-color-focus: var( + --calcite-color-focus, + var(--calcite-ui-focus-color, var(--calcite-color-brand)) + ); +} +" +`; + exports[`generated tokens > CSS > core should match 1`] = ` "/** * Calcite Design System @@ -946,6 +961,7 @@ exports[`generated tokens > CSS > dark should match 1`] = ` --calcite-color-border-input: #757575; --calcite-color-border-ghost: rgba(117, 117, 117, 0.3); --calcite-color-border-white: #f7f7f7; + --calcite-color-focus: var(--calcite-color-brand); } " `; @@ -1376,6 +1392,7 @@ exports[`generated tokens > CSS > light should match 1`] = ` --calcite-color-border-input: #949494; --calcite-color-border-ghost: rgba(0, 0, 0, 0.3); --calcite-color-border-white: #ffffff; + --calcite-color-focus: var(--calcite-color-brand); } " `; @@ -18923,6 +18940,34 @@ exports[`generated tokens > DOCS (internal) > global should match 1`] = ` "name": "Color Border White", "path": ["semantic", "color", "border", "white"] }, + { + "key": "{semantic.color.focus.default}", + "value": "{\\"light\\":\\"#007ac2\\",\\"dark\\":\\"#009af2\\"}", + "type": "color", + "attributes": { + "category": "color", + "scope": "component", + "type": "color", + "item": "focus", + "subitem": "default", + "names": { + "scss": "$calcite-color-focus", + "css": "var(--calcite-color-focus)", + "js": "semantic.color.focus.default", + "docs": "semantic.color.focus.default", + "es6": "calciteColorFocus" + }, + "calcite-schema": { + "system": "calcite", + "tier": "color", + "type": "color" + } + }, + "filePath": "src/tokens/semantic/color/light.json", + "isSource": false, + "name": "Color Focus", + "path": ["semantic", "color", "focus", "default"] + }, { "key": "{semantic.border.width.none}", "value": "0", @@ -30473,6 +30518,7 @@ export const calciteColorBorderGhost = { dark: "rgba(117, 117, 117, 0.3)", }; export const calciteColorBorderWhite = { light: "#ffffff", dark: "#f7f7f7" }; +export const calciteColorFocus = { light: "#007ac2", dark: "#009af2" }; export const calciteBorderWidthNone = "0"; export const calciteBorderWidthSm = "1px"; export const calciteBorderWidthMd = "2px"; @@ -30942,6 +30988,7 @@ export const calciteColorBorder3: { light: string; dark: string }; export const calciteColorBorderInput: { light: string; dark: string }; export const calciteColorBorderGhost: { light: string; dark: string }; export const calciteColorBorderWhite: { light: string; dark: string }; +export const calciteColorFocus: { light: string; dark: string }; export const calciteBorderWidthNone: string; export const calciteBorderWidthSm: string; export const calciteBorderWidthMd: string; @@ -55924,6 +55971,48 @@ export default { path: ["semantic", "color", "border", "white"], }, }, + focus: { + default: { + key: "{semantic.color.focus.default}", + value: { + light: "#007ac2", + dark: "#009af2", + }, + type: "color", + attributes: { + category: "color", + scope: "component", + type: "color", + item: "focus", + subitem: "default", + names: { + scss: "$calcite-color-focus", + css: "var(--calcite-color-focus)", + js: "semantic.color.focus.default", + docs: "semantic.color.focus.default", + es6: "calciteColorFocus", + }, + "calcite-schema": { + system: "calcite", + tier: "color", + type: "color", + }, + }, + filePath: "src/tokens/semantic/color/light.json", + isSource: false, + original: { + value: "{semantic.color.brand.default.default}", + type: "color", + attributes: { + category: "color", + scope: "component", + }, + key: "{semantic.color.focus.default}", + }, + name: "Color Focus", + path: ["semantic", "color", "focus", "default"], + }, + }, }, border: { width: { @@ -64455,6 +64544,9 @@ declare const tokens: { ghost: DesignToken; white: DesignToken; }; + focus: { + default: DesignToken; + }; }; border: { width: { @@ -70747,6 +70839,7 @@ $calcite-color-border-3: #404040; $calcite-color-border-input: #757575; $calcite-color-border-ghost: rgba(117, 117, 117, 0.3); $calcite-color-border-white: #f7f7f7; +$calcite-color-focus: $calcite-color-brand; " `; @@ -71027,6 +71120,7 @@ $calcite-color-border-3: #dedede; $calcite-color-border-input: #949494; $calcite-color-border-ghost: rgba(0, 0, 0, 0.3); $calcite-color-border-white: #ffffff; +$calcite-color-focus: $calcite-color-brand; " `; diff --git a/packages/calcite-design-tokens/tests/spec/index.spec.ts b/packages/calcite-design-tokens/tests/spec/index.spec.ts index ee8491385a6..34ba97fc8a2 100644 --- a/packages/calcite-design-tokens/tests/spec/index.spec.ts +++ b/packages/calcite-design-tokens/tests/spec/index.spec.ts @@ -15,7 +15,10 @@ const platforms: { files: string[]; internal?: boolean; }[] = [ - { name: Platform.CSS, files: ["breakpoints", "classes", "core", "dark", "global", "index", "light", "semantic"] }, + { + name: Platform.CSS, + files: ["breakpoints", "classes", "component", "core", "dark", "global", "index", "light", "semantic"], + }, { name: Platform.SCSS, files: ["breakpoints", "core", "dark", "global", "index", "light", "mixins", "semantic"] }, { name: Platform.ES6, files: ["breakpoints", "core", "global", "semantic"] }, { name: Platform.DOCS, files: ["core", "global", "semantic"], internal: true }, diff --git a/packages/calcite-tailwind-preset/src/index.ts b/packages/calcite-tailwind-preset/src/index.ts index 67b5d9021a5..86b30860f41 100644 --- a/packages/calcite-tailwind-preset/src/index.ts +++ b/packages/calcite-tailwind-preset/src/index.ts @@ -1,28 +1,7 @@ import type { Config } from "tailwindcss"; import flattenColorPalette from "tailwindcss/lib/util/flattenColorPalette"; import plugin from "tailwindcss/plugin"; - -/** - * This helper inverts a value based on a boolean CSS prop flag - * - * When the flag is 0, it will not be inverted. When 1, it will invert it (multiplied by -1) - * - * @param {string} value - the CSS value to invert - * @param {string} flagPropName - the boolean CSS prop (value must be 0 or 1) - */ -function invert(value: string, flagPropName: string): string { - return `calc( - ${value} * - calc( - 1 - - 2 * clamp( - 0, - var(${flagPropName}), - 1 - ) - ) - )`; -} +import { invert } from "./utils"; // we omit content to work around https://github.com/tailwindlabs/tailwindcss/issues/11725 (fixed in v4, but not v3) const config: Omit = { diff --git a/packages/calcite-tailwind-preset/src/utils.ts b/packages/calcite-tailwind-preset/src/utils.ts new file mode 100644 index 00000000000..9ad3f4312c8 --- /dev/null +++ b/packages/calcite-tailwind-preset/src/utils.ts @@ -0,0 +1,21 @@ +/** + * This helper inverts a value based on a boolean CSS prop flag + * + * When the flag is 0, it will not be inverted. When 1, it will invert it (multiplied by -1) + * + * @param {string} value - the CSS value to invert + * @param {string} flagPropName - the boolean CSS prop (value must be 0 or 1) + */ +export function invert(value: string, flagPropName: string): string { + return `calc( + ${value} * + calc( + 1 - + 2 * clamp( + 0, + var(${flagPropName}), + 1 + ) + ) + )`; +} diff --git a/packages/calcite-tailwind-preset/tsconfig.json b/packages/calcite-tailwind-preset/tsconfig.json index 36746ea6f70..d24c4e77e79 100755 --- a/packages/calcite-tailwind-preset/tsconfig.json +++ b/packages/calcite-tailwind-preset/tsconfig.json @@ -4,5 +4,5 @@ "outDir": "dist" }, "extends": "./tsconfig-base", - "include": ["src/index.ts", "vite.config.mts"] + "include": ["src", "vite.config.mts"] } diff --git a/packages/calcite-tailwind-preset/vite.config.mts b/packages/calcite-tailwind-preset/vite.config.mts index c77ba3fffe1..06d1822214f 100644 --- a/packages/calcite-tailwind-preset/vite.config.mts +++ b/packages/calcite-tailwind-preset/vite.config.mts @@ -5,18 +5,23 @@ import packageJson from "./package.json" with { type: "json" }; export default defineConfig({ build: { lib: { - entry: "src/index.ts", - formats: ["cjs"], - fileName: () => "index.js", + entry: { + index: "src/index.ts", + utils: "src/utils.ts" + } }, rollupOptions: { external: ["tailwindcss"], - output: { - banner: `/*! + output: [ + { + entryFileNames: "[name].js", + format: "cjs", + banner: `/*! All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://github.com/Esri/calcite-design-system/blob/${packageJson.version}/LICENSE.md for details. */`, - } + } + ] }, }, plugins: [ From abff75294f6e1c13f33698f890be2a446d3d7952 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Tue, 17 Jun 2025 11:04:54 -0700 Subject: [PATCH 7/7] fix focus util regression --- packages/calcite-components/tailwind.config.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/calcite-components/tailwind.config.ts b/packages/calcite-components/tailwind.config.ts index 60751861b15..dc752819353 100644 --- a/packages/calcite-components/tailwind.config.ts +++ b/packages/calcite-components/tailwind.config.ts @@ -24,18 +24,18 @@ const config: Config = { addUtilities({ // we override preset focus utils to allow using the internal focus color ".focus-base": { - outline: "2px solid", "outline-color": "transparent", - "outline-offset": invert("2px", "--calcite-offset-invert-focus"), }, ".focus-normal": { - "outline-color": " var(--calcite-internal-color-focus)", + outline: "2px solid var(--calcite-internal-color-focus)", }, ".focus-outset": { - "outline-color": " var(--calcite-internal-color-focus)", + outline: "2px solid var(--calcite-internal-color-focus)", + "outline-offset": invert("2px", "--calcite-offset-invert-focus"), }, ".focus-inset": { - "outline-color": " var(--calcite-internal-color-focus)", + outline: "2px solid var(--calcite-internal-color-focus)", + "outline-offset": invert("-2px", "--calcite-offset-invert-focus"), }, }); }),