├── .npmrc ├── assets └── fonts │ ├── sageui-bold.otf │ ├── sageui-bold.ttf │ ├── sageui-bold.woff │ ├── sageui-bold.woff2 │ ├── sageui-medium.otf │ ├── sageui-medium.ttf │ ├── sageui-medium.woff │ ├── sageui-medium.woff2 │ ├── sageui-regular.otf │ ├── sageui-regular.ttf │ ├── sageui-regular.woff │ ├── sageui-regular.woff2 │ └── sageui.css ├── vitest.config.ts ├── .stylelintrc.json ├── .gitignore ├── scripts ├── utils │ ├── filename.ts │ ├── filter-component.ts │ ├── unique-values.ts │ ├── file-header.ts │ └── collect.ts ├── prebuild.ts ├── transforms │ └── removeComments.ts ├── formats │ ├── commonJSExports.ts │ ├── outputJSONWithRefs.ts │ ├── outputES6WithRefs.ts │ ├── commonJSWithRefs.ts │ └── outputRefForToken.ts ├── bump-main-package-version.ts ├── style-dictionary.ts ├── postbuild.ts ├── build.ts └── icons.ts ├── .github ├── workflows │ ├── tests.yaml │ ├── build.yaml │ ├── test-release.yaml │ ├── semantic-release.yaml │ └── lint.yaml └── CODEOWNERS ├── data └── tokens │ ├── components │ ├── page.json │ ├── focus.json │ ├── badge.json │ ├── link.json │ ├── logo.json │ ├── tab.json │ ├── table.json │ ├── popover.json │ ├── nav.json │ ├── progress.json │ ├── message.json │ ├── dataviz.json │ ├── profile.json │ ├── input.json │ ├── container.json │ └── button.json │ ├── $metadata.json │ └── global │ ├── borderwidth.json │ ├── modifier.json │ ├── shadow.json │ ├── size.json │ ├── space.json │ └── radius.json ├── tsconfig.json ├── docs ├── PULL_REQUEST.md ├── ISSUE_TEMPLATE.md └── icons.md ├── .releaserc.json ├── tests ├── components-tokens.test.ts └── utils │ └── index.ts ├── CONTRIBUTING.md ├── package.json ├── CODE_OF_CONDUCT.md ├── README.md └── license /.npmrc: -------------------------------------------------------------------------------- 1 | access=public -------------------------------------------------------------------------------- /assets/fonts/sageui-bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-bold.otf -------------------------------------------------------------------------------- /assets/fonts/sageui-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-bold.ttf -------------------------------------------------------------------------------- /assets/fonts/sageui-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-bold.woff -------------------------------------------------------------------------------- /assets/fonts/sageui-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-bold.woff2 -------------------------------------------------------------------------------- /assets/fonts/sageui-medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-medium.otf -------------------------------------------------------------------------------- /assets/fonts/sageui-medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-medium.ttf -------------------------------------------------------------------------------- /assets/fonts/sageui-medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-medium.woff -------------------------------------------------------------------------------- /assets/fonts/sageui-medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-medium.woff2 -------------------------------------------------------------------------------- /assets/fonts/sageui-regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-regular.otf -------------------------------------------------------------------------------- /assets/fonts/sageui-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-regular.ttf -------------------------------------------------------------------------------- /assets/fonts/sageui-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-regular.woff -------------------------------------------------------------------------------- /assets/fonts/sageui-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sage/design-tokens/HEAD/assets/fonts/sageui-regular.woff2 -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | environment: 'node', 7 | include: ['**/*.test.ts'], 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "extends": ["stylelint-config-standard", "stylelint-config-standard-scss"], 4 | "rules": { 5 | "alpha-value-notation": null, 6 | "color-function-notation": null, 7 | "color-hex-length": null, 8 | "comment-whitespace-inside": null, 9 | "length-zero-no-unit": null, 10 | "scss/comment-no-empty": null, 11 | "scss/operator-no-unspaced": null, 12 | "value-keyword-case": null 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | temp 5 | 6 | # local env files 7 | .env 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # Yarn lock file 18 | yarn.lock 19 | 20 | # Editor directories and files 21 | .idea 22 | .vscode 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | 29 | # Stratos generated file 30 | *.stratosproject 31 | -------------------------------------------------------------------------------- /scripts/utils/filename.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | /** 6 | * Gets the filename for a file 7 | */ 8 | 9 | import { extname, basename } from 'path' 10 | 11 | export const FileName = (filePath?: string): string | undefined => { 12 | const extension = filePath ? extname(filePath) : undefined 13 | return filePath ? basename(filePath, extension) : undefined 14 | } 15 | -------------------------------------------------------------------------------- /scripts/prebuild.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | import { dirname, resolve } from 'path' 6 | import { fileURLToPath } from 'url' 7 | import fs from 'fs-extra' 8 | 9 | const __filename = fileURLToPath(import.meta.url); 10 | const __dirname = dirname(__filename); 11 | 12 | (() => { 13 | console.log('Clearing /dist folder...') 14 | fs.removeSync(resolve(__dirname, '../dist')) 15 | console.log('Done.\r\n') 16 | })() 17 | 18 | -------------------------------------------------------------------------------- /scripts/utils/filter-component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | import type { DesignToken } from 'style-dictionary/types'; 6 | 7 | export const FilterComponent = (token: DesignToken, componentName: string, isJSON: boolean): boolean => { 8 | if (token['path'].indexOf('origin') !== -1) return false; 9 | if (isJSON && componentName === 'global') { 10 | return token['path'][0] === 'global'; 11 | } 12 | return componentName ? token['path'][0] === componentName : false; 13 | } 14 | -------------------------------------------------------------------------------- /scripts/utils/unique-values.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | import { IGlyphData, IIcon } from "../icons.js" 6 | 7 | /** 8 | * Function that selectsunique values of a given fild in whole collection. 9 | */ 10 | 11 | interface IUniqueValues { 12 | array: IIcon[] | IGlyphData[] 13 | mapFn: (item: IIcon | IGlyphData) => string | undefined 14 | } 15 | 16 | export const UniqueValues = ({array, mapFn}: IUniqueValues): (string | undefined)[] => array.map(mapFn).filter((value, index, self) => self.indexOf(value) === index) 17 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | run-unit-tests: 9 | name: Run unit tests 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Check out Git repository 14 | uses: actions/checkout@v6 15 | 16 | - uses: actions/setup-node@v6 17 | with: 18 | node-version: "22" 19 | 20 | - name: Install 21 | run: npm ci --prefer-offline --ignore-scripts 22 | 23 | - name: Build 24 | run: npm run build 25 | 26 | - name: Run tests 27 | run: npm test 28 | -------------------------------------------------------------------------------- /data/tokens/components/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": { 3 | "bg-default": { 4 | "$type": "color", 5 | "$value": "{mode.color.generic.surface.nought}" 6 | }, 7 | "bg-alt": { 8 | "$type": "color", 9 | "$value": "{mode.color.generic.surface.faint}" 10 | }, 11 | "txt-alt": { 12 | "$type": "color", 13 | "$value": "{mode.color.generic.txt.moderate}", 14 | "$description": "for subheadings etc" 15 | }, 16 | "txt-default": { 17 | "$type": "color", 18 | "$value": "{mode.color.generic.txt.severe}", 19 | "$description": "for headings, paragraph txt etc " 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "include": ["**/*.ts"], 4 | "exclude": ["node_modules"], 5 | "compilerOptions": { 6 | "allowJs": true, 7 | "declaration": true, 8 | "emitDeclarationOnly": true, 9 | "esModuleInterop": true, 10 | "exactOptionalPropertyTypes": false, 11 | "noUnusedLocals": false, 12 | "noUnusedParameters": false, 13 | "allowUnreachableCode": true, 14 | "moduleResolution": "NodeNext", 15 | "module": "NodeNext", 16 | "target": "ESNext" 17 | }, 18 | "ts-node": { 19 | "experimentalSpecifierResolution": "node", 20 | "transpileOnly": true, 21 | "esm": true, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /docs/PULL_REQUEST.md: -------------------------------------------------------------------------------- 1 | # Description of Change 2 | 3 | 6 | 7 | ## Release Notes 8 | 9 | 17 | 18 | ## :memo: Type of change 19 | 20 | - [ ] Bug fix 21 | - [ ] New feature 22 | - [ ] Breaking change 23 | - [ ] This change requires a documentation update 24 | - [ ] Refactoring -------------------------------------------------------------------------------- /scripts/utils/file-header.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | /** 6 | * Gets the filename for a file 7 | */ 8 | 9 | export const HeaderContents = (isScss: Boolean): string => { 10 | const year = new Date().getFullYear() 11 | 12 | // As consumers will still need to compile the SCSS files to CSS themselves 13 | // we use a single line comment format to not add to their compiled CSS files 14 | if (isScss) { 15 | return `// Copyright © ${year} The Sage Group plc or its licensors. All Rights reserved` 16 | } 17 | 18 | return `/** 19 | * Copyright © ${year} The Sage Group plc or its licensors. All Rights reserved 20 | */` 21 | } 22 | -------------------------------------------------------------------------------- /scripts/transforms/removeComments.ts: -------------------------------------------------------------------------------- 1 | import type { DesignToken } from 'style-dictionary/types'; 2 | 3 | 4 | /** 5 | * Helper: Removes comments from output 6 | * scss/variables format automatically adds comments from the $description property 7 | */ 8 | export const removeComments = (token: DesignToken): DesignToken => { 9 | const _t = token; 10 | 11 | if (_t.comment) { 12 | delete _t.comment; 13 | } 14 | if (_t['original'] && _t['original'].comment) { 15 | delete _t['original'].comment; 16 | } 17 | if (_t['original'] && _t['original'].$description) { 18 | delete _t['original'].$description; 19 | } 20 | if (_t.$description) { 21 | delete _t.$description; 22 | } 23 | 24 | return _t; 25 | } 26 | -------------------------------------------------------------------------------- /scripts/formats/commonJSExports.ts: -------------------------------------------------------------------------------- 1 | import { Dictionary, DesignToken } from "style-dictionary/types"; 2 | 3 | /** 4 | * Custom format to output ensure commonJS variables output in the same format as ESM 5 | * This only runs on non-component files as components are handled in commonJSWithRefs 6 | * @param dictionary The style dictionary object containing all tokens 7 | * @returns The processed commonJS variables in the same format as the ES6 output 8 | */ 9 | export const formatCommonJSExports = ({dictionary}: {dictionary: Dictionary}) => { 10 | const tokens = dictionary.allTokens 11 | .map((token: DesignToken) => { 12 | return `module.exports.${token.name} = "${token.$value || token.value}";`; 13 | }) 14 | .join('\n'); 15 | 16 | return tokens + '\n'; 17 | } 18 | -------------------------------------------------------------------------------- /data/tokens/$metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "tokenSetOrder": [ 3 | "core", 4 | "global/borderwidth", 5 | "global/modifier", 6 | "global/radius", 7 | "global/shadow", 8 | "global/size", 9 | "global/space", 10 | "global/font", 11 | "mode/light", 12 | "mode/dark", 13 | "components/badge", 14 | "components/button", 15 | "components/container", 16 | "components/dataviz", 17 | "components/focus", 18 | "components/input", 19 | "components/link", 20 | "components/logo", 21 | "components/message", 22 | "components/nav", 23 | "components/page", 24 | "components/pill", 25 | "components/progress", 26 | "components/popover", 27 | "components/profile", 28 | "components/tab", 29 | "components/table" 30 | ] 31 | } -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": ["master"], 3 | "plugins": [ 4 | [ 5 | "@semantic-release/commit-analyzer", 6 | { 7 | "preset": "conventionalcommits" 8 | } 9 | ], 10 | [ 11 | "@semantic-release/release-notes-generator", 12 | { 13 | "preset": "conventionalcommits" 14 | } 15 | ], 16 | [ 17 | "@semantic-release/changelog", 18 | { 19 | "changelogFile": "docs/CHANGELOG.md" 20 | } 21 | ], 22 | [ 23 | "@semantic-release/npm", 24 | { 25 | "changelogFile": "docs/CHANGELOG.md", 26 | "pkgRoot": "dist" 27 | } 28 | ], 29 | [ 30 | "@semantic-release/github", 31 | { 32 | "assets": "dist/*.tgz" 33 | } 34 | ] 35 | ], 36 | "dryRun": false, 37 | "debug": false 38 | } 39 | -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **I'm submitting a ...** 4 | 5 | - [ ] bug report 6 | - [ ] feature request 7 | 8 | ### Subject of the issue 9 | 10 | 11 | 12 | ## Expected Behaviour 13 | 14 | 15 | 16 | ## Current Behaviour 17 | 18 | 19 | 20 | ## Possible Solution 21 | 22 | 23 | 24 | ## Steps to Reproduce 25 | 26 | 27 | 28 | 29 | ### Your environment 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | # Builds token library. 9 | build: 10 | name: Build library 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | packages: write 15 | steps: 16 | - uses: actions/checkout@v6 17 | with: 18 | fetch-depth: 0 19 | persist-credentials: false 20 | 21 | - uses: actions/setup-node@v6 22 | with: 23 | node-version: "22" 24 | cache: "npm" 25 | 26 | - run: npm ci --prefer-offline --ignore-scripts 27 | 28 | - name: Append .env 29 | run: | 30 | echo " 31 | FIGMA_ACCESS_TOKEN=${{ secrets.FIGMA_ACCESS_TOKEN }} 32 | FIGMA_FILE_ID=${{ secrets.FIGMA_FILE_ID }} 33 | " >> .env 34 | 35 | - name: Build 36 | run: npm run build 37 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence. 3 | * @Sage/carbon-design-tokens 4 | 5 | # The Carbon Design Tokens team are responsible for reviewing Pull Requests 6 | # that contain any change modifying JS, TS or JSON files. 7 | *.js @Sage/carbon-design-tokens 8 | *.ts @Sage/carbon-design-tokens 9 | *.json @Sage/carbon-design-tokens 10 | 11 | 12 | # The Design System team are responsible for reviewing Pull Requests 13 | # that contain any changes to the following files. 14 | /data/tokens/$metadata.json @Sage/ds-tokens-reviewers 15 | /data/tokens/$themes.json @Sage/ds-tokens-reviewers 16 | 17 | # Codeowners file changes must be approved by a Carbon Design Tokens 18 | # team member. 19 | .github/CODEOWNERS @Sage/carbon-design-tokens 20 | -------------------------------------------------------------------------------- /.github/workflows/test-release.yaml: -------------------------------------------------------------------------------- 1 | name: Test Semantic Release (Dry Run) 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | test-release: 10 | name: Test Release Process (Dry Run) 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v6 16 | with: 17 | fetch-depth: 0 18 | persist-credentials: false 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v6 22 | with: 23 | node-version: "22" 24 | 25 | - name: Install dependencies 26 | run: npm ci 27 | 28 | - name: Lint commits messages 29 | run: npx commitlint --from origin/master --to HEAD --verbose 30 | 31 | - name: Test semantic-release setup (dry-run) 32 | run: npx semantic-release --dry-run 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 36 | -------------------------------------------------------------------------------- /scripts/bump-main-package-version.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | import { resolve, dirname } from 'path' 5 | import { fileURLToPath } from 'url' 6 | import fs from 'fs-extra' 7 | 8 | const { readJsonSync, outputJsonSync } = fs 9 | const currentFile = fileURLToPath(import.meta.url) 10 | const currentDir = dirname(currentFile) 11 | 12 | console.log('Bumping version in main package.json file...') 13 | try { 14 | const mainPackageJsonFilePath = resolve(currentDir, '../package.json') 15 | const distPackageJsonFilePath = resolve(currentDir, '../dist/package.json') 16 | 17 | const version = readJsonSync(distPackageJsonFilePath).version 18 | const mainPackageJson = readJsonSync(mainPackageJsonFilePath) 19 | const outputPackageJson = { ...mainPackageJson, version } 20 | 21 | outputJsonSync('./package.json', outputPackageJson, { spaces: 2 }) 22 | 23 | console.log('Done.\r\n') 24 | } catch (err) { 25 | console.log('Error!\r\n') 26 | console.log(err) 27 | } 28 | -------------------------------------------------------------------------------- /data/tokens/components/focus.json: -------------------------------------------------------------------------------- 1 | { 2 | "focus": { 3 | "bg": { 4 | "$type": "color", 5 | "$value": "{mode.color.action.focus.withDefaultAlt}" 6 | }, 7 | "borderalt": { 8 | "$type": "color", 9 | "$value": "{mode.color.action.focus.default}" 10 | }, 11 | "border": { 12 | "$type": "color", 13 | "$value": "{mode.color.action.focus.withDefault}" 14 | }, 15 | "label": { 16 | "$type": "color", 17 | "$value": "{mode.color.action.focus.txt}" 18 | }, 19 | "inverse": { 20 | "bg": { 21 | "$type": "color", 22 | "$value": "{mode.color.action.focus.inverse.withDefaultAlt}" 23 | }, 24 | "borderalt": { 25 | "$type": "color", 26 | "$value": "{mode.color.action.focus.inverse.default}" 27 | }, 28 | "border": { 29 | "$type": "color", 30 | "$value": "{mode.color.action.focus.inverse.withDefault}" 31 | }, 32 | "label": { 33 | "$type": "color", 34 | "$value": "{mode.color.action.focus.inverse.txt}" 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /scripts/formats/outputJSONWithRefs.ts: -------------------------------------------------------------------------------- 1 | import { Dictionary, DesignToken } from "style-dictionary/types"; 2 | import { outputRefForToken } from "./outputRefForToken.js"; 3 | 4 | /** 5 | * Custom format to ensure JSON outputs with CSS variable references where applicable 6 | * @param dictionary The style dictionary object containing all tokens 7 | * @param options Optional parameters for the output format 8 | * @returns The processed JSON string with CSS variable references for values 9 | */ 10 | export const outputJSONWithRefs = ({dictionary, options = {}}: {dictionary: Dictionary, options?: Record}) => { 11 | const { outputReferences = true } = options; 12 | 13 | return JSON.stringify( 14 | dictionary.allTokens.reduce((acc: Record, token: DesignToken) => { 15 | const originalValue = token["original"].value || token["original"].$value; 16 | 17 | if (outputReferences && token.name) { 18 | acc[token.name] = outputRefForToken(originalValue, token); 19 | } 20 | 21 | return acc; 22 | }, {}), 23 | null, 24 | 2 25 | ); 26 | } -------------------------------------------------------------------------------- /scripts/utils/collect.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | import Figma from 'figma-js' 6 | 7 | /** 8 | * Callback function for collect util. 9 | */ 10 | 11 | /** 12 | * Recursively selects nodes of object. It preserves node if callback returns true and rejects if callback returns false. 13 | */ 14 | 15 | interface ICollect { 16 | object: Figma.Node 17 | callback: (node: Figma.Node) => boolean 18 | } 19 | 20 | export const Collect = ({object, callback}: ICollect): Figma.Node[] => { 21 | const output: Figma.Node[] = [] 22 | 23 | const walk = (walkObject: Figma.Node) => { 24 | if (callback(walkObject)) { 25 | output.push(walkObject) 26 | return 27 | } 28 | 29 | if (Array.isArray(walkObject)) { 30 | walkObject.forEach((childObj: Figma.Node) => walk(childObj)) 31 | return 32 | } 33 | 34 | if (walkObject instanceof Object) { 35 | Object.values(walkObject).forEach((childObj) => walk(childObj)) 36 | } 37 | } 38 | 39 | walk(object) 40 | return output 41 | } 42 | -------------------------------------------------------------------------------- /scripts/formats/outputES6WithRefs.ts: -------------------------------------------------------------------------------- 1 | import { Dictionary, DesignToken } from "style-dictionary/types"; 2 | import { outputRefForToken } from "./outputRefForToken.js"; 3 | 4 | /** 5 | * Custom format to ensure ES6 variables output with CSS variable references to support white-labelling 6 | * @param dictionary The style dictionary object containing all tokens 7 | * @param options Optional parameters for the output format 8 | * @returns The processed ES6 variables with CSS variable references for values 9 | */ 10 | export const outputES6WithRefs = ({dictionary, options = {}}: {dictionary: Dictionary, options?: Record}) => { 11 | const { outputReferences = true } = options; 12 | 13 | const tokens = dictionary.allTokens 14 | .map((token: DesignToken) => { 15 | const originalValue = token["original"]?.value || token["original"]?.$value; 16 | 17 | if (outputReferences && token.name) { 18 | return `export const ${token.name} = "${outputRefForToken(originalValue, token)}";`; 19 | } 20 | 21 | return ""; 22 | }) 23 | .join('\n'); 24 | 25 | return tokens + '\n'; 26 | } 27 | -------------------------------------------------------------------------------- /data/tokens/global/borderwidth.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "borderwidth": { 4 | "none": { 5 | "$type": "borderWidth", 6 | "$value": "0" 7 | }, 8 | "XS": { 9 | "$type": "borderWidth", 10 | "$value": "{core.dimension.12}", 11 | "$description": "Buttons, Inputs. Dividing lines and container borders." 12 | }, 13 | "S": { 14 | "$type": "borderWidth", 15 | "$value": "{core.dimension.25}", 16 | "$description": "Buttons. Step flow, Validation bars" 17 | }, 18 | "M": { 19 | "$type": "borderWidth", 20 | "$value": "{core.dimension.38}", 21 | "$description": "Focus " 22 | }, 23 | "L": { 24 | "$type": "borderWidth", 25 | "$value": "{core.dimension.50}", 26 | "$description": "Focus underline. Dividing lines. " 27 | }, 28 | "XL": { 29 | "$type": "borderWidth", 30 | "$value": "{core.dimension.75}", 31 | "$description": "Double Focus Border" 32 | }, 33 | "XXL": { 34 | "$type": "borderWidth", 35 | "$value": "{core.dimension.100}" 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /data/tokens/global/modifier.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "modifier": { 4 | "button": { 5 | "active": { 6 | "$type": "other", 7 | "$value": "0.30", 8 | "$description": "Actve modifier used when 15% hover is used" 9 | }, 10 | "activeAlt": { 11 | "$type": "other", 12 | "$value": "0.15", 13 | "$description": "Actve modifier used when 15% hover is used" 14 | }, 15 | "hover": { 16 | "$type": "other", 17 | "$value": "0.15", 18 | "$description": "Hover used on primary buttons" 19 | }, 20 | "hoverAlt": { 21 | "$type": "other", 22 | "$value": "0.10", 23 | "$description": "Hover used on primary buttons" 24 | } 25 | }, 26 | "input": { 27 | "disabledFg": { 28 | "$type": "other", 29 | "$value": "0.3", 30 | "$description": "Disabled alpha for buttons" 31 | }, 32 | "disabledBg": { 33 | "$type": "other", 34 | "$value": "0.05", 35 | "$description": "less contrast" 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /assets/fonts/sageui.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Sage UI"; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: local("Sage UI Regular"), local("SageUI-Regular"); 6 | src: url("./sageui-regular.woff2") format("woff2"), 7 | url("./sageui-regular.woff") format("woff"), 8 | url("./sageui-regular.ttf") format("truetype"), 9 | url("./sageui-regular.otf") format("opentype"); 10 | } 11 | 12 | @font-face { 13 | font-family: "Sage UI"; 14 | font-style: normal; 15 | font-weight: 500; 16 | src: local("Sage UI Medium"), local("SageUI-Medium"); 17 | src: url("./sageui-medium.woff2") format("woff2"), 18 | url("./sageui-medium.woff") format("woff"), 19 | url("./sageui-medium.ttf") format("truetype"), 20 | url("./sageui-medium.otf") format("opentype"); 21 | } 22 | 23 | @font-face { 24 | font-family: "Sage UI"; 25 | font-style: normal; 26 | font-weight: 700; 27 | src: local("Sage UI Bold"), local("SageUI-Bold"); 28 | src: url("./sageui-bold.woff2") format("woff2"), 29 | url("./sageui-bold.woff") format("woff"), 30 | url("./sageui-bold.ttf") format("truetype"), 31 | url("./sageui-bold.otf") format("opentype"); 32 | } 33 | -------------------------------------------------------------------------------- /scripts/formats/commonJSWithRefs.ts: -------------------------------------------------------------------------------- 1 | import { Dictionary, DesignToken } from "style-dictionary/types"; 2 | import { outputRefForToken } from "./outputRefForToken.js"; 3 | 4 | /** 5 | * Custom format to output ensure commonJS variables output in the same format as ESM with CSS variable references to support white-labelling 6 | * @param dictionary The style dictionary object containing all tokens 7 | * @param options Optional parameters for the output format 8 | * @returns The processed commonJS variables with CSS variable references for values 9 | */ 10 | export const outputCommonJSWithRefs = ({dictionary, options = {}}: {dictionary: Dictionary, options?: Record}) => { 11 | const { outputReferences = true } = options; 12 | 13 | const tokens = dictionary.allTokens 14 | .map((token: DesignToken) => { 15 | const originalValue = token["original"]?.value || token["original"]?.$value; 16 | 17 | if (outputReferences && token.name) { 18 | return `module.exports.${token.name} = "${outputRefForToken(originalValue, token)}";`; 19 | } 20 | 21 | return ""; 22 | }) 23 | .join('\n'); 24 | 25 | return tokens + '\n'; 26 | } 27 | -------------------------------------------------------------------------------- /data/tokens/global/shadow.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "depth": { 4 | "none": { 5 | "$type": "boxShadow", 6 | "$value": "{core.depth.none}" 7 | }, 8 | "lvl1": { 9 | "$type": "boxShadow", 10 | "$value": "{core.depth.down-right.1}", 11 | "$description": "Card default state and popover menus used on split, multiaction, dropdown and action popovers" 12 | }, 13 | "lvl2": { 14 | "$type": "boxShadow", 15 | "$value": "{core.depth.down-right.2}", 16 | "$description": "Card hover state" 17 | }, 18 | "lvl3": { 19 | "$type": "boxShadow", 20 | "$value": "{core.depth.down-right.3}", 21 | "$description": "Dialog, Menu, Sidebar, Card drag state" 22 | }, 23 | "stickyB": { 24 | "$type": "boxShadow", 25 | "$value": "{core.depth.up-right.1}", 26 | "$description": "Sticky footer on dialogs, drawer and sidebar" 27 | }, 28 | "stickyL": { 29 | "$type": "boxShadow", 30 | "$value": "{core.depth.left.0}", 31 | "$description": "Right sticky column in Table." 32 | }, 33 | "stickyR": { 34 | "$type": "boxShadow", 35 | "$value": "{core.depth.right.0}", 36 | "$description": "Left sticky column in Table." 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /data/tokens/global/size.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "size": { 4 | "none": { 5 | "$type": "sizing", 6 | "$value": "{core.dimension.0}" 7 | }, 8 | "6XS": { 9 | "$type": "sizing", 10 | "$value": "{core.dimension.25}" 11 | }, 12 | "5XS": { 13 | "$type": "sizing", 14 | "$value": "{core.dimension.50}" 15 | }, 16 | "4XS": { 17 | "$type": "sizing", 18 | "$value": "{core.dimension.100}" 19 | }, 20 | "3XS": { 21 | "$type": "sizing", 22 | "$value": "{core.dimension.200}" 23 | }, 24 | "2XS": { 25 | "$type": "sizing", 26 | "$value": "{core.dimension.250}" 27 | }, 28 | "XS": { 29 | "$type": "sizing", 30 | "$value": "{core.dimension.300}" 31 | }, 32 | "S": { 33 | "$type": "sizing", 34 | "$value": "{core.dimension.400}" 35 | }, 36 | "M": { 37 | "$type": "sizing", 38 | "$value": "{core.dimension.500}" 39 | }, 40 | "L": { 41 | "$type": "sizing", 42 | "$value": "{core.dimension.600}" 43 | }, 44 | "XL": { 45 | "$type": "sizing", 46 | "$value": "{core.dimension.700}" 47 | }, 48 | "2XL": { 49 | "$type": "sizing", 50 | "$value": "{core.dimension.800}" 51 | }, 52 | "3XL": { 53 | "$type": "sizing", 54 | "$value": "{core.dimension.900}" 55 | }, 56 | "4XL": { 57 | "$type": "sizing", 58 | "$value": "{core.dimension.1000}" 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /data/tokens/components/badge.json: -------------------------------------------------------------------------------- 1 | { 2 | "badge": { 3 | "none": { 4 | "$type": "color", 5 | "$value": "{mode.color.none}", 6 | "$description": "transparent override used for hiding colors when needed." 7 | }, 8 | "bg-default": { 9 | "$type": "color", 10 | "$value": "{mode.color.status.negative.default}", 11 | "$description": "Used in global nav notification badges" 12 | }, 13 | "border-default": { 14 | "$type": "color", 15 | "$value": "{mode.color.generic.bg.nought}" 16 | }, 17 | "bg-alt": { 18 | "$type": "color", 19 | "$value": "{mode.color.status.info.default}" 20 | }, 21 | "label-default": { 22 | "$type": "color", 23 | "$value": "{mode.color.generic.txt.inverse.extreme}" 24 | }, 25 | "label-alt": { 26 | "$type": "color", 27 | "$value": "{mode.color.generic.txt.inverse.extreme}" 28 | }, 29 | "inverse": { 30 | "bg-default": { 31 | "$type": "color", 32 | "$value": "{mode.color.status.negative.inverse.default}", 33 | "$description": "Used in global nav notification badges" 34 | }, 35 | "border-default": { 36 | "$type": "color", 37 | "$value": "{mode.color.generic.bg.inverse.nought}" 38 | }, 39 | "bg-alt": { 40 | "$type": "color", 41 | "$value": "{mode.color.status.info.inverse.default}" 42 | }, 43 | "label-default": { 44 | "$type": "color", 45 | "$value": "{mode.color.generic.txt.extreme}" 46 | }, 47 | "label-alt": { 48 | "$type": "color", 49 | "$value": "{mode.color.generic.txt.extreme}" 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /.github/workflows/semantic-release.yaml: -------------------------------------------------------------------------------- 1 | name: Semantic Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | # Builds token library from current data/tokens.json file and releases it. 10 | release: 11 | name: Build and release library 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | packages: write 16 | steps: 17 | - uses: actions/checkout@v6 18 | with: 19 | fetch-depth: 0 20 | persist-credentials: false 21 | 22 | - uses: actions/setup-node@v6 23 | with: 24 | node-version: "22" 25 | cache: "npm" 26 | 27 | - run: npm ci --prefer-offline --ignore-scripts 28 | 29 | - name: Append .env 30 | run: | 31 | echo " 32 | FIGMA_ACCESS_TOKEN=${{ secrets.FIGMA_ACCESS_TOKEN }} 33 | FIGMA_FILE_ID=${{ secrets.FIGMA_FILE_ID }} 34 | " >> .env 35 | 36 | - name: Build 37 | run: npm run build 38 | 39 | - name: Import GPG key 40 | uses: crazy-max/ghaction-import-gpg@v6 41 | with: 42 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} 43 | passphrase: ${{ secrets.GPG_PASSPHRASE }} 44 | git_user_signingkey: true 45 | git_commit_gpgsign: true 46 | 47 | - name: Release package to NPM registry 48 | run: npx semantic-release --debug 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.ADMIN_ACCESS_TOKEN }} 51 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 52 | GIT_COMMITTER_NAME: Sage Carbon 53 | GIT_COMMITTER_EMAIL: ${{ secrets.GIT_COMMITTER_EMAIL }} 54 | -------------------------------------------------------------------------------- /tests/components-tokens.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | describe, 3 | it, 4 | expect, 5 | } from "vitest"; 6 | import { resolve } from "path"; 7 | import { 8 | parseCSSFile, 9 | parseJSONFile, 10 | parseCommonJSFile, 11 | parseES6File, 12 | kebabToCamel 13 | } from "./utils/index.js"; 14 | 15 | import { cwd } from "process"; 16 | import { readdirSync } from "fs"; 17 | 18 | const distPath = resolve(cwd(), "dist"); 19 | 20 | // fetch all component names from css/components directory 21 | const componentsDir = resolve(distPath, "css/components"); 22 | const components = readdirSync(componentsDir) 23 | .filter(file => file.endsWith(".css")) 24 | .map(file => file.replace(".css", "")); 25 | 26 | components.forEach(component => { 27 | describe(`The ${component} component`, () => { 28 | it("should have consistent output across all formats", () => { 29 | const cssTokens = parseCSSFile(resolve(distPath, `css/components/${component}.css`)); 30 | const jsonTokens = parseJSONFile(resolve(distPath, `json/components/${component}.json`)); 31 | const jsTokens = parseCommonJSFile(resolve(distPath, `js/common/components/${component}.js`)); 32 | const es6Tokens = parseES6File(resolve(distPath, `js/es6/components/${component}.js`)); 33 | 34 | // All formats should have the same number of tokens 35 | expect(cssTokens.size).toBe(jsonTokens.size); 36 | expect(cssTokens.size).toBe(jsTokens.size); 37 | expect(cssTokens.size).toBe(es6Tokens.size); 38 | 39 | // Values should match across formats 40 | cssTokens.forEach((cssValue, cssKey) => { 41 | const camelKey = kebabToCamel(cssKey); 42 | expect(jsonTokens.get(camelKey)).toBe(cssValue); 43 | expect(jsTokens.get(camelKey)).toBe(cssValue); 44 | expect(es6Tokens.get(camelKey)).toBe(cssValue); 45 | }); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | run-linters: 9 | name: Run linters 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Check out Git repository 14 | uses: actions/checkout@v6 15 | 16 | - uses: actions/setup-node@v6 17 | with: 18 | node-version: "22" 19 | cache: "npm" 20 | 21 | - name: Install 22 | run: npm ci --prefer-offline --ignore-scripts 23 | 24 | - name: Build project 25 | run: npm run build 26 | 27 | - name: Run stylelint 28 | run: npm run stylelint:dist 29 | 30 | - name: Download W3C Validator (vnu.jar) 31 | run: wget https://github.com/validator/validator/releases/download/latest/vnu.jar 32 | 33 | - name: Validate CSS 34 | run: | 35 | # Find all CSS files in dist directory and run validation 36 | find ./dist -name "*.css" -print0 | xargs -0 java -jar vnu.jar --css 37 | 38 | - name: Validate JSON 39 | run: | 40 | # Find all JSON files in dist directory and run validation 41 | npx jsonlint ./dist/**/*.json --no-duplicate-keys 42 | 43 | - name: Check for [object Object] in SCSS 44 | run: | 45 | if grep -r '\[object Object\]' dist/scss; then 46 | echo "Error: Found [object Object] in SCSS files." 47 | exit 1 48 | fi 49 | 50 | - name: Compile SCSS 51 | run: | 52 | ( 53 | echo "@import 'global.scss';" 54 | echo "@import 'dark.scss';" 55 | echo "@import 'light.scss';" 56 | for f in dist/scss/components/*.scss; do 57 | fname=$(basename "$f") 58 | echo "@import 'components/$fname';" 59 | done 60 | ) > dist/scss/index.scss 61 | npx sass dist/scss/index.scss dist/scss/index.css 62 | -------------------------------------------------------------------------------- /data/tokens/components/link.json: -------------------------------------------------------------------------------- 1 | { 2 | "link": { 3 | "destructive": { 4 | "label-default": { 5 | "$type": "color", 6 | "$value": "{mode.color.action.danger.defaultAlt}", 7 | "$description": "." 8 | }, 9 | "label-hover": { 10 | "$type": "color", 11 | "$value": "{mode.color.action.danger.hoverAlt2}" 12 | }, 13 | "inverse": { 14 | "label-default": { 15 | "$type": "color", 16 | "$value": "{mode.color.action.danger.inverse.defaultAlt}", 17 | "$description": "." 18 | }, 19 | "label-hover": { 20 | "$type": "color", 21 | "$value": "{mode.color.action.danger.inverse.hoverAlt2}" 22 | } 23 | } 24 | }, 25 | "typical": { 26 | "label-default": { 27 | "$type": "color", 28 | "$value": "{mode.color.action.main.defaultAlt2}" 29 | }, 30 | "label-hover": { 31 | "$type": "color", 32 | "$value": "{mode.color.action.main.hoverAlt2}" 33 | }, 34 | "inverse": { 35 | "label-default": { 36 | "$type": "color", 37 | "$value": "{mode.color.action.main.inverse.defaultAlt2}" 38 | }, 39 | "label-hover": { 40 | "$type": "color", 41 | "$value": "{mode.color.action.main.inverse.hoverAlt2}" 42 | } 43 | } 44 | }, 45 | "subtle": { 46 | "label-default": { 47 | "$type": "color", 48 | "$value": "{mode.color.action.grayscale.default}" 49 | }, 50 | "label-hover": { 51 | "$type": "color", 52 | "$value": "{mode.color.action.grayscale.active}" 53 | }, 54 | "inverse": { 55 | "label-default": { 56 | "$type": "color", 57 | "$value": "{mode.color.action.grayscale.inverse.default}" 58 | }, 59 | "label-hover": { 60 | "$type": "color", 61 | "$value": "{mode.color.action.grayscale.inverse.active}" 62 | } 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /data/tokens/components/logo.json: -------------------------------------------------------------------------------- 1 | { 2 | "logo": { 3 | "sage": { 4 | "bg-default": { 5 | "$type": "color", 6 | "$value": "{mode.color.brand.default}", 7 | "$description": "sage logo" 8 | }, 9 | "bg-alt": { 10 | "$type": "color", 11 | "$value": "{mode.color.brand.defaultAlt}" 12 | }, 13 | "inverse": { 14 | "bg-default": { 15 | "$type": "color", 16 | "$value": "{mode.color.brand.inverse.default}", 17 | "$description": "sage logo" 18 | }, 19 | "bg-alt": { 20 | "$type": "color", 21 | "$value": "{mode.color.brand.inverse.defaultAlt}" 22 | } 23 | } 24 | }, 25 | "copilot": { 26 | "noBg": { 27 | "fg-default": { 28 | "$type": "color", 29 | "$value": "{mode.color.brand.copilot.monochrome}" 30 | } 31 | } 32 | }, 33 | "AiSparkle": { 34 | "bg-dot": { 35 | "$type": "color", 36 | "$value": "{mode.color.brand.copilot.withDefault}" 37 | }, 38 | "bg-star": { 39 | "$type": "color", 40 | "$value": "{mode.color.brand.copilot.default}", 41 | "$description": "star bg color" 42 | }, 43 | "outline": { 44 | "$type": "color", 45 | "$value": "{mode.color.brand.copilot.default}", 46 | "$description": "star outline variant" 47 | }, 48 | "inverse": { 49 | "bg-dot": { 50 | "$type": "color", 51 | "$value": "{mode.color.brand.copilot.inverse.withDefault}", 52 | "$description": "star outline variant" 53 | }, 54 | "bg-star": { 55 | "$type": "color", 56 | "$value": "{mode.color.brand.copilot.inverse.default}", 57 | "$description": "star outline variant" 58 | }, 59 | "outline": { 60 | "$type": "color", 61 | "$value": "{mode.color.brand.copilot.inverse.default}", 62 | "$description": "star outline variant" 63 | } 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Welcome to our contribution guidelines 4 | 5 | We're happy you want to contribute to the project, this guide will aim to give you the necessary information to do so adhering to the process and making the task as simple as possible. 6 | 7 | ## Please no editing the data files directly 8 | 9 | Please note that while we welcome any suggestions for making a change to the design tokens themselves, we will not be accepting any pull requests that alter the contents of the data folder. The contents of data is automatically generated from the design files that act as the source of truth for this library. 10 | 11 | Please open an issue if you would like to suggest a change to these values so that we can review it and pass it on to the design system team. 12 | 13 | ## Development 14 | 15 | Instructions for developing with this package locally 16 | 17 | ### Prerequisites 18 | 19 | You will need to have node and npm installed on your system. 20 | 21 | It is recommended that you also install these npm libraries globally: 22 | ```bash 23 | npm install --global style-dictionary less sass 24 | ``` 25 | 26 | ### Build 27 | 28 | To build this package, run this command: 29 | 30 | ```bash 31 | npm run build 32 | ``` 33 | 34 | To run the individual builds, such as the web bundle, run this command: 35 | ```bash 36 | npm run build:web 37 | ``` 38 | 39 | ## Publishing 40 | 41 | This library is published via github actions. After approval has been given an admin can trigger a publish process 42 | 43 | ## Contributions from outside of Sage 44 | 45 | If you're from outside of Sage then you are welcome to contribute to this package. Please make pull requests using the [Fork & Pull Request Workflow](https://gist.github.com/Chaser324/ce0505fbed06b947d962). 46 | 47 | Please be aware that Sage Group Plc. retains the intellectual property rights of this library. If Sage Group decides to change the licence of this library, or make any change that would otherwise infringe on contributors rights, we will make efforts to contact all contributors to inform them of this change three weeks prior to it coming into effect. -------------------------------------------------------------------------------- /data/tokens/global/space.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "space": { 4 | "none": { 5 | "$type": "spacing", 6 | "$value": "{core.dimension.0}" 7 | }, 8 | "layout": { 9 | "3XS": { 10 | "$type": "spacing", 11 | "$value": "{core.dimension.100}" 12 | }, 13 | "2XS": { 14 | "$type": "spacing", 15 | "$value": "{core.dimension.200}" 16 | }, 17 | "XS": { 18 | "$type": "spacing", 19 | "$value": "{core.dimension.300}" 20 | }, 21 | "S": { 22 | "$type": "spacing", 23 | "$value": "{core.dimension.400}" 24 | }, 25 | "M": { 26 | "$type": "spacing", 27 | "$value": "{core.dimension.500}" 28 | }, 29 | "L": { 30 | "$type": "spacing", 31 | "$value": "{core.dimension.600}" 32 | }, 33 | "XL": { 34 | "$type": "spacing", 35 | "$value": "{core.dimension.700}" 36 | }, 37 | "2XL": { 38 | "$type": "spacing", 39 | "$value": "{core.dimension.800}" 40 | }, 41 | "3XL": { 42 | "$type": "spacing", 43 | "$value": "{core.dimension.900}" 44 | }, 45 | "4XL": { 46 | "$type": "spacing", 47 | "$value": "{core.dimension.1000}" 48 | } 49 | }, 50 | "comp": { 51 | "2XS": { 52 | "$type": "spacing", 53 | "$value": "{core.dimension.25}" 54 | }, 55 | "XS": { 56 | "$type": "spacing", 57 | "$value": "{core.dimension.50}" 58 | }, 59 | "S": { 60 | "$type": "spacing", 61 | "$value": "{core.dimension.100}" 62 | }, 63 | "M": { 64 | "$type": "spacing", 65 | "$value": "{core.dimension.150}", 66 | "$description": "Padding inside Medium Inputs" 67 | }, 68 | "L": { 69 | "$type": "spacing", 70 | "$value": "{core.dimension.200}" 71 | }, 72 | "XL": { 73 | "$type": "spacing", 74 | "$value": "{core.dimension.300}" 75 | }, 76 | "2XL": { 77 | "$type": "spacing", 78 | "$value": "{core.dimension.400}" 79 | } 80 | } 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /tests/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | // Helper to parse CSS custom properties 4 | export function parseCSSFile(filePath: string): Map { 5 | const content = readFileSync(filePath, 'utf-8'); 6 | const tokens = new Map(); 7 | 8 | // Match CSS custom properties: --token-name: value; 9 | const regex = /--([^:]+):\s*([^;]+);/g; 10 | let match; 11 | 12 | while ((match = regex.exec(content)) !== null) { 13 | if (match[1] && match[2]) tokens.set(match[1].trim(), match[2].trim()); 14 | } 15 | 16 | return tokens; 17 | } 18 | 19 | // Helper to parse JSON file 20 | export function parseJSONFile(filePath: string): Map { 21 | const content = readFileSync(filePath, 'utf-8'); 22 | const data = JSON.parse(content); 23 | const tokens = new Map(); 24 | 25 | Object.entries(data).forEach(([key, value]) => { 26 | tokens.set(key, String(value)); 27 | }); 28 | 29 | return tokens; 30 | } 31 | 32 | // Helper to parse CommonJS file 33 | export function parseCommonJSFile(filePath: string): Map { 34 | const content = readFileSync(filePath, 'utf-8'); 35 | const tokens = new Map(); 36 | 37 | // Match: module.exports.tokenName = "value"; 38 | const regex = /module\.exports\.(\w+)\s*=\s*"([^"]+)";/g; 39 | let match; 40 | 41 | while ((match = regex.exec(content)) !== null) { 42 | if (match[1] && match[2]) tokens.set(match[1], match[2]); 43 | } 44 | 45 | return tokens; 46 | } 47 | 48 | // Helper to parse ES6 file 49 | export function parseES6File(filePath: string): Map { 50 | const content = readFileSync(filePath, 'utf-8'); 51 | const tokens = new Map(); 52 | 53 | // Match: export const tokenName = "value"; 54 | const regex = /export const (\w+)\s*=\s*"([^"]+)";/g; 55 | let match; 56 | 57 | while ((match = regex.exec(content)) !== null) { 58 | if (match[1] && match[2]) tokens.set(match[1], match[2]); 59 | } 60 | 61 | return tokens; 62 | } 63 | 64 | // Helper to convert kebab-case to camelCase 65 | export function kebabToCamel(str: string): string { 66 | return str 67 | .split('-') 68 | .map((part, index) => { 69 | // First part stays lowercase 70 | if (!index) return part; 71 | 72 | // If part starts with a number, keep it as-is 73 | if (/^\d/.test(part)) return part; 74 | 75 | // Otherwise capitalize first letter 76 | return part.charAt(0).toUpperCase() + part.slice(1); 77 | }) 78 | .join(''); 79 | } 80 | -------------------------------------------------------------------------------- /data/tokens/components/tab.json: -------------------------------------------------------------------------------- 1 | { 2 | "tab": { 3 | "bg-active": { 4 | "$type": "color", 5 | "$value": "{mode.color.action.grayscale.withActive}" 6 | }, 7 | "bg-default": { 8 | "$type": "color", 9 | "$value": "{mode.color.none}" 10 | }, 11 | "bg-hover": { 12 | "$type": "color", 13 | "$value": "{mode.color.action.grayscale.hoverAlt}", 14 | "$description": "For anchor nav, not tab." 15 | }, 16 | "border-activeAlt": { 17 | "$type": "color", 18 | "$value": "{mode.color.generic.fg.moderate}" 19 | }, 20 | "border-active": { 21 | "$type": "color", 22 | "$value": "{mode.color.action.grayscale.active}" 23 | }, 24 | "border-default": { 25 | "$type": "color", 26 | "$value": "{mode.color.generic.fg.moderate}" 27 | }, 28 | "border-hover": { 29 | "$type": "color", 30 | "$value": "{mode.color.generic.fg.firm}" 31 | }, 32 | "icon-default": { 33 | "$type": "color", 34 | "$value": "{mode.color.action.grayscale.defaultAlt}" 35 | }, 36 | "icon-hover": { 37 | "$type": "color", 38 | "$value": "{mode.color.action.grayscale.withHover}" 39 | }, 40 | "label-active": { 41 | "$type": "color", 42 | "$value": "{mode.color.action.grayscale.active}" 43 | }, 44 | "icon-active": { 45 | "$type": "color", 46 | "$value": "{mode.color.action.grayscale.active}" 47 | }, 48 | "label-default": { 49 | "$type": "color", 50 | "$value": "{mode.color.action.grayscale.default}" 51 | }, 52 | "label-hover": { 53 | "$type": "color", 54 | "$value": "{mode.color.action.grayscale.withHover}" 55 | }, 56 | "nav": { 57 | "bg-default": { 58 | "$type": "color", 59 | "$value": "{mode.color.generic.bg.nought}", 60 | "$description": "For previous/next buttons on responsive tabs" 61 | } 62 | }, 63 | "validation": { 64 | "border-warning": { 65 | "$type": "color", 66 | "$value": "{mode.color.status.warning.default}" 67 | }, 68 | "border-error": { 69 | "$type": "color", 70 | "$value": "{mode.color.status.negative.default}" 71 | }, 72 | "icon-error": { 73 | "$type": "color", 74 | "$value": "{mode.color.status.negative.default}" 75 | }, 76 | "icon-warning": { 77 | "$type": "color", 78 | "$value": "{mode.color.status.warning.default}" 79 | }, 80 | "label-error": { 81 | "$type": "color", 82 | "$value": "{mode.color.status.negative.default}" 83 | }, 84 | "label-warning": { 85 | "$type": "color", 86 | "$value": "{mode.color.status.warning.txt}" 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /data/tokens/components/table.json: -------------------------------------------------------------------------------- 1 | { 2 | "table": { 3 | "header": { 4 | "subtle": { 5 | "bg-alt": { 6 | "$type": "color", 7 | "$value": "{mode.color.generic.bg.faint}", 8 | "$description": "header alt" 9 | }, 10 | "bg-default": { 11 | "$type": "color", 12 | "$value": "{mode.color.none}" 13 | }, 14 | "border-default": { 15 | "$type": "color", 16 | "$value": "{mode.color.generic.fg.soft}", 17 | "$description": "Header borders" 18 | }, 19 | "label-default": { 20 | "$type": "color", 21 | "$value": "{mode.color.generic.txt.severe}" 22 | } 23 | }, 24 | "harsh": { 25 | "bg-alt": { 26 | "$type": "color", 27 | "$value": "{mode.color.action.grayscale.default}", 28 | "$description": "header alt" 29 | }, 30 | "bg-default": { 31 | "$type": "color", 32 | "$value": "{mode.color.action.grayscale.defaultAlt}" 33 | }, 34 | "border-default": { 35 | "$type": "color", 36 | "$value": "{mode.color.generic.fg.soft}" 37 | }, 38 | "label-default": { 39 | "$type": "color", 40 | "$value": "{mode.color.generic.txt.inverse.severe}" 41 | } 42 | } 43 | }, 44 | "row": { 45 | "bg-default": { 46 | "$type": "color", 47 | "$value": "{mode.color.generic.bg.nought}" 48 | }, 49 | "bg-alt": { 50 | "$type": "color", 51 | "$value": "{mode.color.generic.bg.delicate}", 52 | "$description": "Zebra stripes" 53 | }, 54 | "bg-alt2": { 55 | "$type": "color", 56 | "$value": "{mode.color.generic.bg.faint}", 57 | "$description": "Child rows" 58 | }, 59 | "bg-alt3": { 60 | "$type": "color", 61 | "$value": "{mode.color.generic.bg.soft}", 62 | "$description": "deprecation candidate" 63 | }, 64 | "bg-selected": { 65 | "$type": "color", 66 | "$value": "{mode.color.generic.bg.soft}" 67 | }, 68 | "border-default": { 69 | "$type": "color", 70 | "$value": "{mode.color.generic.fg.soft}" 71 | }, 72 | "label-default": { 73 | "$type": "color", 74 | "$value": "{mode.color.generic.txt.severe}" 75 | }, 76 | "label-selected": { 77 | "$type": "color", 78 | "$value": "{mode.color.generic.txt.extreme}" 79 | } 80 | }, 81 | "footer": { 82 | "bg-default": { 83 | "$type": "color", 84 | "$value": "{mode.color.generic.bg.soft}" 85 | }, 86 | "border-default": { 87 | "$type": "color", 88 | "$value": "{mode.color.generic.fg.soft}", 89 | "$description": "Header borders" 90 | }, 91 | "label-default": { 92 | "$type": "color", 93 | "$value": "{mode.color.generic.txt.severe}" 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /scripts/formats/outputRefForToken.ts: -------------------------------------------------------------------------------- 1 | import { DesignToken } from "style-dictionary/types"; 2 | import { usesReferences } from 'style-dictionary/utils'; 3 | 4 | /** 5 | * Helper: Converts a reference path to proper kebab-case for CSS variable names 6 | * @param refPath The reference path (e.g., "mode.color.action.mainWithDefault") 7 | * @returns The kebab-case CSS variable name 8 | */ 9 | const convertToKebabCase = (refPath: string): string => { 10 | return refPath 11 | .split('.') 12 | .map(segment => { 13 | // Convert camelCase segments to kebab-case 14 | return segment 15 | .replace(/([a-z\d])([A-Z])/g, '$1-$2') // Handle lowercase/digit + uppercase 16 | .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2') // Handle consecutive capitals 17 | .toLowerCase(); 18 | }) 19 | .join('-'); 20 | }; 21 | 22 | /** 23 | * Helper: Check if a value contains mathematical operations 24 | */ 25 | const containsOperation = (value: string): boolean => { 26 | // Don't wrap if it's already wrapped in calc() 27 | if (value.trim().startsWith('calc(')) { 28 | return false; 29 | } 30 | 31 | // Only wrap if there are operators with spaces around them or at the end 32 | // This catches: "/ 2", " * 1.5", " + 10px", " - 5px" 33 | return /\s[\+\-\*\/]\s|\s[\+\-\*\/]$|[\+\-\*\/]\s/.test(value); 34 | }; 35 | 36 | /** 37 | * Helper: Outputs a token value, replacing references with CSS variable references 38 | * @param originalValue The original value of the token, which may include references 39 | * @param token The design token object 40 | * @returns The processed value with CSS variable references where applicable 41 | */ 42 | export const outputRefForToken = (originalValue: string, token: DesignToken): string => { 43 | if (usesReferences(originalValue)) { 44 | if (originalValue.startsWith("linear-gradient(")) { 45 | const linearCSSValue = originalValue.replace( 46 | /\{([^}]+)\}/g, 47 | (_, refPath) => `var(--${convertToKebabCase(refPath)})` 48 | ); 49 | 50 | return linearCSSValue; 51 | } else { 52 | // need to ensure camelCase references are converted to kebab-case for CSS variable usage 53 | // also needed to ensure other complex formats are handled properly 54 | // e.g. withDefault becomes with-default 55 | // e.g. {container.size.fluid.items.m} / 2 becomes var(--container-size-fluid-items-m) / 2 56 | const processedValue = originalValue.replace( 57 | /\{([^}]+)\}/g, 58 | (_, refPath) => `var(--${convertToKebabCase(refPath)})` 59 | ); 60 | 61 | // Check if the processed value contains operations and wrap in calc() 62 | // e.g. {container.size.fluid.items.m} / 2 becomes calc(var(--container-size-fluid-items-m) / 2) 63 | if (containsOperation(processedValue)) { 64 | return `calc(${processedValue})`; 65 | } 66 | 67 | return processedValue; 68 | } 69 | } else { 70 | return String(token.$value || token.value); 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /scripts/style-dictionary.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | import StyleDictionary from "style-dictionary" 6 | import { register } from "@tokens-studio/sd-transforms" 7 | import { removeComments } from "./transforms/removeComments.js"; 8 | import { outputJSONWithRefs } from "./formats/outputJSONWithRefs.js"; 9 | import { outputES6WithRefs } from "./formats/outputES6WithRefs.js"; 10 | import { outputCommonJSWithRefs } from "./formats/commonJSWithRefs.js"; 11 | import { formatCommonJSExports } from "./formats/commonJSExports.js"; 12 | 13 | StyleDictionary.registerFormat({ 14 | name: "custom/json-with-refs", 15 | format: outputJSONWithRefs 16 | }); 17 | 18 | StyleDictionary.registerFormat({ 19 | name: "custom/es6-with-refs", 20 | format: outputES6WithRefs 21 | }); 22 | 23 | StyleDictionary.registerFormat({ 24 | name: "custom/commonjs-with-refs", 25 | format: outputCommonJSWithRefs 26 | }); 27 | 28 | StyleDictionary.registerFormat({ 29 | name: "custom/commonjs-exports", 30 | format: formatCommonJSExports 31 | }); 32 | 33 | StyleDictionary.registerTransform({ 34 | name: "custom/remove-comments", 35 | type: "attribute", 36 | filter: () => true, 37 | transform: removeComments 38 | }); 39 | 40 | const groups = { 41 | css: [ 42 | "custom/remove-comments", 43 | "border/css/shorthand", 44 | "shadow/css/shorthand", 45 | "transition/css/shorthand", 46 | "typography/css/shorthand", 47 | "name/kebab", 48 | "ts/size/px", 49 | "ts/opacity", 50 | "ts/size/lineheight", 51 | "ts/typography/fontWeight", 52 | "ts/resolveMath", 53 | "ts/size/css/letterspacing", 54 | "ts/color/modifiers" 55 | ], 56 | scss: [ 57 | "custom/remove-comments", 58 | "border/css/shorthand", 59 | "shadow/css/shorthand", 60 | "transition/css/shorthand", 61 | "typography/css/shorthand", 62 | "name/kebab", 63 | "ts/size/px", 64 | "ts/opacity", 65 | "ts/size/lineheight", 66 | "ts/typography/fontWeight", 67 | "ts/resolveMath", 68 | "ts/size/css/letterspacing", 69 | "ts/color/modifiers" 70 | ], 71 | js: [ 72 | "name/camel", 73 | "ts/size/px", 74 | "ts/opacity", 75 | "ts/size/lineheight", 76 | "ts/typography/fontWeight", 77 | "ts/resolveMath", 78 | "ts/size/css/letterspacing", 79 | "ts/color/modifiers" 80 | ], 81 | json: [ 82 | "name/camel", 83 | "ts/size/px", 84 | "ts/opacity", 85 | "ts/size/lineheight", 86 | "ts/typography/fontWeight", 87 | "ts/resolveMath", 88 | "ts/size/css/letterspacing", 89 | "ts/color/modifiers", 90 | ] 91 | } 92 | 93 | register(StyleDictionary, { 94 | "ts/color/modifiers": { 95 | format: "hex" 96 | } 97 | }) 98 | 99 | export { 100 | StyleDictionary, 101 | groups 102 | } 103 | -------------------------------------------------------------------------------- /data/tokens/components/popover.json: -------------------------------------------------------------------------------- 1 | { 2 | "popover": { 3 | "size": { 4 | "menu": { 5 | "minwidth": { 6 | "S": { 7 | "$type": "sizing", 8 | "$value": "{container.size.fluidItems.M} / 2", 9 | "$description": "minwidth of small size popover menu container" 10 | }, 11 | "M": { 12 | "$type": "sizing", 13 | "$value": "{container.size.fluidItems.XS}", 14 | "$description": "minwidth of medium size popover menu container" 15 | }, 16 | "L": { 17 | "$type": "sizing", 18 | "$value": "{container.size.fluidItems.S}", 19 | "$description": "minwidth of large size popover menu container" 20 | } 21 | }, 22 | "maxwidth": { 23 | "S": { 24 | "$type": "sizing", 25 | "$value": "{container.size.fluidItems.M}", 26 | "$description": "maxwidth of small size popover menu container" 27 | }, 28 | "M": { 29 | "$type": "sizing", 30 | "$value": "{container.size.fluidItems.L}", 31 | "$description": "maxwidth of medium size popover menu container" 32 | }, 33 | "L": { 34 | "$type": "sizing", 35 | "$value": "{container.size.fluidItems.XL}", 36 | "$description": "maxwidth of large size popover menu container" 37 | } 38 | } 39 | } 40 | }, 41 | "bg-active": { 42 | "$type": "color", 43 | "$value": "{mode.color.action.grayscale.active}", 44 | "$description": "previously action minor 850" 45 | }, 46 | "bg-activeAlt": { 47 | "$type": "color", 48 | "$value": "{mode.color.action.grayscale.hoverAlt}", 49 | "$description": "REF'D IN FORM. " 50 | }, 51 | "bg-default": { 52 | "$type": "color", 53 | "$value": "{mode.color.generic.bg.nought}", 54 | "$description": "REF'D IN FORM. Popover container bg" 55 | }, 56 | "bg-hover": { 57 | "$type": "color", 58 | "$value": "{mode.color.action.grayscale.hoverAlt}", 59 | "$description": "REF'D IN FORM. " 60 | }, 61 | "label-active": { 62 | "$type": "color", 63 | "$value": "{mode.color.action.grayscale.withActive}" 64 | }, 65 | "label-activeAlt": { 66 | "$type": "color", 67 | "$value": "{mode.color.action.grayscale.withHover}" 68 | }, 69 | "label-disabled": { 70 | "$type": "color", 71 | "$value": "{mode.color.action.inactive.txt}" 72 | }, 73 | "label-hover": { 74 | "$type": "color", 75 | "$value": "{mode.color.action.grayscale.withHover}" 76 | }, 77 | "label-default": { 78 | "$type": "color", 79 | "$value": "{mode.color.action.grayscale.defaultAlt}" 80 | }, 81 | "submenu": { 82 | "bg-default": { 83 | "$type": "color", 84 | "$value": "{mode.color.generic.bg.faint}", 85 | "$description": "popover small screen submenu container bg" 86 | } 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sage/design-tokens", 3 | "version": "17.5.0", 4 | "author": "The Sage Group plc", 5 | "description": "Design tokens for the Sage Design System.", 6 | "private": false, 7 | "license": "SEE LICENSE IN https://github.com/Sage/design-tokens/blob/master/license", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/Sage/design-tokens.git" 11 | }, 12 | "engines": { 13 | "node": ">=22" 14 | }, 15 | "engineStrict": true, 16 | "tags": [ 17 | "design tokens", 18 | "sage", 19 | "design system" 20 | ], 21 | "type": "module", 22 | "scripts": { 23 | "build": "node --loader ts-node/esm --no-warnings=ExperimentalWarning ./scripts/build.ts", 24 | "prebuild": "node --loader ts-node/esm --no-warnings=ExperimentalWarning ./scripts/prebuild.ts", 25 | "postbuild": "node --loader ts-node/esm --no-warnings=ExperimentalWarning ./scripts/postbuild.ts", 26 | "prepublishOnly": "node --loader ts-node/esm --no-warnings=ExperimentalWarning ./scripts/bump-main-package-version.ts", 27 | "stylelint:dist": "npx stylelint 'dist/**/*.{css,scss}' --report-needless-disables --report-descriptionless-disables --report-invalid-scope-disables", 28 | "test": "vitest run" 29 | }, 30 | "devDependencies": { 31 | "@commitlint/cli": "^20.1.0", 32 | "@commitlint/config-conventional": "^20.0.0", 33 | "@prantlf/jsonlint": "^16.0.0", 34 | "@semantic-release/changelog": "^6.0.3", 35 | "@semantic-release/commit-analyzer": "^13.0.0", 36 | "@semantic-release/github": "^11.0.1", 37 | "@semantic-release/npm": "^13.1.2", 38 | "@semantic-release/release-notes-generator": "^14.0.1", 39 | "@tokens-studio/sd-transforms": "^2.0.2", 40 | "@tsconfig/strictest": "^2.0.5", 41 | "@types/chai": "^5.0.1", 42 | "@types/fs-extra": "^11.0.4", 43 | "@types/glob": "^8.1.0", 44 | "@types/lodash": "^4.17.13", 45 | "@types/mocha": "^10.0.10", 46 | "@types/node-fetch": "^2.6.12", 47 | "@vitest/ui": "^4.0.10", 48 | "chai": "^5.1.2", 49 | "conventional-changelog-conventionalcommits": "^8.0.0", 50 | "dotenv": "^16.4.5", 51 | "eslint": "^8.57.0", 52 | "eslint-config-standard": "^17.1.0", 53 | "eslint-plugin-header": "^3.1.1", 54 | "eslint-plugin-import": "^2.31.0", 55 | "eslint-plugin-n": "^16.6.2", 56 | "eslint-plugin-promise": "^6.6.0", 57 | "fantasticon": "^3.0.0", 58 | "figma-js": "^1.16.1-0", 59 | "fs-extra": "^11.2.0", 60 | "glob": "^11.0.0", 61 | "less": "^4.2.1", 62 | "lodash": "^4.17.21", 63 | "node-fetch": "^3.3.2", 64 | "nodemon": "^3.1.7", 65 | "prettier": "^3.4.1", 66 | "scss": "^0.2.4", 67 | "semantic-release": "^25.0.2", 68 | "style-dictionary": "^5.0.4", 69 | "stylelint": "^16.10.0", 70 | "stylelint-config-standard": "^36.0.1", 71 | "stylelint-config-standard-scss": "^13.1.0", 72 | "tinycolor2": "^1.6.0", 73 | "ts-node": "^10.9.2", 74 | "tsx": "^4.19.2", 75 | "typescript": "^5.7.2", 76 | "vitest": "^4.0.10" 77 | }, 78 | "commitlint": { 79 | "extends": [ 80 | "@commitlint/config-conventional" 81 | ] 82 | }, 83 | "overrides": { 84 | "jackspeak": "2.1.1", 85 | "eslint-plugin-n": "^16.6.2", 86 | "eslint-plugin-promise": "^6.6.0" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /docs/icons.md: -------------------------------------------------------------------------------- 1 | # Icons 2 | 3 | File: `./scripts/icons.js` 4 | 5 | ```bash 6 | $ node ./scripts/icons 7 | ``` 8 | 9 | ```js 10 | // script is executed on require, since it uses IIFE 11 | require('./scripts/icons') 12 | ``` 13 | 14 | ## Disclaimer 15 | Purpose of this tool is to delegate whole responsibility for the icons to the designers, and to speed up and facilitate the whole font creation process. Thanks to this, Designers are able to maintain icons and icon sets form the environment that they are most comfortable with. 16 | 17 | Icons script uses Figma API to request given file, and fetch all Icons Data - their name, preferred unicode, set as well as svg icons. They are then placed in a directory given in a config. Downloaded files are finally used to generate webfont file(s). In the end, all the data is saved to JSON file and documentation for icons is generated. 18 | 19 | ## Running 20 | In order to run icons fetch, you need to provide `FIGMA_ACCESS_TOKEN` and `FIGMA_FILE_ID` environmental variables. You can specify them in `.env` file since library utilizes `dotenv`. 21 | 22 | - `FIGMA_ACCESS_TOKEN` - personal access token. Check out [official API docs](https://www.figma.com/developers/api#access-tokens) on how to get your token. 23 | - `FIGMA_FILE_ID` - is a random alphanumeric string in url, like so: `https://www.figma.com/file//Icons` 24 | 25 | ## Figma file structure 26 | Each icon has to be a component in Figma. Component's name will be transformed to kebabCase and will be final icon name. Component Names have to be unique. 27 | You can provide additional information in Figma component's description field: 28 | - **preferred unicode** - this will assign given unicode to given icon in final font file. To consume this feature, add `code: ` at the beginning of your description 29 | - **search keywords** - to help find the best icon in the documentation. To use this feature, specify all keywords separated with coma. 30 | 31 | ### Sample description with preferred unicode and keywords: 32 | ``` 33 | code: f104 34 | mail, envelope, letter, package, parcel, send 35 | ``` 36 | 37 | ``` 38 | code: f120 39 | heart, love, favorites, fave, like 40 | ``` 41 | 42 | 43 | 44 | ## Config 45 | | Property name | Description | 46 | |---|---| 47 | | personalAccessToken | personal access token for figma | 48 | | fileId | id of a figma file | 49 | | pages | names of the pages that are containing icons | 50 | | multipleSets | if multiple sets should be created. If true, then each page will be different set. | 51 | | distDir | main output directory | 52 | | svgDir | directory for svg files | 53 | | fontsDir | directory for font files | 54 | | dataDir | directory for JSON file | 55 | | docsDir | output file for icon docs | 56 | | fontName | name of the font | 57 | | formats | formats to be generated ('svg', 'ttf', 'woff', 'woff2', 'eot') | 58 | | mainTemplate | path to handlebars template for docs | 59 | | meta | meta information for font files | 60 | | docsPartials | Glob to partials directory for documentation | 61 | | meta | meta data for font file | 62 | | meta.description | font files description | 63 | | meta.url | url for a font manufacturer | 64 | | meta.copyright | copyright information | 65 | | meta.version | font version number | 66 | 67 | 68 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behaviour that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behaviour by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behaviour and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviours that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported by contacting the project team on our [Teams Channel](https://teams.microsoft.com/l/channel/19%3a6125ffa9db564ac5b9fb1aec3b25888b%40thread.skype/Project%2520Team?groupId=20b4091d-cd24-4648-92c4-2712b859b3e8&tenantId=3e32dd7c-41f6-492d-a1a3-c58eb02cf4f8). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org/), version 1.4, available at [https://contributor-covenant.org/version/1/4](https://contributor-covenant.org/version/1/4/) -------------------------------------------------------------------------------- /data/tokens/components/nav.json: -------------------------------------------------------------------------------- 1 | { 2 | "nav": { 3 | "bg-default": { 4 | "$type": "color", 5 | "$value": "{mode.color.generic.bg.nought}", 6 | "$description": "nav bar bg" 7 | }, 8 | "leftnav": { 9 | "container": { 10 | "bg-default": { 11 | "$type": "color", 12 | "$value": "{mode.color.generic.bg.inverse.nought}", 13 | "$description": "bg of horizontal nav variant that launches the left nav" 14 | }, 15 | "bg-defaultAlt": { 16 | "$type": "color", 17 | "$value": "{mode.color.action.nav.inverse.defaultAlt}", 18 | "$description": "bg of the parent container" 19 | }, 20 | "border-default": { 21 | "$type": "color", 22 | "$value": "{mode.color.generic.fg.inverse.firm}", 23 | "$description": "dividing lines within parent container" 24 | } 25 | }, 26 | "item": { 27 | "bg-active": { 28 | "$type": "color", 29 | "$value": "{mode.color.action.nav.inverse.active}" 30 | }, 31 | "bg-default": { 32 | "$type": "color", 33 | "$value": "{mode.color.none}" 34 | }, 35 | "bg-hover": { 36 | "$type": "color", 37 | "$value": "{mode.color.action.nav.inverse.hover}" 38 | }, 39 | "label-active": { 40 | "$type": "color", 41 | "$value": "{mode.color.action.nav.inverse.withActive}" 42 | }, 43 | "label-default": { 44 | "$type": "color", 45 | "$value": "{mode.color.action.nav.inverse.withDefault}" 46 | }, 47 | "label-hover": { 48 | "$type": "color", 49 | "$value": "{mode.color.action.nav.inverse.withHover}" 50 | } 51 | } 52 | }, 53 | "modal": { 54 | "container": { 55 | "bg-default": { 56 | "$type": "color", 57 | "$value": "{mode.color.generic.bg.nought}", 58 | "$description": "bg of container holding stack of items in modal nav" 59 | }, 60 | "bg-defaultAlt": { 61 | "$type": "color", 62 | "$value": "{mode.color.generic.surface.faint}", 63 | "$description": "bg of the sticky header within modal" 64 | }, 65 | "border-default": { 66 | "$type": "color", 67 | "$value": "{mode.color.generic.fg.firm}", 68 | "$description": "dividing lines within parent container" 69 | } 70 | }, 71 | "item": { 72 | "bg-active": { 73 | "$type": "color", 74 | "$value": "{mode.color.action.nav.active}" 75 | }, 76 | "bg-default": { 77 | "$type": "color", 78 | "$value": "{mode.color.none}" 79 | }, 80 | "bg-hover": { 81 | "$type": "color", 82 | "$value": "{mode.color.action.nav.hover}" 83 | }, 84 | "label-active": { 85 | "$type": "color", 86 | "$value": "{mode.color.action.nav.withActive}" 87 | }, 88 | "label-default": { 89 | "$type": "color", 90 | "$value": "{mode.color.action.nav.withDefault}" 91 | }, 92 | "label-hover": { 93 | "$type": "color", 94 | "$value": "{mode.color.action.nav.withHover}" 95 | } 96 | }, 97 | "headeritem": { 98 | "label-default": { 99 | "$type": "color", 100 | "$value": "{mode.color.generic.txt.soft}", 101 | "$description": "color for subheaders passed into modal nav" 102 | } 103 | } 104 | }, 105 | "topnav": { 106 | "container": { 107 | "bg-default": { 108 | "$type": "color", 109 | "$value": "{mode.color.generic.bg.nought}", 110 | "$description": "bg of of horizontal nav variant that launches the left nav" 111 | }, 112 | "border-default": { 113 | "$type": "color", 114 | "$value": "{mode.color.generic.fg.firm}", 115 | "$description": "dividing lines within parent container" 116 | } 117 | }, 118 | "item": { 119 | "bg-active": { 120 | "$type": "color", 121 | "$value": "{mode.color.action.nav.active}" 122 | }, 123 | "bg-default": { 124 | "$type": "color", 125 | "$value": "{mode.color.none}" 126 | }, 127 | "bg-hover": { 128 | "$type": "color", 129 | "$value": "{mode.color.action.nav.hover}" 130 | }, 131 | "label-active": { 132 | "$type": "color", 133 | "$value": "{mode.color.action.nav.withActive}" 134 | }, 135 | "label-default": { 136 | "$type": "color", 137 | "$value": "{mode.color.action.nav.withDefault}" 138 | }, 139 | "label-hover": { 140 | "$type": "color", 141 | "$value": "{mode.color.action.nav.withHover}" 142 | } 143 | } 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /data/tokens/global/radius.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "radius": { 4 | "none": { 5 | "$type": "borderRadius", 6 | "$value": "0", 7 | "$description": "Button groups (internal/adjacent corners), Card select group (internal/adjacent corners), File input (integrated base bar top corners)." 8 | }, 9 | "container": { 10 | "3XS": { 11 | "$type": "borderRadius", 12 | "$value": "{core.dimension.12}", 13 | "$description": "Validation bar on input components" 14 | }, 15 | "2XS": { 16 | "$type": "borderRadius", 17 | "$value": "{core.dimension.25}", 18 | "$description": "Pill" 19 | }, 20 | "XS": { 21 | "$type": "borderRadius", 22 | "$value": "{core.dimension.50}" 23 | }, 24 | "S": { 25 | "$type": "borderRadius", 26 | "$value": "{core.dimension.75}", 27 | "$description": "L size loader and tracker corners" 28 | }, 29 | "M": { 30 | "$type": "borderRadius", 31 | "$value": "{core.dimension.100}", 32 | "$description": "Card select group (outer corners), Card select (single), Color picker advanced (inner swatch container), File input (file uploads & integrated progress bar (bottom corners),  File preview (File selector assets on left), Link preview, Message, Note, Popover (menu container in Action popover, Button-split, Button-multi-action, Calendar, Dropdown), Subscription tile (currently a pattern), Table (parent container), Tile & Tile flexbox (less rounded), Toast, Tooltip" 33 | }, 34 | "L": { 35 | "$type": "borderRadius", 36 | "$value": "{core.dimension.200}", 37 | "$description": "Card (less rounded), Carousel (slides), Color picker advanced (parent container), File preview (parent container), Medium Tile & Tile flexbox, Medium Card." 38 | }, 39 | "XL": { 40 | "$type": "borderRadius", 41 | "$value": "{core.dimension.300}", 42 | "$description": "Card (more rounded), Copilot Container, Carousel (parent container), Dialog (not full screen), Large Tile & Tile flexbox, Large cards" 43 | }, 44 | "2XL": { 45 | "$type": "borderRadius", 46 | "$value": "{core.dimension.400}" 47 | }, 48 | "3XL": { 49 | "$type": "borderRadius", 50 | "$value": "{core.dimension.500}", 51 | "$description": "marketing cards and tiles" 52 | }, 53 | "4XL": { 54 | "$type": "borderRadius", 55 | "$value": "{core.dimension.1000}", 56 | "$description": "marketing images " 57 | }, 58 | "circle": { 59 | "$type": "borderRadius", 60 | "$value": "999", 61 | "$description": "CIRCLE. Badge, Calendar date (today indicator and selected), Carousel selector dots, Loader bar, Portrait, Progress tracker, Radio button, Step flow (step indicators), Switch, " 62 | } 63 | }, 64 | "action": { 65 | "2XS": { 66 | "$type": "borderRadius", 67 | "$value": "{core.dimension.12}", 68 | "$description": "Tab baseline." 69 | }, 70 | "XS": { 71 | "$type": "borderRadius", 72 | "$value": "{core.dimension.25}", 73 | "$description": "Link (focus background and underline)" 74 | }, 75 | "S": { 76 | "$type": "borderRadius", 77 | "$value": "{core.dimension.50}", 78 | "$description": "S & M Checkboxes" 79 | }, 80 | "M": { 81 | "$type": "borderRadius", 82 | "$value": "{core.dimension.100}", 83 | "$description": "Button subtle, L Checkboxes, Date picker input, Date range input, Dropdown select (trigger), Search, File input (draggable area), Menu (bottom corners), Navigation: left (state bg shape), Skip focus, Tab, Text area, Text input," 84 | }, 85 | "L": { 86 | "$type": "borderRadius", 87 | "$value": "{core.dimension.200}", 88 | "$description": "Buttons S (typical and destructive, and inc bar, split and multi), Navigation left (collapsible Assets/Menu select top-right and bottom-right corners), " 89 | }, 90 | "XL": { 91 | "$type": "borderRadius", 92 | "$value": "{core.dimension.250}", 93 | "$description": "Buttons M (typical and destructive and inc bar split and multi), Button toggle M (parent container)" 94 | }, 95 | "2XL": { 96 | "$type": "borderRadius", 97 | "$value": "{core.dimension.300}", 98 | "$description": "Buttons L (typical and destructive and inc bar split and multi), Button toggle L (parent container)" 99 | }, 100 | "circle": { 101 | "$type": "borderRadius", 102 | "$value": "999", 103 | "$description": "Icon only buttons" 104 | } 105 | } 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /data/tokens/components/progress.json: -------------------------------------------------------------------------------- 1 | { 2 | "progress": { 3 | "none": { 4 | "$type": "color", 5 | "$value": "{mode.color.none}", 6 | "$description": "transparent override used for hiding colors when needed." 7 | }, 8 | "bg-default": { 9 | "$type": "color", 10 | "$value": "{mode.color.generic.bg.delicate}" 11 | }, 12 | "border-default": { 13 | "$type": "color", 14 | "$value": "{mode.color.generic.fg.firm}" 15 | }, 16 | "fg-alt": { 17 | "$type": "color", 18 | "$value": "{mode.color.status.positive.default}" 19 | }, 20 | "fg-alt2": { 21 | "$type": "color", 22 | "$value": "{mode.color.generic.txt.soft}" 23 | }, 24 | "fg-caution": { 25 | "$type": "color", 26 | "$value": "{mode.color.status.warning.default}" 27 | }, 28 | "fg-default": { 29 | "$type": "color", 30 | "$value": "{mode.color.action.grayscale.active}" 31 | }, 32 | "fg-error": { 33 | "$type": "color", 34 | "$value": "{mode.color.status.negative.default}" 35 | }, 36 | "fg-info": { 37 | "$type": "color", 38 | "$value": "{mode.color.status.info.default}" 39 | }, 40 | "label-alt": { 41 | "$type": "color", 42 | "$value": "{mode.color.generic.txt.moderate}" 43 | }, 44 | "label-default": { 45 | "$type": "color", 46 | "$value": "{mode.color.generic.txt.severe}" 47 | }, 48 | "dataviz": { 49 | "fg-default": { 50 | "$type": "color", 51 | "$value": "{mode.color.status.custom.teal.default}" 52 | } 53 | }, 54 | "inverse": { 55 | "label-alt": { 56 | "$type": "color", 57 | "$value": "{mode.color.generic.txt.inverse.soft}" 58 | } 59 | }, 60 | "loader": { 61 | "bg-default": { 62 | "$type": "color", 63 | "$value": "{mode.color.generic.bg.delicate}" 64 | }, 65 | "bg-skeleton": { 66 | "$type": "color", 67 | "$value": "linear-gradient(135deg, {mode.color.status.skeleton.stop-1} 15%, {mode.color.status.skeleton.stop-2} 85%)" 68 | }, 69 | "fg-default": { 70 | "$type": "color", 71 | "$value": "{mode.color.generic.fg.inverse.nought}", 72 | "$description": "used for loader standard loader spinner" 73 | }, 74 | "fg-ai": { 75 | "$type": "color", 76 | "$value": "{mode.color.status.ai.default-horizontal}", 77 | "$description": "Loader ring or bar" 78 | }, 79 | "fg-error": { 80 | "$type": "color", 81 | "$value": "{mode.color.status.negative.default}" 82 | }, 83 | "fg-complete": { 84 | "$type": "color", 85 | "$value": "{mode.color.status.positive.default}" 86 | }, 87 | "inverse": { 88 | "bg-default": { 89 | "$type": "color", 90 | "$value": "{mode.color.generic.bg.inverse.delicate}", 91 | "$description": "used for inverse loader bgs" 92 | }, 93 | "fg-default": { 94 | "$type": "color", 95 | "$value": "{mode.color.generic.fg.inverse.extreme}", 96 | "$description": "used on inverse loading spinner" 97 | }, 98 | "fg-ai": { 99 | "$type": "color", 100 | "$value": "{mode.color.status.ai.inverse.default-horizontal}", 101 | "$description": "Loader ring or bar" 102 | } 103 | } 104 | }, 105 | "stepflow": { 106 | "bg-active": { 107 | "$type": "color", 108 | "$value": "{mode.color.action.grayscale.active}" 109 | }, 110 | "bg-complete": { 111 | "$type": "color", 112 | "$value": "{mode.color.status.positive.default}" 113 | }, 114 | "bg-default": { 115 | "$type": "color", 116 | "$value": "{mode.color.generic.fg.nought}" 117 | }, 118 | "border-active-inner": { 119 | "$type": "color", 120 | "$value": "{mode.color.generic.fg.nought}" 121 | }, 122 | "border-active-outer": { 123 | "$type": "color", 124 | "$value": "{mode.color.action.grayscale.active}" 125 | }, 126 | "border-default": { 127 | "$type": "color", 128 | "$value": "{mode.color.generic.fg.firm}" 129 | }, 130 | "label-alt": { 131 | "$type": "color", 132 | "$value": "{mode.color.generic.txt.soft}" 133 | }, 134 | "label-default": { 135 | "$type": "color", 136 | "$value": "{mode.color.generic.txt.severe}" 137 | } 138 | }, 139 | "stepindicator": { 140 | "bg-active": { 141 | "$type": "color", 142 | "$value": "{mode.color.action.grayscale.active}" 143 | }, 144 | "bg-complete": { 145 | "$type": "color", 146 | "$value": "{mode.color.status.positive.default}" 147 | }, 148 | "border-active-outer": { 149 | "$type": "color", 150 | "$value": "{mode.color.action.grayscale.active}" 151 | }, 152 | "border-default": { 153 | "$type": "color", 154 | "$value": "{mode.color.generic.fg.firm}" 155 | }, 156 | "border-success": { 157 | "$type": "color", 158 | "$value": "{mode.color.status.positive.default}" 159 | }, 160 | "label-active": { 161 | "$type": "color", 162 | "$value": "{mode.color.action.grayscale.withActive}" 163 | }, 164 | "label-complete": { 165 | "$type": "color", 166 | "$value": "{mode.color.action.grayscale.withActive}" 167 | }, 168 | "label-default": { 169 | "$type": "color", 170 | "$value": "{mode.color.generic.txt.severe}" 171 | } 172 | } 173 | } 174 | } -------------------------------------------------------------------------------- /data/tokens/components/message.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": { 3 | "none": { 4 | "$type": "color", 5 | "$value": "{mode.color.none}", 6 | "$description": "transparent override used for hiding colors when needed." 7 | }, 8 | "contextual": { 9 | "bg": { 10 | "$type": "color", 11 | "$value": "{mode.color.generic.bg.nought}" 12 | }, 13 | "icon": { 14 | "$type": "color", 15 | "$value": "{mode.color.generic.bg.nought}", 16 | "$description": "decorative icon on standard contxtual messages" 17 | }, 18 | "icon-alt": { 19 | "$type": "color", 20 | "$value": "{mode.color.status.neutral.default}", 21 | "$description": "icon on neutral status dialogs" 22 | }, 23 | "txt": { 24 | "$type": "color", 25 | "$value": "{mode.color.status.txt.withHoverAlt}", 26 | "$description": "Message txt." 27 | }, 28 | "AI": { 29 | "bg-default": { 30 | "$type": "color", 31 | "$value": "{mode.color.status.ai.defaultAlt}" 32 | }, 33 | "bgAlt": { 34 | "$type": "color", 35 | "$value": "{mode.color.status.neutral.defaultAlt}" 36 | }, 37 | "border-default": { 38 | "$type": "color", 39 | "$value": "{mode.color.status.ai.defaultAlt}" 40 | } 41 | }, 42 | "callout": { 43 | "bgAlt": { 44 | "$type": "color", 45 | "$value": "{mode.color.status.callout.defaultAlt}", 46 | "$description": "Subtle message bg" 47 | }, 48 | "icon": { 49 | "$type": "color", 50 | "$value": "{mode.color.status.txt.withDefaultAlt}" 51 | } 52 | }, 53 | "error": { 54 | "bg-default": { 55 | "$type": "color", 56 | "$value": "{mode.color.status.negative.default}" 57 | }, 58 | "bgAlt": { 59 | "$type": "color", 60 | "$value": "{mode.color.status.negative.defaultAlt}", 61 | "$description": "Subtle message bg" 62 | }, 63 | "border-default": { 64 | "$type": "color", 65 | "$value": "{mode.color.status.negative.default}" 66 | }, 67 | "icon": { 68 | "$type": "color", 69 | "$value": "{mode.color.status.negative.default}", 70 | "$description": "Icon on tinted bgs" 71 | } 72 | }, 73 | "info": { 74 | "bg-default": { 75 | "$type": "color", 76 | "$value": "{mode.color.status.info.default}" 77 | }, 78 | "bgAlt": { 79 | "$type": "color", 80 | "$value": "{mode.color.status.info.defaultAlt}", 81 | "$description": "Subtle message bg" 82 | }, 83 | "border-default": { 84 | "$type": "color", 85 | "$value": "{mode.color.status.info.default}" 86 | }, 87 | "icon": { 88 | "$type": "color", 89 | "$value": "{mode.color.status.info.default}", 90 | "$description": "Icon on tinted bgs" 91 | } 92 | }, 93 | "success": { 94 | "bg-default": { 95 | "$type": "color", 96 | "$value": "{mode.color.status.positive.default}" 97 | }, 98 | "bgAlt": { 99 | "$type": "color", 100 | "$value": "{mode.color.status.positive.defaultAlt}", 101 | "$description": "Subtle message bg" 102 | }, 103 | "border-default": { 104 | "$type": "color", 105 | "$value": "{mode.color.status.positive.default}" 106 | }, 107 | "icon": { 108 | "$type": "color", 109 | "$value": "{mode.color.status.positive.default}", 110 | "$description": "Icon on tinted bgs" 111 | } 112 | }, 113 | "warning": { 114 | "bg-default": { 115 | "$type": "color", 116 | "$value": "{mode.color.status.warning.default}" 117 | }, 118 | "bgAlt": { 119 | "$type": "color", 120 | "$value": "{mode.color.status.warning.defaultAlt}", 121 | "$description": "Subtle message bg" 122 | }, 123 | "border-default": { 124 | "$type": "color", 125 | "$value": "{mode.color.status.warning.default}" 126 | }, 127 | "icon": { 128 | "$type": "color", 129 | "$value": "{mode.color.status.warning.default}", 130 | "$description": "Icon on tinted bgs" 131 | } 132 | } 133 | }, 134 | "global": { 135 | "label-default": { 136 | "$type": "color", 137 | "$value": "{mode.color.status.txt.withDefaultAlt}" 138 | }, 139 | "label-hover": { 140 | "$type": "color", 141 | "$value": "{mode.color.action.grayscale.withHover}" 142 | }, 143 | "callout": { 144 | "bg-default": { 145 | "$type": "color", 146 | "$value": "{mode.color.status.callout.defaultAlt}", 147 | "$description": "Global message bg" 148 | }, 149 | "bg-hover": { 150 | "$type": "color", 151 | "$value": "{mode.color.status.callout.hoverAlt}", 152 | "$description": "global message bg hover" 153 | }, 154 | "icon": { 155 | "$type": "color", 156 | "$value": "{mode.color.status.txt.withDefaultAlt}", 157 | "$description": "Icon on tinted bgs" 158 | } 159 | }, 160 | "info": { 161 | "bg-default": { 162 | "$type": "color", 163 | "$value": "{mode.color.status.info.defaultAlt}", 164 | "$description": "Global message bg" 165 | }, 166 | "bg-hover": { 167 | "$type": "color", 168 | "$value": "{mode.color.status.info.hoverAlt}" 169 | }, 170 | "icon": { 171 | "$type": "color", 172 | "$value": "{mode.color.status.info.default}", 173 | "$description": "Icon on tinted bgs" 174 | } 175 | }, 176 | "warning": { 177 | "bg-default": { 178 | "$type": "color", 179 | "$value": "{mode.color.status.warning.defaultAlt}", 180 | "$description": "Global message bg" 181 | }, 182 | "bg-hover": { 183 | "$type": "color", 184 | "$value": "{mode.color.status.warning.hoverAlt}" 185 | }, 186 | "icon": { 187 | "$type": "color", 188 | "$value": "{mode.color.status.warning.default}", 189 | "$description": "Icon on tinted bgs" 190 | } 191 | } 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /scripts/postbuild.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | import path, { dirname, resolve } from "path" 5 | import { fileURLToPath } from "url" 6 | import fs from "fs-extra" 7 | import camelCase from "lodash/camelCase.js" 8 | import pick from "lodash/pick.js" 9 | import { FontAssetType } from "fantasticon" 10 | import { sync } from "glob" 11 | import dotenv from "dotenv" 12 | 13 | const __filename = fileURLToPath(import.meta.url); 14 | const __dirname = dirname(__filename); 15 | 16 | import { FileName } from "./utils/filename.js" 17 | import { HeaderContents } from "./utils/file-header.js" 18 | 19 | import { Icons } from "./icons.js" 20 | 21 | dotenv.config() 22 | 23 | function copyPackageJSON () { 24 | try { 25 | const packageDef = fs.readJsonSync(resolve(__dirname, "../package.json")) 26 | const filteredPackageDef = pick( 27 | packageDef, 28 | ["name", "dependencies", "repository", "description", "author", "version", "peerDependencies", "license", "tags"] 29 | ) 30 | 31 | // Writes to package.json in dist 32 | fs.outputJsonSync( 33 | resolve(__dirname, "../dist/package.json"), 34 | filteredPackageDef, 35 | { 36 | spaces: 2 37 | } 38 | ) 39 | } catch (err) { 40 | console.log("Error copying package.json") 41 | console.log(err) 42 | } 43 | } 44 | 45 | function copyReadme () { 46 | try { 47 | fs.copySync( 48 | resolve(__dirname, "../README.md"), 49 | resolve(__dirname, "../dist/README.md") 50 | ) 51 | } catch (err) { 52 | console.log("Error copying readme to dist") 53 | console.log(err) 54 | } 55 | } 56 | 57 | function addCommonJSEntryFile () { 58 | const jsFilePaths = sync("./dist/js/common/*.js") 59 | const jsComponentPaths = sync("./dist/js/common/components/*.js") 60 | const entryFilePath = resolve(__dirname, "../dist/js/common/index.js") 61 | 62 | const fileRequires = jsFilePaths 63 | .map((filePath: string) => { 64 | const pathParts = filePath.split("/") 65 | const fullName = pathParts[pathParts.length - 1] 66 | const name = FileName(fullName) 67 | 68 | return `const ${camelCase(name)} = require("./${name}.js");` 69 | } 70 | ).join("\n") 71 | 72 | const componentRequires = jsComponentPaths 73 | .map((filePath: string) => { 74 | const pathParts = filePath.split("/") 75 | const fullName = pathParts[pathParts.length - 1] 76 | const component = FileName(fullName) 77 | 78 | return `const ${camelCase(component)} = require("./components/${component}.js");` 79 | }).join("\n") 80 | 81 | const fileExports = jsFilePaths 82 | .map((filePath: string) => { 83 | const pathParts = filePath.split("/") 84 | const fullName = pathParts[pathParts.length - 1] 85 | const name = FileName(fullName) 86 | 87 | return ` ${camelCase(name)},` 88 | }).join("\n") 89 | 90 | const componentExports = jsComponentPaths 91 | .map((filePath: string) => { 92 | const pathParts = filePath.split("/") 93 | const fullName = pathParts[pathParts.length - 1] 94 | const component = FileName(fullName) 95 | 96 | return ` ${camelCase(component)},` 97 | }).join("\n") 98 | 99 | const entryContent = [ 100 | "// Main commonJS token requires", 101 | fileRequires, 102 | "", 103 | "// Component commonJS token requires", 104 | componentRequires, 105 | "", 106 | "module.exports = {", 107 | fileExports, 108 | componentExports, 109 | "};" 110 | ].join("\n") 111 | 112 | fs.outputFileSync(entryFilePath, entryContent) 113 | } 114 | 115 | function addES6EntryFiles () { 116 | const jsFilePaths = sync("./dist/js/ES6/*.js") 117 | const jsComponentPaths = sync("./dist/js/ES6/components/*.js") 118 | const entryFilePath = resolve(__dirname, "../dist/js/ES6/index.js") 119 | 120 | const fileExports = jsFilePaths 121 | .map((filePath: string) => { 122 | const pathParts = filePath.split("/") 123 | const fullName = pathParts[pathParts.length - 1] 124 | const name = FileName(fullName) 125 | 126 | return `export * as ${camelCase(name)} from "./${name}.js"` 127 | }).join("\n") 128 | 129 | const componentExports = jsComponentPaths 130 | .map((filePath: string) => { 131 | const pathParts = filePath.split("/") 132 | const fullName = pathParts[pathParts.length - 1] 133 | const component = FileName(fullName) 134 | 135 | return `export * as ${camelCase(component)} from "./components/${component}.js"` 136 | }).join("\n") 137 | 138 | const entryContent = [ 139 | "// Main ES6 token exports", 140 | fileExports, 141 | "", 142 | "// Component ES6 token exports", 143 | componentExports, 144 | "" 145 | ].join("\n") 146 | 147 | fs.outputFileSync(entryFilePath, entryContent) 148 | } 149 | 150 | function addFileHeader () { 151 | const files = sync("dist/**/*.@(css|js|ts|d.ts|scss|less)") 152 | files.forEach((file: string) => { 153 | try { 154 | const isScss = path.extname(file) === ".scss" 155 | const filePath = resolve(__dirname, "../", file) 156 | const outputData = HeaderContents(isScss) + "\r\n\r\n" + fs.readFileSync(filePath) 157 | 158 | fs.outputFileSync(filePath, outputData) 159 | } catch (er) { 160 | console.error(`Error adding header to ${file}`, er) 161 | } 162 | }) 163 | } 164 | 165 | function copyAssets () { 166 | try { 167 | fs.copySync( 168 | resolve(__dirname, "../assets"), 169 | resolve(__dirname, "../dist/assets/") 170 | ) 171 | } catch (err) { 172 | console.log("Error copying assets to dist") 173 | console.log(err) 174 | } 175 | } 176 | 177 | function fixCSSCalcExpressions() { 178 | const cssFiles = sync("./dist/css/**/*.css"); 179 | cssFiles.forEach(file => { 180 | try { 181 | const filePath = resolve(__dirname, "../", file); 182 | let content = fs.readFileSync(filePath, 'utf8'); 183 | 184 | // Wrap any values with mathematical operation in calc() 185 | content = content.replace( 186 | /(--[\w-]+):\s*(var\(--[\w-]+\)\s*[\+\-\*\/]\s*[^;]+);/g, 187 | '$1: calc($2);' 188 | ); 189 | 190 | fs.writeFileSync(filePath, content); 191 | } catch (err) { 192 | console.error(`Error fixing math in ${file}:`, err); 193 | } 194 | }); 195 | } 196 | 197 | (async () => { 198 | copyPackageJSON() 199 | copyReadme() 200 | copyAssets() 201 | addCommonJSEntryFile() 202 | addES6EntryFiles() 203 | fixCSSCalcExpressions() 204 | addFileHeader() 205 | await Icons({ 206 | personalAccessToken: process.env["FIGMA_ACCESS_TOKEN"], 207 | fileId: process.env["FIGMA_FILE_ID"], 208 | pages: ["Icons"], 209 | multipleSets: false, 210 | distDir: "./dist", 211 | svgDir: "./dist/assets/icons/svg", 212 | fontsDir: "./dist/assets/icons/fonts", 213 | dataDir: "./dist/assets/icons/data", 214 | fontName: "sage-icons", 215 | formats: [FontAssetType.SVG, FontAssetType.WOFF, FontAssetType.WOFF2, FontAssetType.TTF, FontAssetType.EOT], 216 | mainTemplate: "./templates/layout.hbs", 217 | docsDir: "./dist/docs/icons/", 218 | docsPartials: "./templates/partials/**/*.hbs", 219 | meta: { 220 | description: "Sage Icon Font", 221 | url: "http://sage.com", 222 | copyright: "Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved.", 223 | version: "1.0" 224 | } 225 | }) 226 | })() 227 | -------------------------------------------------------------------------------- /scripts/build.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | import * as fs from "fs" 6 | import { StyleDictionary, groups } from "./style-dictionary.js" 7 | import { DesignToken, File } from "style-dictionary/types" 8 | import { FilterComponent } from "./utils/filter-component.js" 9 | import { Config } from "style-dictionary" 10 | 11 | const components = fs.readdirSync("./data/tokens/components/") 12 | const modes = fs.readdirSync("./data/tokens/mode/") 13 | 14 | interface IMode { 15 | modeName?: string 16 | format: string 17 | suffix: string 18 | subPath?: string 19 | } 20 | 21 | interface IFiles extends IMode { 22 | componentName: string 23 | outputRefs?: boolean 24 | } 25 | 26 | const getMode = ({modeName = "", format, suffix, subPath}: IMode): File[] => { 27 | const mode = format.includes("variables") ? "" : modeName 28 | 29 | const componentArray: File[] = [] 30 | 31 | components.forEach((component) => { 32 | const componentName = component.split(".json")[0] 33 | 34 | if (!componentName) { 35 | throw new Error( 36 | `Component name not found for ${component}`) 37 | } 38 | 39 | componentArray.push(...getFiles({componentName, modeName: mode, format, suffix, outputRefs: true, subPath})) 40 | }) 41 | 42 | return [ 43 | ...getFiles({componentName: "mode", modeName, format, suffix, subPath}), 44 | ...componentArray 45 | ] 46 | } 47 | 48 | const getFormat = (format: string, outputRefs: boolean, componentName: string): string => { 49 | // outputRefs is true for mode and component files, false for global 50 | if (format === "json/flat" && outputRefs) { 51 | return "custom/json-with-refs"; 52 | } else if (format === "javascript/es6" && !["mode", "global", "dark", "light"].includes(componentName)) { 53 | // For component files, use custom ES6 format instead of standard 54 | return "custom/es6-with-refs"; 55 | } else if (format === "javascript/module") { 56 | if (["mode", "global", "dark", "light"].includes(componentName)) { 57 | // For mode/global files we want to have similar export format to ES6 rather nested objects 58 | return "custom/commonjs-exports"; 59 | } else { 60 | // For component files, use custom CommonJS format instead of standard 61 | return "custom/commonjs-with-refs"; 62 | } 63 | } 64 | 65 | return format; 66 | } 67 | 68 | const getFiles = ({componentName, modeName = "", format, suffix, outputRefs = false, subPath}: IFiles): File[] => { 69 | const getPath = (componentName: string) => { 70 | let path = "" 71 | 72 | switch(componentName) { 73 | case "mode": 74 | path = modeName; 75 | break 76 | case "global": 77 | path = "global"; 78 | break 79 | default: 80 | path = `components/${componentName}`; 81 | } 82 | 83 | if (subPath) { 84 | path = subPath + (path ? `/${path}` : ""); 85 | } 86 | 87 | return path 88 | } 89 | 90 | const path = getPath(componentName).trim() 91 | const actualFormat = getFormat(format, outputRefs, componentName); 92 | 93 | return [ 94 | { 95 | destination: `${path}.${suffix}`, 96 | filter: (token: DesignToken) => FilterComponent(token, componentName, format.includes("json")), 97 | format: actualFormat, 98 | options: { 99 | outputReferences: outputRefs 100 | } 101 | } 102 | ] 103 | } 104 | 105 | const getGlobalConfig = (): Config => { 106 | return { 107 | source: [ 108 | "./data/tokens/core.json", 109 | "./data/tokens/global/*.json" 110 | ], 111 | preprocessors: ["tokens-studio"], 112 | platforms: { 113 | css: { 114 | buildPath: "dist/css/", 115 | transforms: groups.css, 116 | files: [ 117 | ...getFiles({componentName: "global", format: "css/variables", suffix: "css"}) 118 | ] 119 | }, 120 | scss: { 121 | buildPath: "dist/scss/", 122 | transforms: groups.scss, 123 | files: [ 124 | ...getFiles({componentName: "global", format: "scss/variables", suffix: "scss"}) 125 | ] 126 | }, 127 | js: { 128 | buildPath: "dist/js/", 129 | transforms: groups.js, 130 | files: [ 131 | ...getFiles({componentName: "global", format: "javascript/module", subPath: "common", suffix: "js"}), 132 | ...getFiles({componentName: "global", format: "typescript/module-declarations", subPath: "common", suffix: "d.ts"}), 133 | ...getFiles({componentName: "global", format: "javascript/es6", subPath: "es6", suffix: "js"}), 134 | ...getFiles({componentName: "global", format: "typescript/es6-declarations", subPath: "es6", suffix: "d.ts"}), 135 | ] 136 | }, 137 | json: { 138 | buildPath: "dist/json/", 139 | transforms: groups.json, 140 | files: [ 141 | ...getFiles({componentName: "global", format: "json/flat", suffix: "json"}) 142 | ] 143 | } 144 | }, 145 | log: { 146 | warnings: "warn" as const, 147 | verbosity: "verbose" as const, 148 | errors: { 149 | brokenReferences: "throw" as const, 150 | }, 151 | }, 152 | } 153 | } 154 | 155 | const getModeConfig = (modeName: string): Config => { 156 | return { 157 | source: [ 158 | "./data/tokens/core.json", 159 | "./data/tokens/global/*.json", 160 | `./data/tokens/mode/${modeName}.json`, 161 | "./data/tokens/components/*.json" 162 | ], 163 | preprocessors: ["tokens-studio"], 164 | platforms: { 165 | css: { 166 | buildPath: "dist/css/", 167 | transforms: groups.css, 168 | files: [ 169 | ...getMode({modeName, format: "css/variables", suffix: "css"}) 170 | ] 171 | }, 172 | scss: { 173 | buildPath: "dist/scss/", 174 | transforms: groups.scss, 175 | files: [ 176 | ...getMode({modeName, format: "scss/variables", suffix: "scss"}) 177 | ] 178 | }, 179 | js: { 180 | buildPath: "dist/js/", 181 | transforms: groups.js, 182 | files: [ 183 | ...getMode({modeName, format: "javascript/module", subPath: "common", suffix: "js"}), 184 | ...getMode({modeName, format: "typescript/module-declarations", subPath: "common", suffix: "d.ts"}), 185 | ...getMode({modeName, format: "javascript/es6", subPath: "es6", suffix: "js"}), 186 | ...getMode({modeName, format: "typescript/es6-declarations", subPath: "es6", suffix: "d.ts"}), 187 | ] 188 | }, 189 | json: { 190 | buildPath: "dist/json/", 191 | transforms: groups.json, 192 | files: [ 193 | ...getMode({modeName, format: "json/flat", suffix: "json"}) 194 | ] 195 | } 196 | }, 197 | log: { 198 | warnings: "warn" as const, 199 | verbosity: "verbose" as const, 200 | errors: { 201 | brokenReferences: "throw" as const, 202 | }, 203 | }, 204 | } 205 | } 206 | 207 | // Build global tokens 208 | const globalStyleDictionary = new StyleDictionary(getGlobalConfig()) 209 | 210 | await globalStyleDictionary.buildPlatform("css") 211 | await globalStyleDictionary.buildPlatform("scss") 212 | await globalStyleDictionary.buildPlatform("js") 213 | await globalStyleDictionary.buildPlatform("json") 214 | 215 | // Build mode-specific tokens 216 | modes.forEach(async (mode) => { 217 | const modeName = mode.split(".json")[0] 218 | 219 | if (!modeName) { 220 | throw new Error( 221 | `Mode name not found for ${mode}`) 222 | } 223 | 224 | const modeStyleDictionary = new StyleDictionary(getModeConfig(modeName)) 225 | 226 | await modeStyleDictionary.buildPlatform("css") 227 | await modeStyleDictionary.buildPlatform("scss") 228 | await modeStyleDictionary.buildPlatform("js") 229 | await modeStyleDictionary.buildPlatform("json") 230 | }); 231 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sage Design Tokens 2 | 3 | [![GitHub release](https://img.shields.io/github/release/Sage/design-tokens.svg)](https://GitHub.com/Sage/design-tokens/releases/) 4 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 5 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Sage/design-tokens/graphs/commit-activity) 6 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 7 | 8 | This repository contains the design tokens from the Sage Design System. These are maintained by the Sage DS team. This library is for distributing these tokens across multiple platforms. 9 | 10 | ## What are design tokens? 11 | 12 | Design Tokens are Design System’s most basic, lowest level element. In atomic design terminology those would be the protons or electrons. 13 | 14 | Basically those are **key-value records named and organized the same way regardless of the platform** (e.g. web, Android, iOS, Figma). They can define various properties, such as colors, paddings, margins, sizes, font sizes, font families, transitions, animations, and others. **They represent certain design decisions.** 15 | 16 | Design tokens purpose is to: 17 | - **Release developers from taking design decisions.** Often while developing a component, developer needs to take decision what tint of what color should be used. This decision should be taken by designer, not developer. 18 | - **Improve handover process and communication between designers and developers.** Both, developers and designers are going to use the same token name for given property (color, background color, border, padding, margin, transition and so on). In the end, developers don't need to know what the final value will be. 19 | - **Narrow value set to only needed values.** Design System uses narrow set of values (spacings, colors, typography properties and others). Those are only values that are needed for visual description of the component. 20 | - **Keep visual consistency across all components of the library.** 21 | 22 | ## Docs: 23 | - [Generating icons](./docs/icons.md) 24 | 25 | ## Using the design tokens 26 | 27 | ### Web 28 | 29 | To make use of these tokens in your application, import the correct variable definitions based on your styling technology. 30 | 31 | #### Install 32 | 33 | To add to a project using npm: 34 | 35 | ```bash 36 | # If you're using npm: 37 | npm install --save @sage/design-tokens 38 | 39 | # OR If you're using yarn: 40 | yarn add @sage/design-tokens 41 | ``` 42 | 43 | You can also add the files directly by downloading from the [releases page on Github](https://github.com/Sage/design-tokens/releases). 44 | 45 | #### CSS 46 | 47 | To make use of the css variables, import them into your code like so: 48 | 49 | ```css 50 | @import url("@sage/design-tokens/css/light.css"); 51 | @import url("@sage/design-tokens/css/dark.css") (prefers-color-scheme: dark); 52 | @import url("@sage/design-tokens/css/components/button.css"); 53 | ``` 54 | 55 | **Note:** For manual theme switching in JavaScript applications, we recommend using the HTML `` approach rather than dynamic imports to avoid bundler complexity. You can import the component css files in your JS like below. 56 | 57 | ```js 58 | import "@sage/design-tokens/css/components/button.css"; 59 | ``` 60 | 61 | ```html 62 | 63 | 64 | 82 | ``` 83 | 84 | #### SCSS 85 | 86 | The SCSS format provides traditional Sass variables while handling mode switching through separate mode files. Due to variable naming conventions, loading both `light` and `dark` modes simultaneously would cause conflicts. To address this, dedicated mode files are provided. 87 | 88 | ##### Available Files 89 | 90 | - Individual component files (e.g., `button.scss`, `container.scss`) - Available for granular imports 91 | - The `global`, `light` and `dark` tokens are also exported in their own files for granularity as well 92 | 93 | **Note:** The current SCSS output requires the use of `@import` statements to ensure variables are properly scoped across files. While `@import` will be deprecated when Dart Sass 3.0.0 is released, we will endeavor to update our output format to support the modern `@use` module system before that release. 94 | 95 | To suppress deprecation warnings during compilation, add the `--silence-deprecation=import` flag to your Sass build command. 96 | 97 | ##### Light and Dark Mode Support with Granular Imports 98 | 99 | Import specific components when you only need certain tokens and create separate CSS files for each mode: 100 | 101 | ```scss 102 | // button-light.scss 103 | @import "@sage/design-tokens/scss/global.scss"; 104 | @import "@sage/design-tokens/scss/light.scss"; 105 | @import "@sage/design-tokens/scss/components/button.scss"; 106 | 107 | .button-destructive-primary { 108 | background-color: $button-destructive-primary-bg-default; 109 | 110 | &:hover { 111 | background-color: $button-destructive-primary-bg-hover; 112 | } 113 | } 114 | ``` 115 | 116 | ```scss 117 | // button-dark.scss 118 | @import "@sage/design-tokens/scss/global.scss"; 119 | @import "@sage/design-tokens/scss/dark.scss"; 120 | @import "@sage/design-tokens/scss/components/button.scss"; 121 | 122 | .button-destructive-primary { 123 | background-color: $button-destructive-primary-bg-default; 124 | 125 | &:hover { 126 | background-color: $button-destructive-primary-bg-hover; 127 | } 128 | } 129 | ``` 130 | 131 | Build process: 132 | ```bash 133 | sass button-light.scss:button-light.css --no-source-map --silence-deprecation=import 134 | 135 | sass button-dark.scss:button-dark.css --no-source-map --silence-deprecation=import 136 | ``` 137 | 138 | #### Common JS module 139 | 140 | ```js 141 | const commonTokens = require("@sage/design-tokens/js/common"); 142 | const buttonTokens = commonTokens.button; 143 | 144 | element.style.backgroundColor = buttonTokens.buttonDestructivePrimaryBgDefault; 145 | ``` 146 | 147 | #### ES6 module 148 | 149 | ```js 150 | import { button } from "@sage/design-tokens/js/es6"; 151 | 152 | element.style.backgroundColor = button.buttonDestructivePrimaryBgDefault; 153 | ``` 154 | 155 | A type definition file is also included to work in projects with typescript installed. 156 | 157 | #### Other formats 158 | 159 | It is possible to export design tokens to any format or language. If you need to use design tokens in your technology, please contact us and describe your needs. 160 | 161 | ## Contributing 162 | 163 | If you would like to help contribute to this library, please read our [contributing documentation](./docs/CONTRIBUTING.md), 164 | 165 | ## Licence 166 | 167 | Licensed under the Apache License, Version 2.0 (the "License"); 168 | you may not use these files except in compliance with the License. 169 | You may obtain a copy of the License at [Apache 2.0 license](./license). 170 | 171 | Unless required by applicable law or agreed to in writing, software 172 | distributed under the License is distributed on an "AS IS" BASIS, 173 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 174 | See the License for the specific language governing permissions and 175 | limitations under the License. 176 | 177 | Copyright (c) 2025 Sage Group Plc. All rights reserved. 178 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 22 | 23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 24 | 25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 26 | 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 28 | 29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 30 | 31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | You must cause any modified files to carry prominent notices stating that You changed the files; and 39 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 40 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 41 | 42 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 43 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 44 | 45 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 46 | 47 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 48 | 49 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 50 | 51 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 52 | 53 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /data/tokens/components/dataviz.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataviz": { 3 | "chart": { 4 | "line": { 5 | "bg-default": { 6 | "$type": "color", 7 | "$value": "{mode.color.colorcode.green.deep}" 8 | }, 9 | "border": { 10 | "$type": "color", 11 | "$value": "{mode.color.colorcode.green.deep}" 12 | } 13 | }, 14 | "bar": { 15 | "gray": { 16 | "bg-alt": { 17 | "$type": "color", 18 | "$value": "{mode.color.colorcode.gray.muted}" 19 | }, 20 | "bg-default": { 21 | "$type": "color", 22 | "$value": "{mode.color.colorcode.gray.severe}" 23 | }, 24 | "border": { 25 | "$type": "color", 26 | "$value": "{mode.color.colorcode.gray.severe}" 27 | }, 28 | "pattern": { 29 | "$type": "color", 30 | "$value": "{mode.color.colorcode.gray.severe}" 31 | } 32 | }, 33 | "green": { 34 | "bg-alt": { 35 | "$type": "color", 36 | "$value": "{mode.color.colorcode.green.muted}" 37 | }, 38 | "bg-default": { 39 | "$type": "color", 40 | "$value": "{mode.color.colorcode.green.deep}" 41 | }, 42 | "border": { 43 | "$type": "color", 44 | "$value": "{mode.color.colorcode.green.deep}" 45 | }, 46 | "pattern": { 47 | "$type": "color", 48 | "$value": "{mode.color.colorcode.green.deep}" 49 | } 50 | }, 51 | "orange": { 52 | "bg-alt": { 53 | "$type": "color", 54 | "$value": "{mode.color.colorcode.orange.muted}" 55 | }, 56 | "bg-default": { 57 | "$type": "color", 58 | "$value": "{mode.color.colorcode.orange.deep}" 59 | }, 60 | "border": { 61 | "$type": "color", 62 | "$value": "{mode.color.colorcode.orange.deep}" 63 | }, 64 | "pattern": { 65 | "$type": "color", 66 | "$value": "{mode.color.colorcode.orange.deep}" 67 | } 68 | } 69 | }, 70 | "donut": { 71 | "gray": { 72 | "bg-alt": { 73 | "$type": "color", 74 | "$value": "{mode.color.colorcode.gray.muted}" 75 | }, 76 | "bg-default": { 77 | "$type": "color", 78 | "$value": "{mode.color.colorcode.gray.severe}" 79 | }, 80 | "border": { 81 | "$type": "color", 82 | "$value": "{mode.color.colorcode.gray.severe}" 83 | }, 84 | "pattern": { 85 | "$type": "color", 86 | "$value": "{mode.color.colorcode.gray.severe}" 87 | } 88 | }, 89 | "green": { 90 | "bg-alt": { 91 | "$type": "color", 92 | "$value": "{mode.color.colorcode.green.muted}" 93 | }, 94 | "bg-default": { 95 | "$type": "color", 96 | "$value": "{mode.color.colorcode.green.deep}" 97 | }, 98 | "border": { 99 | "$type": "color", 100 | "$value": "{mode.color.colorcode.green.deep}" 101 | }, 102 | "pattern": { 103 | "$type": "color", 104 | "$value": "{mode.color.colorcode.green.deep}" 105 | } 106 | }, 107 | "orange": { 108 | "bg-alt": { 109 | "$type": "color", 110 | "$value": "{mode.color.colorcode.orange.muted}" 111 | }, 112 | "bg-default": { 113 | "$type": "color", 114 | "$value": "{mode.color.colorcode.orange.deep}" 115 | }, 116 | "border": { 117 | "$type": "color", 118 | "$value": "{mode.color.colorcode.orange.deep}" 119 | }, 120 | "pattern": { 121 | "$type": "color", 122 | "$value": "{mode.color.colorcode.orange.deep}" 123 | } 124 | } 125 | } 126 | }, 127 | "generic": { 128 | "label-alt": { 129 | "$type": "color", 130 | "$value": "{mode.color.generic.txt.inverse.extreme}", 131 | "$description": "Accessible on colorpicker bg-alt colors." 132 | }, 133 | "label-default": { 134 | "$type": "color", 135 | "$value": "{mode.color.generic.txt.extreme}", 136 | "$description": "Accessible on colorpicker bg-default colors." 137 | }, 138 | "blue": { 139 | "bg-alt": { 140 | "$type": "color", 141 | "$value": "{mode.color.colorcode.blue.deep}" 142 | }, 143 | "bg-default": { 144 | "$type": "color", 145 | "$value": "{mode.color.colorcode.blue.muted}" 146 | }, 147 | "border": { 148 | "$type": "color", 149 | "$value": "{mode.color.colorcode.blue.deep}" 150 | }, 151 | "pattern": { 152 | "$type": "color", 153 | "$value": "{mode.color.colorcode.blue.deep}" 154 | } 155 | }, 156 | "teal": { 157 | "bg-alt": { 158 | "$type": "color", 159 | "$value": "{mode.color.colorcode.teal.deep}" 160 | }, 161 | "bg-default": { 162 | "$type": "color", 163 | "$value": "{mode.color.colorcode.teal.muted}" 164 | }, 165 | "border": { 166 | "$type": "color", 167 | "$value": "{mode.color.colorcode.teal.deep}" 168 | }, 169 | "pattern": { 170 | "$type": "color", 171 | "$value": "{mode.color.colorcode.teal.deep}" 172 | } 173 | }, 174 | "green": { 175 | "bg-alt": { 176 | "$type": "color", 177 | "$value": "{mode.color.colorcode.green.deep}" 178 | }, 179 | "bg-default": { 180 | "$type": "color", 181 | "$value": "{mode.color.colorcode.green.muted}" 182 | }, 183 | "border": { 184 | "$type": "color", 185 | "$value": "{mode.color.colorcode.green.deep}" 186 | }, 187 | "pattern": { 188 | "$type": "color", 189 | "$value": "{mode.color.colorcode.green.deep}" 190 | } 191 | }, 192 | "lime": { 193 | "bg-alt": { 194 | "$type": "color", 195 | "$value": "{mode.color.colorcode.lime.deep}" 196 | }, 197 | "bg-default": { 198 | "$type": "color", 199 | "$value": "{mode.color.colorcode.lime.muted}" 200 | }, 201 | "border": { 202 | "$type": "color", 203 | "$value": "{mode.color.colorcode.lime.deep}" 204 | }, 205 | "pattern": { 206 | "$type": "color", 207 | "$value": "{mode.color.colorcode.lime.deep}" 208 | } 209 | }, 210 | "orange": { 211 | "bg-alt": { 212 | "$type": "color", 213 | "$value": "{mode.color.colorcode.orange.deep}" 214 | }, 215 | "bg-default": { 216 | "$type": "color", 217 | "$value": "{mode.color.colorcode.orange.muted}" 218 | }, 219 | "border": { 220 | "$type": "color", 221 | "$value": "{mode.color.colorcode.orange.deep}" 222 | }, 223 | "pattern": { 224 | "$type": "color", 225 | "$value": "{mode.color.colorcode.orange.deep}" 226 | } 227 | }, 228 | "red": { 229 | "bg-alt": { 230 | "$type": "color", 231 | "$value": "{mode.color.colorcode.red.deep}" 232 | }, 233 | "bg-default": { 234 | "$type": "color", 235 | "$value": "{mode.color.colorcode.red.muted}" 236 | }, 237 | "border": { 238 | "$type": "color", 239 | "$value": "{mode.color.colorcode.red.deep}" 240 | }, 241 | "pattern": { 242 | "$type": "color", 243 | "$value": "{mode.color.colorcode.red.deep}" 244 | } 245 | }, 246 | "pink": { 247 | "bg-alt": { 248 | "$type": "color", 249 | "$value": "{mode.color.colorcode.pink.deep}" 250 | }, 251 | "bg-default": { 252 | "$type": "color", 253 | "$value": "{mode.color.colorcode.pink.muted}" 254 | }, 255 | "border": { 256 | "$type": "color", 257 | "$value": "{mode.color.colorcode.pink.deep}" 258 | }, 259 | "pattern": { 260 | "$type": "color", 261 | "$value": "{mode.color.colorcode.pink.deep}" 262 | } 263 | }, 264 | "purple": { 265 | "bg-alt": { 266 | "$type": "color", 267 | "$value": "{mode.color.colorcode.purple.deep}" 268 | }, 269 | "bg-default": { 270 | "$type": "color", 271 | "$value": "{mode.color.colorcode.purple.muted}" 272 | }, 273 | "border": { 274 | "$type": "color", 275 | "$value": "{mode.color.colorcode.purple.deep}" 276 | }, 277 | "pattern": { 278 | "$type": "color", 279 | "$value": "{mode.color.colorcode.purple.deep}" 280 | } 281 | }, 282 | "gray": { 283 | "bg-alt": { 284 | "$type": "color", 285 | "$value": "{mode.color.colorcode.gray.deep}" 286 | }, 287 | "bg-default": { 288 | "$type": "color", 289 | "$value": "{mode.color.colorcode.gray.muted}" 290 | }, 291 | "border": { 292 | "$type": "color", 293 | "$value": "{mode.color.colorcode.gray.deep}" 294 | }, 295 | "pattern": { 296 | "$type": "color", 297 | "$value": "{mode.color.colorcode.gray.deep}" 298 | } 299 | }, 300 | "slate": { 301 | "bg-alt": { 302 | "$type": "color", 303 | "$value": "{mode.color.colorcode.slate.deep}" 304 | }, 305 | "bg-default": { 306 | "$type": "color", 307 | "$value": "{mode.color.colorcode.slate.muted}" 308 | }, 309 | "border": { 310 | "$type": "color", 311 | "$value": "{mode.color.colorcode.slate.deep}" 312 | }, 313 | "pattern": { 314 | "$type": "color", 315 | "$value": "{mode.color.colorcode.slate.deep}" 316 | } 317 | } 318 | } 319 | } 320 | } -------------------------------------------------------------------------------- /scripts/icons.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2025 The Sage Group plc or its licensors. All Rights reserved 3 | */ 4 | 5 | import Figma from 'figma-js' 6 | import fetch from 'node-fetch' 7 | import fs from 'fs-extra' 8 | import { FontAssetType, generateFonts } from 'fantasticon' 9 | import { 10 | join, 11 | relative, 12 | resolve, 13 | sep, 14 | posix 15 | } from 'path' 16 | import kebabCase from 'lodash/kebabCase.js' 17 | import pick from 'lodash/pick.js' 18 | 19 | import { UniqueValues } from './utils/unique-values.js' 20 | import { Collect } from './utils/collect.js' 21 | 22 | interface IConfig { 23 | personalAccessToken: string | undefined 24 | fileId: string | undefined 25 | pages: string[] 26 | multipleSets: boolean 27 | distDir: string 28 | svgDir: string 29 | fontsDir: string 30 | dataDir: string 31 | fontName: string 32 | formats: FontAssetType[] 33 | mainTemplate: string 34 | docsDir: string 35 | docsPartials: string 36 | meta: IMeta 37 | } 38 | 39 | interface IMeta { 40 | description: string 41 | url: string 42 | copyright: string 43 | version: string 44 | } 45 | 46 | export interface IIcon { 47 | url: string | undefined 48 | unicode?: string 49 | description?: string 50 | key?: string 51 | name?: string 52 | id: any 53 | path?: string 54 | set: string | undefined 55 | svg?: string 56 | } 57 | 58 | export interface IGlyphData { 59 | name: string | undefined 60 | path?: string 61 | codepoint?: number 62 | glyph?: string 63 | unicode?: string 64 | url?: string | undefined 65 | description?: string 66 | key?: string 67 | id?: any 68 | set?: string | undefined 69 | svg?: string 70 | } 71 | 72 | const getDataFromDescription = (description: string) => { 73 | const output: { unicode?: string, description?:string } = {} 74 | const unicodeMatch = description.match(/code: ?([a-zA-Z0-9]{4})\s?/) 75 | 76 | if (unicodeMatch && unicodeMatch[1]) { 77 | output.unicode = unicodeMatch[1] 78 | output.description = description.replace(unicodeMatch[0], '') 79 | } 80 | 81 | return output 82 | } 83 | 84 | async function getIconsArray (config: IConfig): Promise { 85 | 86 | if (!config.fileId || !config.personalAccessToken) { 87 | throw new Error( 88 | `'FIGMA_ACCESS_TOKEN or FIGMA_FILE_ID not defined.\r\n'` 89 | ); 90 | } 91 | 92 | console.log(`Fetching icon components information from Figma file ${config.fileId}...`) 93 | const client = Figma.Client({ personalAccessToken: config.personalAccessToken }) 94 | 95 | const components = await client.file(config.fileId).then(({ data }) => { 96 | console.log(` File name: ${data.name}`) 97 | console.log(` Last modified: ${data.lastModified}`) 98 | 99 | const components = data.components 100 | const componentIds = Object.keys(components) 101 | let canvases = data.document.children.filter(node => node.type === 'CANVAS') 102 | if (config.pages && config.pages.length > 0) { 103 | canvases = canvases.filter(node => config.pages.includes(node.name)) 104 | } 105 | const verifyFn = (node: Figma.Node) => componentIds.includes(node?.id) 106 | 107 | const groupedComponents = Object.fromEntries(canvases.map(canvas => [canvas.name, Collect({object: canvas, callback: verifyFn}).map(node => node.id)])) 108 | 109 | return Object.entries(groupedComponents) 110 | .map(([set, ids]) => ids.map(id => { 111 | const currentComponent = components[id] 112 | 113 | if (!currentComponent) { 114 | throw new Error( 115 | `Component ${id} is undefined` 116 | ); 117 | } 118 | 119 | const output = { 120 | id, 121 | set: config.multipleSets ? set : undefined, 122 | ...currentComponent, 123 | ...getDataFromDescription(currentComponent.description) 124 | } 125 | 126 | return output 127 | })) 128 | .flat() 129 | }) 130 | 131 | return await client.fileImages(config.fileId, { 132 | ids: components.map(component => component.id), 133 | format: 'svg', 134 | scale: 4 135 | }).then(({ data }) => components.map((component) => ({ 136 | ...component, 137 | url: data.images[component.id] 138 | }))) 139 | } 140 | 141 | async function fetchIconData (iconsList: IIcon[]) { 142 | console.log('Fetching icons svg data...') 143 | return await Promise.all( 144 | iconsList.map(async (icon) => { 145 | 146 | if (!icon.url) { 147 | throw new Error( 148 | `Icon ${icon.name} has undefined url` 149 | ); 150 | } 151 | 152 | return { 153 | ...icon, 154 | svg: await fetch(icon.url).then(response => { 155 | console.log(` - Fetched ${icon.name} svg data from ${icon.url}`) 156 | return response.text() 157 | }) 158 | } 159 | }) 160 | ).then((data) => { 161 | console.log('Done.\r\n') 162 | return data 163 | }) 164 | } 165 | 166 | async function writeIconsToSvg (iconsList: IIcon[], config: IConfig) { 167 | console.log('Writing svg icons data to files...') 168 | return await Promise.all( 169 | iconsList.map(async (icon) => { 170 | const filePath = config.multipleSets 171 | ? join(config.svgDir, kebabCase(icon.set), `${kebabCase(icon.name)}.svg`) 172 | : join(config.svgDir, `${kebabCase(icon.name)}.svg`) 173 | 174 | if (!icon.svg) { 175 | throw new Error( 176 | `Icon ${icon.name} has undefined svg` 177 | ); 178 | } 179 | 180 | return await fs.outputFile(filePath, icon.svg).then(() => { 181 | console.log(` - ${icon.name} written to ${filePath}`) 182 | return { 183 | ...icon, 184 | path: filePath 185 | } 186 | }) 187 | }) 188 | ).then((data) => { 189 | console.log('Done.\r\n') 190 | return data 191 | }) 192 | } 193 | 194 | async function createWebFonts (iconsList: IIcon[], config: IConfig) { 195 | console.log('Writing font files...') 196 | console.log(` Using formats: ${config.formats.join(', ')} \r\n`) 197 | 198 | const sets = config.multipleSets ? UniqueValues({array: iconsList, mapFn: item => item.set}) : [''] 199 | return await Promise.all(sets.map(async (set) => { 200 | const icons = config.multipleSets ? iconsList.filter(icon => icon.set === set) : iconsList 201 | const name = config.multipleSets ? `${config.fontName}-${set}` : config.fontName 202 | const inputDir = config.multipleSets ? resolve('.', config.svgDir, kebabCase(set)) : resolve('.', config.svgDir) 203 | const outputDir = resolve('.', config.fontsDir) 204 | const codepoints = Object.fromEntries(icons.filter(icon => !!icon.unicode).map(icon => { 205 | if (!icon.unicode) { 206 | throw new Error( 207 | `Unicode is undefined for icon ${icon.name}` 208 | ); 209 | } 210 | return [kebabCase(icon.name), parseInt(icon.unicode, 16)] 211 | })) 212 | 213 | await fs.ensureDir(outputDir) 214 | 215 | return generateFonts({ 216 | name, 217 | inputDir, 218 | outputDir, 219 | codepoints, 220 | fontTypes: config.formats, 221 | formatOptions: { 222 | ttf: config.meta 223 | }, 224 | assetTypes: [], 225 | fontHeight: 1000 226 | }).then((results) => { 227 | console.log(` - ${name} set formats written in ${outputDir} in ${config.formats.length} formats`) 228 | 229 | const { codepoints } = results 230 | 231 | return icons.map(icon => { 232 | const codepoint = codepoints[kebabCase(icon.name)] 233 | 234 | if (!codepoint) { 235 | throw new Error( 236 | `Codepoint is undefined for icon ${icon.name}` 237 | ); 238 | } 239 | 240 | if (!icon.path) { 241 | throw new Error( 242 | `Path is undefined for icon ${icon.name}` 243 | ); 244 | } 245 | 246 | return { 247 | ...icon, 248 | path: relative(config.distDir, icon.path).split(sep).join(posix.sep), 249 | codepoint, 250 | glyph: String.fromCodePoint(codepoint), 251 | unicode: String.fromCodePoint(codepoint).charCodeAt(0).toString(16) 252 | } 253 | }) 254 | }) 255 | })).then((results) => { 256 | console.log('Done.\r\n') 257 | return results 258 | }) 259 | } 260 | 261 | async function writeGlyphsData (glyphsData: IGlyphData[], config: IConfig) { 262 | console.log('Writing glyph data...') 263 | const glyphsDataFilePath = resolve(config.dataDir, 'glyphs.json') 264 | if (config.multipleSets) { 265 | const sets = UniqueValues({array: glyphsData, mapFn: item => item.set}) 266 | console.log(` Writing data for ${glyphsData.length} icons in ${sets.length} sets`) 267 | } else { 268 | console.log(` Writing data for ${glyphsData.length} icons in 1 set`) 269 | } 270 | 271 | await fs.outputJson(glyphsDataFilePath, glyphsData, { spaces: 2 }).then(() => { 272 | console.log(` - glyphs data in file: ${glyphsDataFilePath}`) 273 | console.log('Done.\r\n') 274 | }) 275 | } 276 | 277 | /** 278 | * @typedef {Object} IconsConfig 279 | * @property {string} personalAccessToken - personal access token for figma 280 | * @property {string} fileId - id of a figma file 281 | * @property {string[]} pages - names of the pages that are containing icons 282 | * @property {boolean} multipleSets - if multiple sets should be created. If true, then each page will be different set. 283 | * @property {string} distDir - main output directory 284 | * @property {string} svgDir - directory for svg files 285 | * @property {string} fontsDir - directory for font files 286 | * @property {string} dataDir - directory for JSON file 287 | * @property {string} docsDir - output file for icon docs 288 | * @property {string} fontName - name of the font 289 | * @property {array} formats - formats to be generated ('svg', 'ttf', 'woff', 'woff2', 'eot')as in th 290 | * @property {string} mainTemplate - path to handlebars template for docs 291 | * @property {string} docsPartials - glob to partials for tokens documentation 292 | * @property {object} meta - meta information for font files. 293 | * @property {string} meta.description - font files description 294 | * @property {string} meta.url - url for a font manufacturer 295 | * @property {string} meta.copyright - copyright information 296 | * @property {string} meta.version - font version number 297 | */ 298 | 299 | /** 300 | * @param {IconsConfig} config - config for icons generator 301 | * @returns {Promise} 302 | */ 303 | export const Icons = async (config: IConfig) => { 304 | if (!config.fileId || !config.personalAccessToken) { 305 | console.error('Icons will not be generated, since token and figma file id were not found.') 306 | console.error('Please provide FIGMA_ACCESS_TOKEN and FIGMA_FILE_ID env variables or in .env file.\r\n') 307 | return 308 | } 309 | 310 | fs.removeSync(config.svgDir) 311 | fs.removeSync(config.dataDir) 312 | fs.removeSync(config.fontsDir) 313 | 314 | const iconsList = await getIconsArray(config) 315 | const iconsData = await fetchIconData(iconsList) 316 | const svgIcons = await writeIconsToSvg(iconsData, config) 317 | const glyphsData = await createWebFonts(svgIcons, config) 318 | 319 | const formattedGlyphsData = glyphsData.flat() 320 | .map((icon) => pick(icon, ['name', 'set', 'description', 'documentationLinks', 'path', 'codepoint', 'glyph', 'unicode'])) 321 | .map((icon) => ({ 322 | ...icon, 323 | name: icon.name 324 | })) 325 | 326 | await writeGlyphsData(formattedGlyphsData, config) 327 | 328 | } 329 | -------------------------------------------------------------------------------- /data/tokens/components/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "profile": { 3 | "size": { 4 | "outside": { 5 | "XS": { 6 | "$type": "sizing", 7 | "$value": "{global.size.XS}", 8 | "$description": "XS Portraits" 9 | }, 10 | "S": { 11 | "$type": "sizing", 12 | "$value": "{global.size.S}", 13 | "$description": "S Portraits" 14 | }, 15 | "M": { 16 | "$type": "sizing", 17 | "$value": "{global.size.M}", 18 | "$description": "M Portraits" 19 | }, 20 | "ML": { 21 | "$type": "sizing", 22 | "$value": "56", 23 | "$description": "L Portraits" 24 | }, 25 | "L": { 26 | "$type": "sizing", 27 | "$value": "{global.size.4XL}", 28 | "$description": "XL Portraits" 29 | }, 30 | "XL": { 31 | "$type": "sizing", 32 | "$value": "104", 33 | "$description": "XL Portraits" 34 | }, 35 | "XXL": { 36 | "$type": "sizing", 37 | "$value": "128", 38 | "$description": "XXL Portraits" 39 | } 40 | }, 41 | "inside": { 42 | "XS": { 43 | "$type": "sizing", 44 | "$value": "{global.size.3XS}", 45 | "$description": "XS Portrait icons" 46 | }, 47 | "S": { 48 | "$type": "sizing", 49 | "$value": "{global.size.2XS}", 50 | "$description": "S Portrait icons" 51 | }, 52 | "M": { 53 | "$type": "sizing", 54 | "$value": "{global.size.XS}", 55 | "$description": "M Portrait icons" 56 | }, 57 | "ML": { 58 | "$type": "sizing", 59 | "$value": "{global.size.S}", 60 | "$description": "L Portrait icons" 61 | }, 62 | "L": { 63 | "$type": "sizing", 64 | "$value": "{global.size.L}", 65 | "$description": "XL Portrait icons" 66 | }, 67 | "XL": { 68 | "$type": "sizing", 69 | "$value": "{global.size.2XL}", 70 | "$description": "XL Portrait icons" 71 | }, 72 | "XXL": { 73 | "$type": "sizing", 74 | "$value": "{global.size.4XL}", 75 | "$description": "XXL Portraits" 76 | } 77 | } 78 | }, 79 | "bg-alt": { 80 | "$type": "color", 81 | "$value": "{mode.color.generic.txt.severe}", 82 | "$description": "For portrait image" 83 | }, 84 | "bg-def": { 85 | "$type": "color", 86 | "$value": "{mode.color.generic.bg.nought}" 87 | }, 88 | "border-default": { 89 | "$type": "color", 90 | "$value": "{mode.color.generic.fg.moderate}" 91 | }, 92 | "label-default": { 93 | "$type": "color", 94 | "$value": "{mode.color.generic.txt.severe}" 95 | }, 96 | "swatches": { 97 | "blue": { 98 | "bg-default": { 99 | "$type": "color", 100 | "$value": "{mode.color.status.custom.blue.defaultAlt}" 101 | }, 102 | "label-default": { 103 | "$type": "color", 104 | "$value": "{mode.color.status.custom.blue.default}" 105 | } 106 | }, 107 | "teal": { 108 | "bg-default": { 109 | "$type": "color", 110 | "$value": "{mode.color.status.custom.teal.defaultAlt}" 111 | }, 112 | "label-default": { 113 | "$type": "color", 114 | "$value": "{mode.color.status.custom.teal.default}" 115 | } 116 | }, 117 | "green": { 118 | "bg-default": { 119 | "$type": "color", 120 | "$value": "{mode.color.status.custom.green.defaultAlt}" 121 | }, 122 | "label-default": { 123 | "$type": "color", 124 | "$value": "{mode.color.status.custom.green.default}" 125 | } 126 | }, 127 | "lime": { 128 | "bg-default": { 129 | "$type": "color", 130 | "$value": "{mode.color.status.custom.lime.defaultAlt}" 131 | }, 132 | "label-default": { 133 | "$type": "color", 134 | "$value": "{mode.color.status.custom.lime.default}" 135 | } 136 | }, 137 | "orange": { 138 | "bg-default": { 139 | "$type": "color", 140 | "$value": "{mode.color.status.custom.orange.defaultAlt}" 141 | }, 142 | "label-default": { 143 | "$type": "color", 144 | "$value": "{mode.color.status.custom.orange.txt}", 145 | "$description": " " 146 | } 147 | }, 148 | "red": { 149 | "bg-default": { 150 | "$type": "color", 151 | "$value": "{mode.color.status.custom.red.defaultAlt}" 152 | }, 153 | "label-default": { 154 | "$type": "color", 155 | "$value": "{mode.color.status.custom.red.default}" 156 | } 157 | }, 158 | "pink": { 159 | "bg-default": { 160 | "$type": "color", 161 | "$value": "{mode.color.status.custom.pink.defaultAlt}" 162 | }, 163 | "label-default": { 164 | "$type": "color", 165 | "$value": "{mode.color.status.custom.pink.default}" 166 | } 167 | }, 168 | "purple": { 169 | "bg-default": { 170 | "$type": "color", 171 | "$value": "{mode.color.status.custom.purple.defaultAlt}" 172 | }, 173 | "label-default": { 174 | "$type": "color", 175 | "$value": "{mode.color.status.custom.purple.default}" 176 | } 177 | }, 178 | "slate": { 179 | "bg-default": { 180 | "$type": "color", 181 | "$value": "{mode.color.status.custom.slate.defaultAlt}" 182 | }, 183 | "label-default": { 184 | "$type": "color", 185 | "$value": "{mode.color.status.custom.slate.default}" 186 | } 187 | }, 188 | "gray": { 189 | "bg-default": { 190 | "$type": "color", 191 | "$value": "{mode.color.status.custom.gray.defaultAlt}" 192 | }, 193 | "label-default": { 194 | "$type": "color", 195 | "$value": "{mode.color.status.custom.gray.default}" 196 | } 197 | } 198 | }, 199 | "font": { 200 | "initials": { 201 | "XS": { 202 | "$type": "typography", 203 | "$value": "{global.font.static.comp.placeholder.XS}" 204 | }, 205 | "S": { 206 | "$type": "typography", 207 | "$value": "{global.font.static.comp.placeholder.S}" 208 | }, 209 | "M": { 210 | "$type": "typography", 211 | "$value": "{global.font.static.comp.placeholder.M}" 212 | }, 213 | "ML": { 214 | "$type": "typography", 215 | "$value": "{global.font.static.comp.placeholder.ML}" 216 | }, 217 | "L": { 218 | "$type": "typography", 219 | "$value": "{global.font.static.comp.placeholder.L}" 220 | }, 221 | "XL": { 222 | "$type": "typography", 223 | "$value": "{global.font.static.comp.placeholder.XL}" 224 | }, 225 | "XXL": { 226 | "$type": "typography", 227 | "$value": "{global.font.static.comp.placeholder.XXL}" 228 | } 229 | }, 230 | "def": { 231 | "XS": { 232 | "$type": "typography", 233 | "$value": "{global.font.static.comp.regular.S}" 234 | }, 235 | "S": { 236 | "$type": "typography", 237 | "$value": "{global.font.static.comp.regular.M}" 238 | }, 239 | "M": { 240 | "$type": "typography", 241 | "$value": "{global.font.static.comp.regular.M}" 242 | }, 243 | "ML": { 244 | "$type": "typography", 245 | "$value": "{global.font.static.comp.regular.M}" 246 | }, 247 | "L": { 248 | "$type": "typography", 249 | "$value": "{global.font.static.comp.regular.L}" 250 | }, 251 | "XL": { 252 | "$type": "typography", 253 | "$value": "{global.font.static.comp.regular.L}" 254 | }, 255 | "XXL": { 256 | "$type": "typography", 257 | "$value": "{global.font.static.comp.regular.L}" 258 | } 259 | }, 260 | "heading": { 261 | "XS": { 262 | "$type": "typography", 263 | "$value": "{global.font.static.comp.medium.S}" 264 | }, 265 | "S": { 266 | "$type": "typography", 267 | "$value": "{global.font.static.comp.medium.M}" 268 | }, 269 | "M": { 270 | "$type": "typography", 271 | "$value": "{global.font.static.subheading.L}" 272 | }, 273 | "ML": { 274 | "$type": "typography", 275 | "$value": "{global.font.static.subheading.L}" 276 | }, 277 | "L": { 278 | "$type": "typography", 279 | "$value": "{global.font.static.subheading.L}" 280 | }, 281 | "XL": { 282 | "$type": "typography", 283 | "$value": "{global.font.static.heading.M}" 284 | }, 285 | "XXL": { 286 | "$type": "typography", 287 | "$value": "{global.font.static.heading.L}" 288 | } 289 | }, 290 | "fluid": { 291 | "initials": { 292 | "XS": { 293 | "$type": "typography", 294 | "$value": "{global.font.fluid.comp.placeholder.XS}" 295 | }, 296 | "S": { 297 | "$type": "typography", 298 | "$value": "{global.font.fluid.comp.placeholder.S}", 299 | "$description": "Small Viewports: 16, Large Viewports: 16" 300 | }, 301 | "M": { 302 | "$type": "typography", 303 | "$value": "{global.font.fluid.comp.placeholder.M}", 304 | "$description": "Small Viewports: 18.66, Large Viewports: 21.53" 305 | }, 306 | "ML": { 307 | "$type": "typography", 308 | "$value": "{global.font.fluid.comp.placeholder.ML}", 309 | "$description": "Small Viewports: 21.77, Large Viewports: 28.97" 310 | }, 311 | "L": { 312 | "$type": "typography", 313 | "$value": "{global.font.fluid.comp.placeholder.L}", 314 | "$description": "Small Viewports: 25.39, Large Viewports: 38.98" 315 | }, 316 | "XL": { 317 | "$type": "typography", 318 | "$value": "{global.font.fluid.comp.placeholder.XL}", 319 | "$description": "Small Viewports: 29.61, Large Viewports: 52.45" 320 | }, 321 | "XXL": { 322 | "$type": "typography", 323 | "$value": "{global.font.fluid.comp.placeholder.XXL}", 324 | "$description": "Small Viewports: 34.54, Large Viewports: 70.58" 325 | } 326 | }, 327 | "def": { 328 | "XS": { 329 | "$type": "typography", 330 | "$value": "{global.font.fluid.comp.regular.S}" 331 | }, 332 | "S": { 333 | "$type": "typography", 334 | "$value": "{global.font.fluid.comp.regular.M}" 335 | }, 336 | "M": { 337 | "$type": "typography", 338 | "$value": "{global.font.fluid.comp.regular.M}" 339 | }, 340 | "ML": { 341 | "$type": "typography", 342 | "$value": "{global.font.fluid.comp.regular.M}" 343 | }, 344 | "L": { 345 | "$type": "typography", 346 | "$value": "{global.font.fluid.comp.regular.L}" 347 | }, 348 | "XL": { 349 | "$type": "typography", 350 | "$value": "{global.font.fluid.comp.regular.L}" 351 | }, 352 | "XXL": { 353 | "$type": "typography", 354 | "$value": "{global.font.fluid.comp.regular.L}" 355 | } 356 | }, 357 | "heading": { 358 | "XS": { 359 | "$type": "typography", 360 | "$value": "{global.font.fluid.comp.medium.S}" 361 | }, 362 | "S": { 363 | "$type": "typography", 364 | "$value": "{global.font.fluid.comp.medium.M}" 365 | }, 366 | "M": { 367 | "$type": "typography", 368 | "$value": "{global.font.fluid.subheading.L}" 369 | }, 370 | "ML": { 371 | "$type": "typography", 372 | "$value": "{global.font.fluid.subheading.L}" 373 | }, 374 | "L": { 375 | "$type": "typography", 376 | "$value": "{global.font.fluid.subheading.L}" 377 | }, 378 | "XL": { 379 | "$type": "typography", 380 | "$value": "{global.font.fluid.heading.M}" 381 | }, 382 | "XXL": { 383 | "$type": "typography", 384 | "$value": "{global.font.fluid.heading.L}" 385 | } 386 | } 387 | } 388 | } 389 | } 390 | } -------------------------------------------------------------------------------- /data/tokens/components/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "calendar": { 4 | "bg-active": { 5 | "$type": "color", 6 | "$value": "{mode.color.action.grayscale.active}" 7 | }, 8 | "bg-disabled": { 9 | "$type": "color", 10 | "$value": "{mode.color.action.inactive.defaultAlt}" 11 | }, 12 | "bg-duration": { 13 | "$type": "color", 14 | "$value": "{mode.color.action.dataEntry.hoverAlt}" 15 | }, 16 | "bg-hover": { 17 | "$type": "color", 18 | "$value": "{mode.color.action.dataEntry.hoverAlt}" 19 | }, 20 | "border-duration": { 21 | "$type": "color", 22 | "$value": "{mode.color.action.grayscale.active}" 23 | }, 24 | "txt-active": { 25 | "$type": "color", 26 | "$value": "{mode.color.action.grayscale.withActive}" 27 | }, 28 | "txt-alt": { 29 | "$type": "color", 30 | "$value": "{mode.color.action.dataEntry.txtAlt}", 31 | "$description": "Days of the week subheaders" 32 | }, 33 | "txt-disabled": { 34 | "$type": "color", 35 | "$value": "{mode.color.action.inactive.txt}" 36 | }, 37 | "txt-duration": { 38 | "$type": "color", 39 | "$value": "{mode.color.action.dataEntry.txt}" 40 | }, 41 | "txt-default": { 42 | "$type": "color", 43 | "$value": "{mode.color.action.dataEntry.txt}" 44 | }, 45 | "txt-hover": { 46 | "$type": "color", 47 | "$value": "{mode.color.action.grayscale.withHover}" 48 | }, 49 | "txtAlt-default": { 50 | "$type": "color", 51 | "$value": "{mode.color.action.dataEntry.txtAlt}" 52 | } 53 | }, 54 | "dropdown": { 55 | "bg-hover": { 56 | "$type": "color", 57 | "$value": "{mode.color.action.dataEntry.hoverAlt}", 58 | "$description": "REF POPOVER " 59 | }, 60 | "label-active": { 61 | "$type": "color", 62 | "$value": "{mode.color.action.grayscale.withActive}" 63 | }, 64 | "label-alt": { 65 | "$type": "color", 66 | "$value": "{mode.color.action.dataEntry.txt}" 67 | }, 68 | "label-disabled": { 69 | "$type": "color", 70 | "$value": "{mode.color.action.inactive.txt}" 71 | }, 72 | "label-default": { 73 | "$type": "color", 74 | "$value": "{mode.color.action.dataEntry.txtAlt}" 75 | }, 76 | "label-hover": { 77 | "$type": "color", 78 | "$value": "{mode.color.action.grayscale.withHover}" 79 | }, 80 | "label-subtxt": { 81 | "$type": "color", 82 | "$value": "{mode.color.action.dataEntry.txtAlt}", 83 | "$description": "Option subtxt." 84 | } 85 | }, 86 | "labelset": { 87 | "label-required": { 88 | "$type": "color", 89 | "$value": "{mode.color.action.danger.default}" 90 | }, 91 | "label-default": { 92 | "$type": "color", 93 | "$value": "{mode.color.action.dataEntry.txt}" 94 | }, 95 | "label-alt": { 96 | "$type": "color", 97 | "$value": "{mode.color.action.dataEntry.txtAlt}", 98 | "$description": "used for '(optional)' txt and hint txt." 99 | }, 100 | "label-disabled": { 101 | "$type": "color", 102 | "$value": "{mode.color.action.inactive.txt}" 103 | }, 104 | "label-readOnly": { 105 | "$type": "color", 106 | "$value": "{mode.color.action.dataEntry.txt}" 107 | }, 108 | "inverse": { 109 | "label-required": { 110 | "$type": "color", 111 | "$value": "{mode.color.action.danger.inverse.default}" 112 | }, 113 | "label-default": { 114 | "$type": "color", 115 | "$value": "{mode.color.action.dataEntry.inverse.txt}" 116 | }, 117 | "label-alt": { 118 | "$type": "color", 119 | "$value": "{mode.color.action.dataEntry.inverse.txtAlt}", 120 | "$description": "used for '(optional)' txt and hint txt." 121 | } 122 | } 123 | }, 124 | "typical": { 125 | "bg-alt": { 126 | "$type": "color", 127 | "$value": "{mode.color.action.dataEntry.defaultAlt}", 128 | "$description": "progress bar bg, txt editor preview and txt editor footer" 129 | }, 130 | "bg-disabled": { 131 | "$type": "color", 132 | "$value": "{mode.color.action.inactive.defaultAlt}" 133 | }, 134 | "bg-default": { 135 | "$type": "color", 136 | "$value": "{mode.color.action.dataEntry.default}" 137 | }, 138 | "bg-hover": { 139 | "$type": "color", 140 | "$value": "{mode.color.action.dataEntry.hoverAlt}" 141 | }, 142 | "bg-readOnly": { 143 | "$type": "color", 144 | "$value": "{mode.color.action.inactive.defaultAlt}" 145 | }, 146 | "border-alt": { 147 | "$type": "color", 148 | "$value": "{mode.color.action.inactive.default}" 149 | }, 150 | "border-default": { 151 | "$type": "color", 152 | "$value": "{mode.color.action.dataEntry.withDefault}" 153 | }, 154 | "border-disabled": { 155 | "$type": "color", 156 | "$value": "{mode.color.action.inactive.default}" 157 | }, 158 | "border-hover": { 159 | "$type": "color", 160 | "$value": "{mode.color.action.dataEntry.withHover}", 161 | "$description": "File input draggable border state" 162 | }, 163 | "border-readOnly": { 164 | "$type": "color", 165 | "$value": "{mode.color.action.inactive.default}" 166 | }, 167 | "icon-active": { 168 | "$type": "color", 169 | "$value": "{mode.color.action.dataEntry.withActive}", 170 | "$description": "used on tick in color picker and selected tick on dropdowns" 171 | }, 172 | "icon-default": { 173 | "$type": "color", 174 | "$value": "{mode.color.action.dataEntry.txt}" 175 | }, 176 | "icon-disabled": { 177 | "$type": "color", 178 | "$value": "{mode.color.action.inactive.icon}" 179 | }, 180 | "icon-hover": { 181 | "$type": "color", 182 | "$value": "{mode.color.action.dataEntry.withHover}" 183 | }, 184 | "icon-readOnly": { 185 | "$type": "color", 186 | "$value": "{mode.color.action.inactive.iconAlt}" 187 | }, 188 | "iconAlt-active": { 189 | "$type": "color", 190 | "$value": "{mode.color.action.dataEntry.withActiveAlt}", 191 | "$description": "used on tick in color picker" 192 | }, 193 | "txt-active": { 194 | "$type": "color", 195 | "$value": "{mode.color.action.dataEntry.txt}" 196 | }, 197 | "txt-alt": { 198 | "$type": "color", 199 | "$value": "{mode.color.action.dataEntry.txtAlt}" 200 | }, 201 | "txt-default": { 202 | "$type": "color", 203 | "$value": "{mode.color.action.dataEntry.txt}" 204 | }, 205 | "txt-disabled": { 206 | "$type": "color", 207 | "$value": "{mode.color.action.inactive.txt}" 208 | }, 209 | "txt-hover": { 210 | "$type": "color", 211 | "$value": "{mode.color.action.dataEntry.withHover}", 212 | "$description": "txt hover state when dragging a file over file input" 213 | }, 214 | "txt-readOnly": { 215 | "$type": "color", 216 | "$value": "{mode.color.generic.txt.severe}" 217 | }, 218 | "inverse": { 219 | "bg-default": { 220 | "$type": "color", 221 | "$value": "{mode.color.action.dataEntry.inverse.default}" 222 | }, 223 | "border-default": { 224 | "$type": "color", 225 | "$value": "{mode.color.action.dataEntry.inverse.withDefault}" 226 | }, 227 | "txt-default": { 228 | "$type": "color", 229 | "$value": "{mode.color.action.dataEntry.inverse.txt}" 230 | } 231 | } 232 | }, 233 | "switch": { 234 | "bg-active": { 235 | "$type": "color", 236 | "$value": "{mode.color.action.grayscale.active}", 237 | "$description": "background of switch in active state" 238 | }, 239 | "bg-activateDisabled": { 240 | "$type": "color", 241 | "$value": "{mode.color.action.inactive.default}", 242 | "$description": "background of switch in active when disabled" 243 | }, 244 | "bg-disabled": { 245 | "$type": "color", 246 | "$value": "{mode.color.action.inactive.defaultAlt}", 247 | "$description": "background of switch when disabled in off state" 248 | }, 249 | "bg-default": { 250 | "$type": "color", 251 | "$value": "{mode.color.action.dataEntry.default}", 252 | "$description": "background of switch in off state" 253 | }, 254 | "border-active": { 255 | "$type": "color", 256 | "$value": "transparent", 257 | "$description": "border of switch in active state" 258 | }, 259 | "border-activeDisabled": { 260 | "$type": "color", 261 | "$value": "transparent", 262 | "$description": "border of switch inactive and disabled state" 263 | }, 264 | "border-disabled": { 265 | "$type": "color", 266 | "$value": "{mode.color.action.inactive.default}", 267 | "$description": "border of disabled switch in off state" 268 | }, 269 | "border-default": { 270 | "$type": "color", 271 | "$value": "{mode.color.action.dataEntry.withDefault}", 272 | "$description": "border of switch in off state" 273 | }, 274 | "fg-active": { 275 | "$type": "color", 276 | "$value": "{mode.color.action.grayscale.withActive}", 277 | "$description": "switch knob in active state" 278 | }, 279 | "fg-activateDisabled": { 280 | "$type": "color", 281 | "$value": "{mode.color.action.inactive.txtAlt}", 282 | "$description": "switch knob in active but disabled state." 283 | }, 284 | "fg-disabled": { 285 | "$type": "color", 286 | "$value": "{mode.color.action.inactive.default}", 287 | "$description": "switch knob in off disabled state" 288 | }, 289 | "fg-default": { 290 | "$type": "color", 291 | "$value": "{mode.color.action.dataEntry.withDefault}", 292 | "$description": "switch knob in off state" 293 | }, 294 | "label-active": { 295 | "$type": "color", 296 | "$value": "{mode.color.action.dataEntry.txt}", 297 | "$description": "label to right of switch when switch is active" 298 | }, 299 | "label-activateDisabled": { 300 | "$type": "color", 301 | "$value": "{mode.color.action.inactive.default}", 302 | "$description": "label to right of switch when switch is active but disabled" 303 | }, 304 | "label-disabled": { 305 | "$type": "color", 306 | "$value": "{mode.color.action.inactive.default}", 307 | "$description": "label to right of switch when switch is off but disabled" 308 | }, 309 | "label-default": { 310 | "$type": "color", 311 | "$value": "{mode.color.action.dataEntry.txtAlt}", 312 | "$description": "label to right of switch when switch is off " 313 | } 314 | }, 315 | "validation": { 316 | "border-error": { 317 | "$type": "color", 318 | "$value": "{mode.color.status.negative.default}" 319 | }, 320 | "bar-error": { 321 | "$type": "color", 322 | "$value": "{mode.color.status.negative.default}", 323 | "$description": "error bar to left of inputs" 324 | }, 325 | "bar-warn": { 326 | "$type": "color", 327 | "$value": "{mode.color.status.warning.default}", 328 | "$description": "warning bar to left of inputs" 329 | }, 330 | "label-error": { 331 | "$type": "color", 332 | "$value": "{mode.color.status.negative.default}" 333 | }, 334 | "label-success": { 335 | "$type": "color", 336 | "$value": "{mode.color.status.positive.default}" 337 | }, 338 | "label-warn": { 339 | "$type": "color", 340 | "$value": "{mode.color.status.warning.txt}" 341 | }, 342 | "inverse": { 343 | "border-error": { 344 | "$type": "color", 345 | "$value": "{mode.color.status.negative.inverse.default}" 346 | }, 347 | "bar-error": { 348 | "$type": "color", 349 | "$value": "{mode.color.status.negative.inverse.default}", 350 | "$description": "error bar to left of inputs" 351 | }, 352 | "label-error": { 353 | "$type": "color", 354 | "$value": "{mode.color.status.negative.inverse.default}" 355 | } 356 | } 357 | }, 358 | "size": { 359 | "generic": { 360 | "minW": { 361 | "$type": "sizing", 362 | "$value": "288", 363 | "$description": "optional generic minWidth size used across some inputs such as file input." 364 | } 365 | } 366 | } 367 | } 368 | } -------------------------------------------------------------------------------- /data/tokens/components/container.json: -------------------------------------------------------------------------------- 1 | { 2 | "container": { 3 | "action": { 4 | "bgFooter-activated": { 5 | "$type": "color", 6 | "$value": "{mode.color.status.positive.default}", 7 | "$description": " " 8 | }, 9 | "bgFooter-active": { 10 | "$type": "color", 11 | "$value": "{mode.color.action.grayscale.active}" 12 | }, 13 | "border-activated": { 14 | "$type": "color", 15 | "$value": "{mode.color.status.positive.default}" 16 | }, 17 | "bgFooter-default": { 18 | "$type": "color", 19 | "$value": "{mode.color.generic.bg.nought}", 20 | "$description": "tile footer bg color " 21 | }, 22 | "bg-default": { 23 | "$type": "color", 24 | "$value": "{mode.color.generic.bg.nought}" 25 | }, 26 | "bg-disabled": { 27 | "$type": "color", 28 | "$value": "{mode.color.action.inactive.defaultAlt}" 29 | }, 30 | "bg-hover": { 31 | "$type": "color", 32 | "$value": "{mode.color.action.grayscale.hoverAlt}", 33 | "$description": "Used for accordion hov backgrounds " 34 | }, 35 | "border-active": { 36 | "$type": "color", 37 | "$value": "{mode.color.action.grayscale.active}" 38 | }, 39 | "border-alt": { 40 | "$type": "color", 41 | "$value": "{mode.color.generic.fg.moderate}", 42 | "$description": "Link preview. " 43 | }, 44 | "borderalt-hover": { 45 | "$type": "color", 46 | "$value": "{mode.color.action.grayscale.default}", 47 | "$description": "For hov border on Link preview." 48 | }, 49 | "border-inactive": { 50 | "$type": "color", 51 | "$value": "{mode.color.generic.fg.firm}" 52 | }, 53 | "border-default": { 54 | "$type": "color", 55 | "$value": "{mode.color.generic.fg.firm}" 56 | }, 57 | "icon-active": { 58 | "$type": "color", 59 | "$value": "{mode.color.action.grayscale.active}", 60 | "$description": "Active chevron for any accordion." 61 | }, 62 | "icon-default": { 63 | "$type": "color", 64 | "$value": "{mode.color.action.grayscale.default}", 65 | "$description": "Enabled chevron for any accordion." 66 | }, 67 | "iconAlt-default": { 68 | "$type": "color", 69 | "$value": "{mode.color.action.grayscale.defaultAlt}", 70 | "$description": "Enabled label for subtle accordion." 71 | }, 72 | "icon-hover": { 73 | "$type": "color", 74 | "$value": "{mode.color.action.grayscale.default}", 75 | "$description": "hov chevron for any accordion." 76 | }, 77 | "labelFooter-active": { 78 | "$type": "color", 79 | "$value": "{mode.color.action.grayscale.withActive}" 80 | }, 81 | "labelFooter-activated": { 82 | "$type": "color", 83 | "$value": "{mode.color.action.grayscale.withActive}" 84 | }, 85 | "txt-active": { 86 | "$type": "color", 87 | "$value": "{mode.color.action.grayscale.active}", 88 | "$description": "Active label for any accordion." 89 | }, 90 | "txt-disabled": { 91 | "$type": "color", 92 | "$value": "{mode.color.action.inactive.txt}" 93 | }, 94 | "txt-default": { 95 | "$type": "color", 96 | "$value": "{mode.color.generic.txt.severe}", 97 | "$description": "Enabled label for standard accordion." 98 | }, 99 | "txtAlt-default": { 100 | "$type": "color", 101 | "$value": "{mode.color.generic.txt.moderate}", 102 | "$description": "Enabled label for subtle accordion." 103 | }, 104 | "txt-hover": { 105 | "$type": "color", 106 | "$value": "{mode.color.action.grayscale.default}", 107 | "$description": "hov label for any accordion." 108 | }, 109 | "detailedicon": { 110 | "bg": { 111 | "$type": "color", 112 | "$value": "{mode.color.status.custom.green.default}", 113 | "$description": " " 114 | } 115 | }, 116 | "target": { 117 | "bg-default": { 118 | "$type": "color", 119 | "$value": "{mode.color.status.info.default}", 120 | "$description": "draggable drop target background color" 121 | } 122 | } 123 | }, 124 | "scrollbar": { 125 | "bg-default": { 126 | "$type": "color", 127 | "$value": "{mode.color.generic.bg.faint}" 128 | }, 129 | "fg-default": { 130 | "$type": "color", 131 | "$value": "{mode.color.generic.fg.firm}" 132 | }, 133 | "inverse": { 134 | "bg-default": { 135 | "$type": "color", 136 | "$value": "{mode.color.generic.bg.inverse.soft}" 137 | }, 138 | "fg-default": { 139 | "$type": "color", 140 | "$value": "{mode.color.generic.fg.inverse.firm}" 141 | } 142 | } 143 | }, 144 | "standard": { 145 | "bg-alt": { 146 | "$type": "color", 147 | "$value": "{mode.color.generic.bg.faint}" 148 | }, 149 | "bg-default": { 150 | "$type": "color", 151 | "$value": "{mode.color.generic.bg.nought}" 152 | }, 153 | "bgFooter-default": { 154 | "$type": "color", 155 | "$value": "{mode.color.generic.bg.faint}", 156 | "$description": "tile footer bg color " 157 | }, 158 | "border-active": { 159 | "$type": "color", 160 | "$value": "{mode.color.action.grayscale.active}" 161 | }, 162 | "border-AiH": { 163 | "$type": "color", 164 | "$value": "{mode.color.status.ai.inverse.default-horizontal}", 165 | "$description": "top border for AI dialog component" 166 | }, 167 | "border-AiV": { 168 | "$type": "color", 169 | "$value": "{mode.color.status.ai.inverse.default-vertical}", 170 | "$description": "left and right border for AI nudge message component" 171 | }, 172 | "border-alt": { 173 | "$type": "color", 174 | "$value": "{mode.color.generic.fg.firm}" 175 | }, 176 | "border-default": { 177 | "$type": "color", 178 | "$value": "{mode.color.generic.fg.soft}" 179 | }, 180 | "dimmer": { 181 | "$type": "color", 182 | "$value": "{mode.color.action.inactive.mask}", 183 | "$description": "dimmed mask for dialogs" 184 | }, 185 | "icon": { 186 | "$type": "color", 187 | "$value": "{mode.color.generic.fg.firm}", 188 | "$description": "Link preview image thumbnail" 189 | }, 190 | "txt-alt": { 191 | "$type": "color", 192 | "$value": "{mode.color.generic.txt.moderate}", 193 | "$description": "for subheadings etc" 194 | }, 195 | "txt-default": { 196 | "$type": "color", 197 | "$value": "{mode.color.generic.txt.severe}", 198 | "$description": "for headings, paragraph txt etc " 199 | }, 200 | "inverse": { 201 | "bg-alt": { 202 | "$type": "color", 203 | "$value": "{mode.color.generic.bg.inverse.delicate}" 204 | }, 205 | "border-alt": { 206 | "$type": "color", 207 | "$value": "{mode.color.generic.fg.inverse.firm}" 208 | }, 209 | "bg-default": { 210 | "$type": "color", 211 | "$value": "{mode.color.generic.bg.inverse.nought}" 212 | }, 213 | "border-default": { 214 | "$type": "color", 215 | "$value": "{mode.color.generic.fg.inverse.soft}" 216 | }, 217 | "txt-alt": { 218 | "$type": "color", 219 | "$value": "{mode.color.generic.txt.inverse.moderate}", 220 | "$description": "for subheadings etc" 221 | }, 222 | "txt-default": { 223 | "$type": "color", 224 | "$value": "{mode.color.generic.txt.inverse.severe}", 225 | "$description": "for headings, paragraph txt etc " 226 | } 227 | }, 228 | "priority": { 229 | "bg-ai": { 230 | "$type": "color", 231 | "$value": "{mode.color.status.ai.inverse.default-vertical}" 232 | }, 233 | "bg-caution": { 234 | "$type": "color", 235 | "$value": "{mode.color.status.warning.default}" 236 | }, 237 | "bg-negative": { 238 | "$type": "color", 239 | "$value": "{mode.color.status.negative.default}" 240 | }, 241 | "bg-prio": { 242 | "$type": "color", 243 | "$value": "{mode.color.status.priority.default}" 244 | }, 245 | "bg-info": { 246 | "$type": "color", 247 | "$value": "{mode.color.status.info.default}" 248 | }, 249 | "bg-neutral": { 250 | "$type": "color", 251 | "$value": "{mode.color.status.neutral.default}" 252 | }, 253 | "bg-positive": { 254 | "$type": "color", 255 | "$value": "{mode.color.status.positive.default}" 256 | }, 257 | "inverse": { 258 | "bg-ai": { 259 | "$type": "color", 260 | "$value": "{mode.color.status.ai.inverse.default-vertical}" 261 | }, 262 | "bg-caution": { 263 | "$type": "color", 264 | "$value": "{mode.color.status.warning.inverse.default}" 265 | }, 266 | "bg-negative": { 267 | "$type": "color", 268 | "$value": "{mode.color.status.negative.inverse.default}" 269 | }, 270 | "bg-prio": { 271 | "$type": "color", 272 | "$value": "{mode.color.status.priority.inverse.default}" 273 | }, 274 | "bg-info": { 275 | "$type": "color", 276 | "$value": "{mode.color.status.info.inverse.default}" 277 | }, 278 | "bg-neutral": { 279 | "$type": "color", 280 | "$value": "{mode.color.status.neutral.inverse.default}" 281 | }, 282 | "bg-positive": { 283 | "$type": "color", 284 | "$value": "{mode.color.status.positive.inverse.default}" 285 | } 286 | } 287 | } 288 | }, 289 | "quote": { 290 | "border": { 291 | "$type": "color", 292 | "$value": "{mode.color.action.main.default}" 293 | } 294 | }, 295 | "size": { 296 | "bubble": { 297 | "maxW": { 298 | "$type": "sizing", 299 | "$value": "600", 300 | "$description": "max width for chat bubbles" 301 | } 302 | }, 303 | "copilot": { 304 | "emptystate": { 305 | "illustration": { 306 | "$type": "sizing", 307 | "$value": "200", 308 | "$description": "Illustration size" 309 | }, 310 | "txt": { 311 | "maxW": { 312 | "$type": "sizing", 313 | "$value": "600", 314 | "$description": "max-width of the insight empty state txt area" 315 | } 316 | } 317 | }, 318 | "overlay": { 319 | "maxH": { 320 | "$type": "sizing", 321 | "$value": "960", 322 | "$description": "max-height of the copilot container overlay " 323 | } 324 | } 325 | }, 326 | "dialog": { 327 | "maxW": { 328 | "S": { 329 | "$type": "sizing", 330 | "$value": "540" 331 | }, 332 | "M": { 333 | "$type": "sizing", 334 | "$value": "850" 335 | }, 336 | "L": { 337 | "$type": "sizing", 338 | "$value": "1080" 339 | } 340 | }, 341 | "minW": { 342 | "$type": "sizing", 343 | "$value": "288" 344 | } 345 | }, 346 | "emptyState": { 347 | "primary": { 348 | "S": { 349 | "$type": "sizing", 350 | "$value": "200", 351 | "$description": "Illustration size for small error and empty states" 352 | }, 353 | "M": { 354 | "$type": "sizing", 355 | "$value": "240", 356 | "$description": "Illustration size for medium error and empty states" 357 | }, 358 | "L": { 359 | "$type": "sizing", 360 | "$value": "320", 361 | "$description": "Illustration size for large error and empty states" 362 | } 363 | } 364 | }, 365 | "fluidItems": { 366 | "2XS": { 367 | "$type": "sizing", 368 | "$value": "80", 369 | "$description": "min and max widths for responsive tile items" 370 | }, 371 | "XS": { 372 | "$type": "sizing", 373 | "$value": "128", 374 | "$description": "min and max widths for responsive tile items" 375 | }, 376 | "S": { 377 | "$type": "sizing", 378 | "$value": "160", 379 | "$description": "min and max widths for responsive tile items. Min width for Tile select txt wrapper." 380 | }, 381 | "M": { 382 | "$type": "sizing", 383 | "$value": "200", 384 | "$description": "min and max widths for responsive tile items" 385 | }, 386 | "L": { 387 | "$type": "sizing", 388 | "$value": "240", 389 | "$description": "min and max widths for responsive tile items" 390 | }, 391 | "XL": { 392 | "$type": "sizing", 393 | "$value": "288", 394 | "$description": "Small screen mid width inside page margins." 395 | }, 396 | "2XL": { 397 | "$type": "sizing", 398 | "$value": "320", 399 | "$description": "Small screen full page width." 400 | }, 401 | "3XL": { 402 | "$type": "sizing", 403 | "$value": "560", 404 | "$description": "min and max widths for responsive tile items" 405 | }, 406 | "4XL": { 407 | "$type": "sizing", 408 | "$value": "760", 409 | "$description": "Max width for single line of txt." 410 | } 411 | }, 412 | "sidebar": { 413 | "maxW": { 414 | "S": { 415 | "$type": "sizing", 416 | "$value": "760", 417 | "$description": "max-width for S size sidebar" 418 | }, 419 | "M": { 420 | "$type": "sizing", 421 | "$value": "1000", 422 | "$description": "max-width for M size sidebar" 423 | } 424 | }, 425 | "minW": { 426 | "$type": "sizing", 427 | "$value": "288", 428 | "$description": "min-width for M sized sidebar" 429 | } 430 | } 431 | } 432 | } 433 | } -------------------------------------------------------------------------------- /data/tokens/components/button.json: -------------------------------------------------------------------------------- 1 | { 2 | "button": { 3 | "none": { 4 | "$type": "color", 5 | "$value": "{mode.color.none}", 6 | "$description": "transparent override used for hiding colors when needed." 7 | }, 8 | "ai": { 9 | "bg-active": { 10 | "$type": "color", 11 | "$value": "{mode.color.action.ai.grad.active}" 12 | }, 13 | "bg-hover": { 14 | "$type": "color", 15 | "$value": "{mode.color.action.ai.grad.hover}" 16 | }, 17 | "border-active": { 18 | "$type": "color", 19 | "$value": "linear-gradient(90deg, {mode.color.ai.stop-1} 0%, {mode.color.ai.stop-2} 40%, {mode.color.ai.stop-3} 90%)" 20 | }, 21 | "border-disabled": { 22 | "$type": "color", 23 | "$value": "{mode.color.action.inactive.default}" 24 | }, 25 | "border-default": { 26 | "$type": "color", 27 | "$value": "linear-gradient(90deg, {mode.color.ai.stop-1} 0%, {mode.color.ai.stop-2} 40%, {mode.color.ai.stop-3} 90%)" 28 | }, 29 | "border-hover": { 30 | "$type": "color", 31 | "$value": "linear-gradient(90deg, {mode.color.ai.stop-1} 0%, {mode.color.ai.stop-2} 40%, {mode.color.ai.stop-3} 90%)" 32 | }, 33 | "label-active": { 34 | "$type": "color", 35 | "$value": "{mode.color.action.grayscale.withActiveAlt}" 36 | }, 37 | "label-disabled": { 38 | "$type": "color", 39 | "$value": "{mode.color.action.inactive.txt}" 40 | }, 41 | "label-default": { 42 | "$type": "color", 43 | "$value": "{mode.color.action.grayscale.default}" 44 | }, 45 | "label-hover": { 46 | "$type": "color", 47 | "$value": "{mode.color.action.grayscale.withHover}" 48 | } 49 | }, 50 | "destructive": { 51 | "primary": { 52 | "bg-disabled": { 53 | "$type": "color", 54 | "$value": "{mode.color.action.inactive.default}" 55 | }, 56 | "bg-default": { 57 | "$type": "color", 58 | "$value": "{mode.color.action.danger.default}" 59 | }, 60 | "bg-hover": { 61 | "$type": "color", 62 | "$value": "{mode.color.action.danger.hover}" 63 | }, 64 | "label-disabled": { 65 | "$type": "color", 66 | "$value": "{mode.color.action.inactive.txtAlt}" 67 | }, 68 | "label-default": { 69 | "$type": "color", 70 | "$value": "{mode.color.action.danger.withDefault}" 71 | }, 72 | "label-hover": { 73 | "$type": "color", 74 | "$value": "{mode.color.action.danger.withDefault}" 75 | } 76 | }, 77 | "secondary": { 78 | "bg-hover": { 79 | "$type": "color", 80 | "$value": "{mode.color.action.danger.hoverAlt}" 81 | }, 82 | "border-disabled": { 83 | "$type": "color", 84 | "$value": "{mode.color.action.inactive.default}" 85 | }, 86 | "border-default": { 87 | "$type": "color", 88 | "$value": "{mode.color.action.danger.default}" 89 | }, 90 | "border-hover": { 91 | "$type": "color", 92 | "$value": "{mode.color.action.danger.hover}" 93 | }, 94 | "label-disabled": { 95 | "$type": "color", 96 | "$value": "{mode.color.action.inactive.txt}" 97 | }, 98 | "label-default": { 99 | "$type": "color", 100 | "$value": "{mode.color.action.danger.default}" 101 | }, 102 | "label-hover": { 103 | "$type": "color", 104 | "$value": "{mode.color.action.danger.hover}" 105 | } 106 | } 107 | }, 108 | "typical": { 109 | "primary": { 110 | "bg-active": { 111 | "$type": "color", 112 | "$value": "{mode.color.action.main.active}" 113 | }, 114 | "border-default": { 115 | "$type": "color", 116 | "$value": "{mode.color.action.main.withDefault}", 117 | "$description": "For spacer in Split button" 118 | }, 119 | "bg-disabled": { 120 | "$type": "color", 121 | "$value": "{mode.color.action.inactive.default}" 122 | }, 123 | "bg-default": { 124 | "$type": "color", 125 | "$value": "{mode.color.action.main.default}" 126 | }, 127 | "bg-hover": { 128 | "$type": "color", 129 | "$value": "{mode.color.action.main.hover}" 130 | }, 131 | "label-active": { 132 | "$type": "color", 133 | "$value": "{mode.color.action.main.withActive}" 134 | }, 135 | "label-disabled": { 136 | "$type": "color", 137 | "$value": "{mode.color.action.inactive.txtAlt}" 138 | }, 139 | "label-default": { 140 | "$type": "color", 141 | "$value": "{mode.color.action.main.withDefault}" 142 | }, 143 | "label-hover": { 144 | "$type": "color", 145 | "$value": "{mode.color.action.main.withDefault}" 146 | }, 147 | "inverse": { 148 | "bg-active": { 149 | "$type": "color", 150 | "$value": "{mode.color.action.main.inverse.active}" 151 | }, 152 | "border-default": { 153 | "$type": "color", 154 | "$value": "{mode.color.action.main.inverse.withDefault}", 155 | "$description": "For spacer in Split button" 156 | }, 157 | "bg-disabled": { 158 | "$type": "color", 159 | "$value": "{mode.color.action.inactive.inverse.default}" 160 | }, 161 | "bg-default": { 162 | "$type": "color", 163 | "$value": "{mode.color.action.main.inverse.default}" 164 | }, 165 | "bg-hover": { 166 | "$type": "color", 167 | "$value": "{mode.color.action.main.inverse.hover}" 168 | }, 169 | "label-active": { 170 | "$type": "color", 171 | "$value": "{mode.color.action.main.inverse.withActive}" 172 | }, 173 | "label-disabled": { 174 | "$type": "color", 175 | "$value": "{mode.color.action.inactive.inverse.txtAlt}" 176 | }, 177 | "label-default": { 178 | "$type": "color", 179 | "$value": "{mode.color.action.main.inverse.withDefault}" 180 | }, 181 | "label-hover": { 182 | "$type": "color", 183 | "$value": "{mode.color.action.main.inverse.withDefault}" 184 | } 185 | } 186 | }, 187 | "secondary": { 188 | "bg-active": { 189 | "$type": "color", 190 | "$value": "{mode.color.action.main.activeAlt}" 191 | }, 192 | "bg-default": { 193 | "$type": "color", 194 | "$value": "{mode.color.action.main.defaultAlt3}" 195 | }, 196 | "bg-hover": { 197 | "$type": "color", 198 | "$value": "{mode.color.action.main.hoverAlt}" 199 | }, 200 | "border-active": { 201 | "$type": "color", 202 | "$value": "{mode.color.action.main.active}" 203 | }, 204 | "border-disabled": { 205 | "$type": "color", 206 | "$value": "{mode.color.action.inactive.default}" 207 | }, 208 | "border-default": { 209 | "$type": "color", 210 | "$value": "{mode.color.action.main.defaultAlt}" 211 | }, 212 | "border-hover": { 213 | "$type": "color", 214 | "$value": "{mode.color.action.main.hover}" 215 | }, 216 | "label-active": { 217 | "$type": "color", 218 | "$value": "{mode.color.action.grayscale.withActiveAlt}" 219 | }, 220 | "label-disabled": { 221 | "$type": "color", 222 | "$value": "{mode.color.action.inactive.txt}" 223 | }, 224 | "label-default": { 225 | "$type": "color", 226 | "$value": "{mode.color.action.grayscale.default}" 227 | }, 228 | "label-hover": { 229 | "$type": "color", 230 | "$value": "{mode.color.action.grayscale.withHover}" 231 | }, 232 | "inverse": { 233 | "bg-active": { 234 | "$type": "color", 235 | "$value": "{mode.color.action.main.inverse.activeAlt}" 236 | }, 237 | "bg-default": { 238 | "$type": "color", 239 | "$value": "{mode.color.action.main.inverse.defaultAlt3}" 240 | }, 241 | "bg-hover": { 242 | "$type": "color", 243 | "$value": "{mode.color.action.main.inverse.hoverAlt}" 244 | }, 245 | "border-active": { 246 | "$type": "color", 247 | "$value": "{mode.color.action.main.inverse.active}" 248 | }, 249 | "border-disabled": { 250 | "$type": "color", 251 | "$value": "{mode.color.action.inactive.inverse.default}" 252 | }, 253 | "border-default": { 254 | "$type": "color", 255 | "$value": "{mode.color.action.main.inverse.defaultAlt}" 256 | }, 257 | "border-hover": { 258 | "$type": "color", 259 | "$value": "{mode.color.action.main.inverse.hover}" 260 | }, 261 | "label-active": { 262 | "$type": "color", 263 | "$value": "{mode.color.action.grayscale.inverse.withActiveAlt}" 264 | }, 265 | "label-disabled": { 266 | "$type": "color", 267 | "$value": "{mode.color.action.inactive.inverse.txt}" 268 | }, 269 | "label-default": { 270 | "$type": "color", 271 | "$value": "{mode.color.action.grayscale.inverse.default}" 272 | }, 273 | "label-hover": { 274 | "$type": "color", 275 | "$value": "{mode.color.action.grayscale.inverse.withHover}" 276 | } 277 | } 278 | }, 279 | "tertiary": { 280 | "bg-active": { 281 | "$type": "color", 282 | "$value": "{mode.color.action.main.activeAlt}" 283 | }, 284 | "bg-default": { 285 | "$type": "color", 286 | "$value": "{button.none}" 287 | }, 288 | "bg-hover": { 289 | "$type": "color", 290 | "$value": "{mode.color.action.main.hoverAlt}" 291 | }, 292 | "border-active": { 293 | "$type": "color", 294 | "$value": "{mode.color.action.main.active}" 295 | }, 296 | "border-disabled": { 297 | "$type": "color", 298 | "$value": "{mode.color.action.inactive.default}" 299 | }, 300 | "border-default": { 301 | "$type": "color", 302 | "$value": "{mode.color.action.main.defaultAlt}" 303 | }, 304 | "border-hover": { 305 | "$type": "color", 306 | "$value": "{mode.color.action.main.hover}" 307 | }, 308 | "label-active": { 309 | "$type": "color", 310 | "$value": "{mode.color.action.grayscale.withActiveAlt}" 311 | }, 312 | "label-disabled": { 313 | "$type": "color", 314 | "$value": "{mode.color.action.inactive.txt}" 315 | }, 316 | "label-default": { 317 | "$type": "color", 318 | "$value": "{mode.color.action.grayscale.default}" 319 | }, 320 | "label-hover": { 321 | "$type": "color", 322 | "$value": "{mode.color.action.grayscale.withHover}" 323 | }, 324 | "inverse": { 325 | "bg-active": { 326 | "$type": "color", 327 | "$value": "{mode.color.action.main.inverse.activeAlt}" 328 | }, 329 | "bg-default": { 330 | "$type": "color", 331 | "$value": "{button.none}" 332 | }, 333 | "bg-hover": { 334 | "$type": "color", 335 | "$value": "{mode.color.action.main.inverse.hoverAlt}" 336 | }, 337 | "border-active": { 338 | "$type": "color", 339 | "$value": "{mode.color.action.main.inverse.active}" 340 | }, 341 | "border-disabled": { 342 | "$type": "color", 343 | "$value": "{mode.color.action.inactive.inverse.default}" 344 | }, 345 | "border-default": { 346 | "$type": "color", 347 | "$value": "{mode.color.action.main.inverse.defaultAlt}" 348 | }, 349 | "border-hover": { 350 | "$type": "color", 351 | "$value": "{mode.color.action.main.inverse.hover}" 352 | }, 353 | "label-active": { 354 | "$type": "color", 355 | "$value": "{mode.color.action.grayscale.inverse.withActiveAlt}" 356 | }, 357 | "label-disabled": { 358 | "$type": "color", 359 | "$value": "{mode.color.action.inactive.inverse.txt}" 360 | }, 361 | "label-default": { 362 | "$type": "color", 363 | "$value": "{mode.color.action.grayscale.inverse.default}" 364 | }, 365 | "label-hover": { 366 | "$type": "color", 367 | "$value": "{mode.color.action.grayscale.inverse.withHover}" 368 | } 369 | } 370 | }, 371 | "subtle": { 372 | "bg-active": { 373 | "$type": "color", 374 | "$value": "{mode.color.action.grayscale.activeAlt}" 375 | }, 376 | "bg-hover": { 377 | "$type": "color", 378 | "$value": "{mode.color.action.grayscale.hoverAlt}" 379 | }, 380 | "label-active": { 381 | "$type": "color", 382 | "$value": "{mode.color.action.grayscale.withActiveAlt}" 383 | }, 384 | "label-disabled": { 385 | "$type": "color", 386 | "$value": "{mode.color.action.inactive.txt}" 387 | }, 388 | "label-default": { 389 | "$type": "color", 390 | "$value": "{mode.color.action.grayscale.default}" 391 | }, 392 | "label-hover": { 393 | "$type": "color", 394 | "$value": "{mode.color.action.grayscale.withHover}" 395 | }, 396 | "inverse": { 397 | "bg-active": { 398 | "$type": "color", 399 | "$value": "{mode.color.action.grayscale.inverse.activeAlt}" 400 | }, 401 | "bg-hover": { 402 | "$type": "color", 403 | "$value": "{mode.color.action.grayscale.inverse.hoverAlt}" 404 | }, 405 | "label-active": { 406 | "$type": "color", 407 | "$value": "{mode.color.action.grayscale.inverse.withActiveAlt}" 408 | }, 409 | "label-disabled": { 410 | "$type": "color", 411 | "$value": "{mode.color.action.inactive.inverse.txt}" 412 | }, 413 | "label-default": { 414 | "$type": "color", 415 | "$value": "{mode.color.action.grayscale.inverse.default}" 416 | }, 417 | "label-hover": { 418 | "$type": "color", 419 | "$value": "{mode.color.action.grayscale.inverse.withHover}" 420 | } 421 | } 422 | }, 423 | "toggle": { 424 | "bg-activeDisabled": { 425 | "$type": "color", 426 | "$value": "{mode.color.action.inactive.default}" 427 | }, 428 | "label-activeDisabled": { 429 | "$type": "color", 430 | "$value": "{mode.color.action.inactive.txtAlt}" 431 | }, 432 | "bg-active": { 433 | "$type": "color", 434 | "$value": "{mode.color.action.grayscale.active}" 435 | }, 436 | "bg-hover": { 437 | "$type": "color", 438 | "$value": "{mode.color.action.grayscale.hoverAlt}" 439 | }, 440 | "border-default": { 441 | "$type": "color", 442 | "$value": "{mode.color.action.grayscale.default}" 443 | }, 444 | "border-disabled": { 445 | "$type": "color", 446 | "$value": "{mode.color.action.inactive.default}" 447 | }, 448 | "label-active": { 449 | "$type": "color", 450 | "$value": "{mode.color.action.grayscale.withActive}" 451 | }, 452 | "label-disabled": { 453 | "$type": "color", 454 | "$value": "{mode.color.action.inactive.txt}" 455 | }, 456 | "label-default": { 457 | "$type": "color", 458 | "$value": "{mode.color.action.grayscale.defaultAlt}" 459 | }, 460 | "label-hover": { 461 | "$type": "color", 462 | "$value": "{mode.color.action.grayscale.withHover}" 463 | } 464 | } 465 | }, 466 | "video": { 467 | "bg-blur": { 468 | "$type": "color", 469 | "$value": "{mode.color.action.grayscale.hoverAlt}" 470 | }, 471 | "primary": { 472 | "bg-default": { 473 | "$type": "color", 474 | "$value": "{mode.color.action.grayscale.withDefault}" 475 | }, 476 | "bg-hover": { 477 | "$type": "color", 478 | "$value": "{mode.color.action.grayscale.withDefault}" 479 | }, 480 | "label-default": { 481 | "$type": "color", 482 | "$value": "{mode.color.action.grayscale.default}" 483 | }, 484 | "label-hover": { 485 | "$type": "color", 486 | "$value": "{mode.color.action.grayscale.default}" 487 | } 488 | }, 489 | "secondary": { 490 | "bg-hover": { 491 | "$type": "color", 492 | "$value": "{mode.color.action.grayscale.withDefault}" 493 | }, 494 | "border-default": { 495 | "$type": "color", 496 | "$value": "{mode.color.action.grayscale.withDefault}" 497 | }, 498 | "border-hover": { 499 | "$type": "color", 500 | "$value": "{mode.color.action.grayscale.withDefault}" 501 | }, 502 | "label-default": { 503 | "$type": "color", 504 | "$value": "{mode.color.action.grayscale.withDefault}" 505 | }, 506 | "label-hover": { 507 | "$type": "color", 508 | "$value": "{mode.color.action.grayscale.default}" 509 | } 510 | } 511 | } 512 | } 513 | } --------------------------------------------------------------------------------