diff --git a/.changeset/serious-frogs-rescue.md b/.changeset/serious-frogs-rescue.md new file mode 100644 index 0000000000..ecd73414df --- /dev/null +++ b/.changeset/serious-frogs-rescue.md @@ -0,0 +1,5 @@ +--- +'@digdir/designsystemet': minor +--- + +New create tokens script with color options support diff --git a/.github/workflows/deploy-theme.yml b/.github/workflows/deploy-theme.yml index bdbb8e00ac..bf48feefbc 100644 --- a/.github/workflows/deploy-theme.yml +++ b/.github/workflows/deploy-theme.yml @@ -12,10 +12,6 @@ on: options: - production - next - pull_request: - types: [opened, synchronize] - paths: - - 'apps/theme/**' push: branches: - next diff --git a/.gitignore b/.gitignore index ad866af1e4..9bbf503b19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ # Built files dist -build ignore tsc-build diff --git a/apps/_components/src/CodeSnippet/CodeSnippet.module.css b/apps/_components/src/CodeSnippet/CodeSnippet.module.css index 06fcf127cc..40776471c0 100644 --- a/apps/_components/src/CodeSnippet/CodeSnippet.module.css +++ b/apps/_components/src/CodeSnippet/CodeSnippet.module.css @@ -7,6 +7,10 @@ height: fit-content; } +.codeSnippet > pre { + padding-right: var(--ds-spacing-11) !important; +} + .copyButton { position: absolute; top: var(--ds-spacing-2); diff --git a/apps/_components/src/CodeSnippet/CodeSnippet.tsx b/apps/_components/src/CodeSnippet/CodeSnippet.tsx index 48940f5065..a3fa1962e0 100644 --- a/apps/_components/src/CodeSnippet/CodeSnippet.tsx +++ b/apps/_components/src/CodeSnippet/CodeSnippet.tsx @@ -25,7 +25,7 @@ const plugins = [ ]; type CodeSnippetProps = { - language?: 'css' | 'html' | 'ts' | 'markdown' | 'js' | 'json'; + language?: 'css' | 'html' | 'ts' | 'markdown' | 'js' | 'json' | 'sh'; children?: string; }; @@ -85,7 +85,7 @@ const CodeSnippet = ({ (null); - const themeMode = (params.get('theme') as Mode) || 'light'; + const themeMode = (params.get('theme') as ColorMode) || 'light'; const contrastMode = (params.get('contrastMode') as ContrastMode) || 'aa'; useEffect(() => { @@ -123,7 +123,7 @@ export default function Home() { * @param returnColor The default color to return * @returns The color from the query or the default color */ - const getQueryColor = (colorType: ColorType, returnColor: CssColor) => { + const getQueryColor = (colorType: ThemeColors, returnColor: CssColor) => { const queryColor = params.get(colorType); if (queryColor && isHexColor(queryColor.substring(1))) { return queryColor as CssColor; @@ -139,7 +139,11 @@ export default function Home() { * @param color The color to update * @param theme The theme to update */ - const updateColor = (type: ColorType, color: CssColor, theme: ThemeInfo) => { + const updateColor = ( + type: ThemeColors, + color: CssColor, + theme: ThemeInfo, + ) => { const colorErrorSetterMap = { accent: setAccentError, neutral: setNeutralError, @@ -167,7 +171,7 @@ export default function Home() { * @param colorType The type of color to set * @param color The color to set */ - const colorQuerySetter = (colorType: ColorType, color: CssColor) => { + const colorQuerySetter = (colorType: ThemeColors, color: CssColor) => { const defaultColor = { accent: Settings.accentBaseColor, neutral: Settings.neutralBaseColor, @@ -193,7 +197,7 @@ export default function Home() { contrastMode, }: { colors?: ThemeInfo; - theme?: Mode; + theme?: ColorMode; borderRadius?: string; contrastMode?: ContrastMode; }) => { @@ -215,7 +219,7 @@ export default function Home() { setQueryParams({ borderRadius: radius }); }; - const updateTheme = (theme: Mode) => { + const updateTheme = (theme: ColorMode) => { setQueryParams({ theme }); }; diff --git a/apps/theme/components/Color/Color.tsx b/apps/theme/components/Color/Color.tsx index 9d76fb7dee..f8ec90f21c 100644 --- a/apps/theme/components/Color/Color.tsx +++ b/apps/theme/components/Color/Color.tsx @@ -1,5 +1,5 @@ import { omit } from '@digdir/designsystemet-react'; -import type { ColorInfo, ColorType } from '@digdir/designsystemet/color'; +import type { ColorInfo, ThemeColors } from '@digdir/designsystemet/color'; import { SunIcon } from '@navikt/aksel-icons'; import cl from 'clsx/lite'; import { forwardRef } from 'react'; @@ -16,7 +16,7 @@ type ColorProps = { featured?: boolean; hex?: string; showColorMeta?: boolean; - type: ColorType; + type: ThemeColors; } & Omit, 'color'>; const Color = forwardRef( diff --git a/apps/theme/components/Group/Group.tsx b/apps/theme/components/Group/Group.tsx index 303849bbd8..5b47275764 100644 --- a/apps/theme/components/Group/Group.tsx +++ b/apps/theme/components/Group/Group.tsx @@ -1,5 +1,5 @@ import { RovingFocusItem } from '@digdir/designsystemet-react'; -import type { ColorInfo, ColorType } from '@digdir/designsystemet/color'; +import type { ColorInfo, ThemeColors } from '@digdir/designsystemet/color'; import cl from 'clsx/lite'; import { Color } from '../Color/Color'; @@ -12,7 +12,7 @@ type GroupProps = { showColorMeta?: boolean; names?: string[]; featured?: boolean; - type: ColorType; + type: ThemeColors; }; export const Group = ({ diff --git a/apps/theme/components/Previews/Previews.tsx b/apps/theme/components/Previews/Previews.tsx index ee52f731e0..27e9f354a5 100644 --- a/apps/theme/components/Previews/Previews.tsx +++ b/apps/theme/components/Previews/Previews.tsx @@ -1,4 +1,4 @@ -import type { Mode } from '@digdir/designsystemet/color'; +import type { ColorMode } from '@digdir/designsystemet/color'; import cl from 'clsx/lite'; import { useState } from 'react'; @@ -15,8 +15,8 @@ type previewModeType = | 'components'; type PreviewsProps = { - themeMode: Mode; - onThemeModeChange: (themeMode: Mode) => void; + themeMode: ColorMode; + onThemeModeChange: (themeMode: ColorMode) => void; }; export const Previews = ({ themeMode, onThemeModeChange }: PreviewsProps) => { diff --git a/apps/theme/components/Scale/Scale.tsx b/apps/theme/components/Scale/Scale.tsx index 293e69bb31..e911a2eb49 100644 --- a/apps/theme/components/Scale/Scale.tsx +++ b/apps/theme/components/Scale/Scale.tsx @@ -1,5 +1,5 @@ import { RovingFocusRoot } from '@digdir/designsystemet-react'; -import type { ColorInfo, ColorType } from '@digdir/designsystemet/color'; +import type { ColorInfo, ThemeColors } from '@digdir/designsystemet/color'; import { useEffect, useState } from 'react'; import type { modeType } from '../../types'; @@ -12,7 +12,7 @@ type ScaleProps = { showHeader?: boolean; showColorMeta?: boolean; themeMode: modeType; - type: ColorType; + type: ThemeColors; }; const setTokens = (lightColors: ColorInfo[], type: string) => { diff --git a/apps/theme/components/Scales/Scales.tsx b/apps/theme/components/Scales/Scales.tsx index 854bfc0ffe..147e12a937 100644 --- a/apps/theme/components/Scales/Scales.tsx +++ b/apps/theme/components/Scales/Scales.tsx @@ -1,4 +1,4 @@ -import type { Mode } from '@digdir/designsystemet/color'; +import type { ColorMode } from '@digdir/designsystemet/color'; import cl from 'clsx/lite'; import { useThemeStore } from '../../store'; @@ -7,7 +7,7 @@ import { Scale } from '../Scale/Scale'; import classes from './Scales.module.css'; type ScalesProps = { - themeMode: Mode; + themeMode: ColorMode; }; export const Scales = ({ themeMode }: ScalesProps) => { diff --git a/apps/theme/components/ThemeToolbar/ThemeToolbar.tsx b/apps/theme/components/ThemeToolbar/ThemeToolbar.tsx index 1741e673ca..031b4c3fbf 100644 --- a/apps/theme/components/ThemeToolbar/ThemeToolbar.tsx +++ b/apps/theme/components/ThemeToolbar/ThemeToolbar.tsx @@ -2,8 +2,8 @@ import type { CssColor } from '@adobe/leonardo-contrast-colors'; import { NativeSelect } from '@digdir/designsystemet-react'; import type { ColorError, - ColorType, ContrastMode, + ThemeColors, } from '@digdir/designsystemet/color'; import cl from 'clsx/lite'; @@ -20,7 +20,7 @@ type ThemeToolbarProps = { brand2Error: ColorError; brand3Error: ColorError; borderRadius: string; - onColorChanged: (type: ColorType, color: CssColor) => void; + onColorChanged: (type: ThemeColors, color: CssColor) => void; onContrastModeChanged: (mode: 'aa' | 'aaa') => void; onBorderRadiusChanged: (radius: string) => void; contrastMode: ContrastMode; diff --git a/apps/theme/components/TokenModal/TokenModal.tsx b/apps/theme/components/TokenModal/TokenModal.tsx index 05bae432c0..885bb30527 100644 --- a/apps/theme/components/TokenModal/TokenModal.tsx +++ b/apps/theme/components/TokenModal/TokenModal.tsx @@ -8,14 +8,9 @@ import { Tabs, Tooltip, } from '@digdir/designsystemet-react'; -import type { ColorInfo, ColorType } from '@digdir/designsystemet/color'; -import { generateScaleForColor } from '@digdir/designsystemet/color'; import { ArrowForwardIcon } from '@navikt/aksel-icons'; import { CodeSnippet } from '@repo/components'; -import { useEffect, useRef, useState } from 'react'; - -import { Settings } from '../../settings'; -import type { modeType } from '../../types'; +import { useRef, useState } from 'react'; import classes from './TokenModal.module.css'; @@ -37,114 +32,10 @@ export const TokenModal = ({ borderRadius, }: TokenModalProps) => { const modalRef = useRef(null); - const [JSONTheme, setJSONTheme] = useState(''); - const [css, setCss] = useState( - ':root { --color-1: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; --color-2: #F45F63; }', - ); - const [toolTipText, setToolTipText] = useState('Kopier nettaddresse'); - const [showGlobals, setShowGlobals] = useState(false); - - const generateJsonForColor = (colorArray: ColorInfo[]) => { - const obj: { [key: string]: { value: string; type: string } } = {}; - for (let i = 0; i < colorArray.length; i++) { - if (i === 13 && colorArray.length >= 14) { - obj['contrast-1'] = { - value: colorArray[i].hex, - type: 'color', - }; - } else if (i === 14 && colorArray.length >= 15) { - obj['contrast-2'] = { - value: colorArray[i].hex, - type: 'color', - }; - } else { - obj[i + 1] = { value: colorArray[i].hex, type: 'color' }; - } - } - return obj; - }; - - const genereateGlobalsJson = (theme: modeType) => { - const blueScale = generateScaleForColor(Settings.blueBaseColor, theme); - const greenScale = generateScaleForColor(Settings.greenBaseColor, theme); - const orangeScale = generateScaleForColor(Settings.orangeBaseColor, theme); - const purpleScale = generateScaleForColor(Settings.purpleBaseColor, theme); - const redScale = generateScaleForColor(Settings.redBaseColor, theme); - const yellowScale = generateScaleForColor(Settings.yellowBaseColor, theme); - - const obj = { - global: { - blue: generateJsonForColor(blueScale), - green: generateJsonForColor(greenScale), - orange: generateJsonForColor(orangeScale), - purple: generateJsonForColor(purpleScale), - red: generateJsonForColor(redScale), - yellow: generateJsonForColor(yellowScale), - }, - }; - - const json = JSON.stringify(obj, null, '\t'); - setJSONTheme(json); - }; - - const generateThemeJson = (theme: modeType) => { - const accentColors = generateScaleForColor(accentColor, theme); - const neutralColors = generateScaleForColor(neutralColor, theme); - const brand1Colors = generateScaleForColor(brand1Color, theme); - const brand2Colors = generateScaleForColor(brand2Color, theme); - const brand3Colors = generateScaleForColor(brand3Color, theme); - - const obj = { - theme: { - accent: generateJsonForColor(accentColors), - neutral: generateJsonForColor(neutralColors), - brand1: generateJsonForColor(brand1Colors), - brand2: generateJsonForColor(brand2Colors), - brand3: generateJsonForColor(brand3Colors), - }, - }; - const json = JSON.stringify(obj, null, '\t'); - setJSONTheme(json); - }; - - const generateCSSVars = (theme: modeType) => { - const accentColors = generateScaleForColor(accentColor, theme); - const neutralColors = generateScaleForColor(neutralColor, theme); - const brand1Colors = generateScaleForColor(brand1Color, theme); - const brand2Colors = generateScaleForColor(brand2Color, theme); - const brand3Colors = generateScaleForColor(brand3Color, theme); - - const obj = { - theme: { - accent: generateJsonForColor(accentColors), - neutral: generateJsonForColor(neutralColors), - brand1: generateJsonForColor(brand1Colors), - brand2: generateJsonForColor(brand2Colors), - brand3: generateJsonForColor(brand3Colors), - }, - }; - - let CSS = ''; - - if (theme === 'light') { - CSS = ':root, [data-ds-theme="light"] {'; - } else if (theme === 'dark') { - CSS = '[data-ds-theme="dark"] {'; - } else { - CSS = '[data-ds-theme="contrast"] {'; - } - - CSS += `--ds-border-radius-base: ${borderRadius};`; + const [toolTipText, setToolTipText] = useState('Kopier nettaddresse'); - for (const key in obj.theme) { - for (const color in obj.theme[key as ColorType]) { - CSS += `--ds-color-${key}-${color}: ${obj.theme[key as ColorType][color].value};`; - } - } - CSS += '}'; - return CSS; - }; + const cliSnippet = `npx @digdir/designsystemet tokens create --write --accent "${accentColor}" --neutral "${neutralColor}" --brand1 "${brand1Color}" --brand2 "${brand2Color}" --brand3 "${brand3Color}"`; const onButtonClick = () => { setToolTipText('Kopiert!'); @@ -153,23 +44,10 @@ export const TokenModal = ({ }); }; - useEffect(() => { - generateThemeJson('light'); - const lightCSS = generateCSSVars('light'); - const darkCSS = generateCSSVars('dark'); - const contrastCSS = generateCSSVars('contrast'); - setCss(lightCSS + darkCSS + contrastCSS); - }, []); - return ( { - generateThemeJson('light'); - const lightCSS = generateCSSVars('light'); - const darkCSS = generateCSSVars('dark'); - const contrastCSS = generateCSSVars('contrast'); - setCss(lightCSS + darkCSS + contrastCSS); return modalRef.current?.showModal(); }} > @@ -203,73 +81,8 @@ export const TokenModal = ({
- -
- - Json til Figma - -
- - - generateThemeJson('light')} - value='value1' - > - Light Mode - - generateThemeJson('dark')} - value='value2' - > - Dark Mode - - {showGlobals && ( - <> - generateThemeJson('contrast')} - value='value3' - > - Contrast Mode - - genereateGlobalsJson('light')} - value='value4' - > - G:Light - - genereateGlobalsJson('dark')} - value='value5' - > - G:Dark - - genereateGlobalsJson('contrast')} - value='value6' - > - G:Contrast - - - )} - - -
-
- {JSONTheme} -
-
-
- - CSS variabler - - -
- {css} -
+
+ {cliSnippet}
diff --git a/apps/theme/store.ts b/apps/theme/store.ts index e93bdb7b39..6f69a5a729 100644 --- a/apps/theme/store.ts +++ b/apps/theme/store.ts @@ -1,7 +1,7 @@ import type { CssColor } from '@adobe/leonardo-contrast-colors'; import type { ColorInfo, - ColorType, + ThemeColors, ThemeInfo, } from '@digdir/designsystemet/color'; import { create } from 'zustand'; @@ -25,8 +25,8 @@ type ColorStore = { setBrandOneTheme: (theme: ThemeInfo, color: CssColor) => void; setBrandTwoTheme: (theme: ThemeInfo, color: CssColor) => void; setBrandThreeTheme: (theme: ThemeInfo, color: CssColor) => void; - selectedColor: { color: ColorInfo; type: ColorType }; - setSelectedColor: (color: ColorInfo, type: ColorType) => void; + selectedColor: { color: ColorInfo; type: ThemeColors }; + setSelectedColor: (color: ColorInfo, type: ThemeColors) => void; borderRadius: string; setBorderRadius: (radius: string) => void; }; diff --git a/packages/cli/bin/designsystemet.ts b/packages/cli/bin/designsystemet.ts index 709c2d8ec1..ed4b7d6512 100644 --- a/packages/cli/bin/designsystemet.ts +++ b/packages/cli/bin/designsystemet.ts @@ -1,31 +1,74 @@ #!/usr/bin/env node -import { Argument, Command, program } from '@commander-js/extra-typings'; +import { Argument, createCommand, program } from '@commander-js/extra-typings'; import chalk from 'chalk'; -import { makeInitCommand } from '../src/init/index.js'; +import { write } from 'node:fs'; +import { convertToHex } from '../src/colors/index.js'; +import { createTokensPackage } from '../src/init/createTokensPackage.js'; import migrations from '../src/migrations/index.js'; -import { run } from '../src/tokens/build.js'; +import { typography } from '../src/tokens/build/formats/css'; +import { buildTokens } from '../src/tokens/build/index.js'; +import { createTokens } from '../src/tokens/create//index.js'; -program.name('Designsystemet').description('CLI for working with Designsystemet'); +program.name('Designsystemet').description('CLI for working with Designsystemet').showHelpAfterError(); -program - .command('tokens') - .showHelpAfterError() - .description('run Designsystemet token builder') - .option('-t, --tokens [string]', `Path to ${chalk.blue('design-tokens')}`, './design-tokens') - .option('-o, --out [string]', `Output directory for built ${chalk.blue('design-tokens')}`, './dist/tokens') - .option('-p, --preview', 'Generate preview token.ts files', false) - .action((opts) => { - const tokens = typeof opts.tokens === 'string' ? opts.tokens : './design-tokens'; - const out = typeof opts.out === 'string' ? opts.out : './dist/tokens'; - const preview = opts.preview; - console.log(`Bulding tokens in ${chalk.green(tokens)}`); - return run({ tokens, out, preview }); - }); +function makeTokenCommands() { + const tokenCmd = createCommand('tokens'); + + tokenCmd + .command('build') + .description('Build Designsystemet tokens') + .option('-t, --tokens ', `Path to ${chalk.blue('design-tokens')}`, './design-tokens') + .option('-o, --out ', `Output directory for built ${chalk.blue('design-tokens')}`, './dist/tokens') + .option('-p, --preview', 'Generate preview token.ts files', false) + .action((opts) => { + const tokens = typeof opts.tokens === 'string' ? opts.tokens : './design-tokens'; + const out = typeof opts.out === 'string' ? opts.out : './dist/tokens'; + const preview = opts.preview; + console.log(`Bulding tokens in ${chalk.green(tokens)}`); + return buildTokens({ tokens, out, preview }); + }); + + tokenCmd + .command('create') + .description('Create Designsystemet tokens') + .option('-w, --write [string]', `Output directory for created ${chalk.blue('design-tokens')}`) + .option('-a, --accent ', `Accent hex color`) + .option('-n, --neutral ', `Neutral hex color`) + .option('-b1, --brand1 ', `Brand1 hex color`) + .option('-b2, --brand2 ', `Brand2 hex color`) + .option('-b3, --brand3 ', `Brand3 hex color`) + .option('-f, --font-family ', `Font family`) + .action(async (opts) => { + // const out = typeof opts.out === 'string' ? opts.out : './dist/tokens'; + console.log(`Creating tokens with options ${chalk.green(JSON.stringify(opts))}`); + const family = typeof opts.fontFamily === 'string' ? opts.fontFamily : 'Inter'; + const write = typeof opts.write === 'boolean' ? './design-tokens' : opts.write; + + const props = { + colors: { + accent: convertToHex(opts.accent), + neutral: convertToHex(opts.neutral), + brand1: convertToHex(opts.brand1), + brand2: convertToHex(opts.brand2), + brand3: convertToHex(opts.brand3), + }, + typography: { + family, + }, + write, + }; + + await createTokens(props); + }); + + return tokenCmd; +} + +program.addCommand(makeTokenCommands()); program .command('migrate') - .showHelpAfterError() .description('run a Designsystemet migration') .addArgument(new Argument('[migration]', 'Available migrations').choices(Object.keys(migrations))) .option('-l --list', 'List available migrations') @@ -53,6 +96,12 @@ program } }); -program.addCommand(makeInitCommand(new Command('init'))); +program + .command('init') + .description('create an initial token structure for Designsystemet') + .addArgument(new Argument('', 'Target directory for the generated code')) + .action(async (targetDir) => { + await createTokensPackage(targetDir); + }); await program.parseAsync(process.argv); diff --git a/packages/cli/package.json b/packages/cli/package.json index 54867f897b..c9e7ebaab4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@digdir/designsystemet", - "version": "0.1.0-next.23", + "version": "0.1.0-next.30", "description": "CLI for Designsystemet", "author": "Designsystemet team", "repository": { @@ -19,6 +19,10 @@ "./color": { "types": "./dist/types/src/colors/index.d.ts", "import": "./dist/src/colors/index.js" + }, + "./tokens": { + "types": "./dist/types/src/tokens/index.d.ts", + "import": "./dist/src/tokens/index.js" } }, "publishConfig": { @@ -26,8 +30,8 @@ }, "scripts": { "designsystemet": "tsx ./bin/designsystemet.ts", - "build:tokens": "yarn clean:theme && yarn designsystemet tokens -p -t ../../design-tokens -o ../../packages/theme/brand", - "build:tokens:debug": "yarn clean:theme && tsx --inspect-brk ./bin/designsystemet.ts tokens -p -t ../../design-tokens -o ../../packages/theme/brand", + "build:tokens": "yarn clean:theme && yarn designsystemet tokens build -p -t ../../design-tokens -o ../../packages/theme/brand", + "build:tokens:debug": "yarn clean:theme && tsx --inspect-brk ./bin/designsystemet.ts tokens build -p -t ../../design-tokens -o ../../packages/theme/brand", "build": "tsup && yarn build:types", "build:swc": "yarn clean && swc src bin --copy-files -d dist && yarn build:types", "build:types": "tsc --emitDeclarationOnly --declaration", diff --git a/packages/cli/src/colors/index.ts b/packages/cli/src/colors/index.ts index f402c8aa25..59dfd41893 100644 --- a/packages/cli/src/colors/index.ts +++ b/packages/cli/src/colors/index.ts @@ -1,3 +1,3 @@ -export * from './colorUtils'; -export * from './themeUtils'; -export * from './types'; +export * from './utils.js'; +export * from './theme.js'; +export * from './types.js'; diff --git a/packages/cli/src/colors/themeUtils.ts b/packages/cli/src/colors/theme.ts similarity index 88% rename from packages/cli/src/colors/themeUtils.ts rename to packages/cli/src/colors/theme.ts index d0c21e1a1a..575a26af30 100644 --- a/packages/cli/src/colors/themeUtils.ts +++ b/packages/cli/src/colors/theme.ts @@ -2,31 +2,29 @@ import type { CssColor } from '@adobe/leonardo-contrast-colors'; import { BackgroundColor, Color, Theme } from '@adobe/leonardo-contrast-colors'; import { Hsluv } from 'hsluv'; -import { getContrastFromHex, getContrastFromLightness, getLightnessFromHex } from './colorUtils'; -import type { ColorInfo, ColorNumber, ContrastMode, Mode, ThemeInfo } from './types'; +import type { ColorInfo, ColorMode, ColorNumber, ContrastMode, GlobalColors, ThemeColors, ThemeInfo } from './types.js'; +import { getContrastFromHex, getContrastFromLightness, getLightnessFromHex } from './utils.js'; + +export const baseColors: Record = { + blue: '#0A71C0', + green: '#078D19', + orange: '#CA5C21', + purple: '#663299', + red: '#C01B1B', + yellow: '#EABF28', +}; -const blueBaseColor = '#0A71C0'; -const greenBaseColor = '#078D19'; -const orangeBaseColor = '#CA5C21'; -const purpleBaseColor = '#663299'; -const redBaseColor = '#C01B1B'; -const yellowBaseColor = '#EABF28'; +type Colors = Record; export type ColorError = 'none' | 'decorative' | 'interaction'; type GlobalGenType = { - themeMode?: Mode | 'all'; + themeMode?: ColorMode | 'all'; contrastMode?: ContrastMode; }; type ThemeGenType = { - colors: { - accent: CssColor; - neutral: CssColor; - brand1: CssColor; - brand2: CssColor; - brand3: CssColor; - }; + colors: Colors; contrastMode?: ContrastMode; }; @@ -38,7 +36,7 @@ type ThemeGenType = { * @param contrastMode Contrast mode * @returns */ -const generateThemeColor = (color: CssColor, mode: Mode, contrastMode: 'aa' | 'aaa' = 'aa') => { +const generateThemeColor = (color: CssColor, mode: ColorMode, contrastMode: 'aa' | 'aaa' = 'aa') => { const leoBackgroundColor = new BackgroundColor({ name: 'backgroundColor', colorKeys: ['#ffffff'], @@ -103,7 +101,11 @@ const generateThemeColor = (color: CssColor, mode: Mode, contrastMode: 'aa' | 'a * @param color The base color that is used to generate the color scale * @param mode The mode of the theme */ -export const generateScaleForColor = (color: CssColor, mode: Mode, contrastMode: 'aa' | 'aaa' = 'aa'): ColorInfo[] => { +export const generateScaleForColor = ( + color: CssColor, + mode: ColorMode, + contrastMode: 'aa' | 'aaa' = 'aa', +): ColorInfo[] => { const themeColor = generateThemeColor(color, mode, contrastMode); const leoBackgroundColor = new BackgroundColor({ @@ -150,7 +152,7 @@ export const generateScaleForColor = (color: CssColor, mode: Mode, contrastMode: * * @param color The base color that is used to generate the color theme */ -export const generateThemeForColor = (color: CssColor, contrastMode: 'aa' | 'aaa' = 'aa') => { +export const generateThemeForColor = (color: CssColor, contrastMode: 'aa' | 'aaa' = 'aa'): ThemeInfo => { const lightScale = generateScaleForColor(color, 'light', contrastMode); const darkScale = generateScaleForColor(color, 'dark', contrastMode); const contrastScale = generateScaleForColor(color, 'contrast', contrastMode); @@ -159,16 +161,16 @@ export const generateThemeForColor = (color: CssColor, contrastMode: 'aa' | 'aaa light: lightScale, dark: darkScale, contrast: contrastScale, - } as ThemeInfo; + }; }; -export const generateGlobalColors = ({ contrastMode = 'aa' }: GlobalGenType) => { - const blueTheme = generateThemeForColor(blueBaseColor, contrastMode); - const greenTheme = generateThemeForColor(greenBaseColor, contrastMode); - const orangeTheme = generateThemeForColor(orangeBaseColor, contrastMode); - const purpleTheme = generateThemeForColor(purpleBaseColor, contrastMode); - const redTheme = generateThemeForColor(redBaseColor, contrastMode); - const yellowTheme = generateThemeForColor(yellowBaseColor, contrastMode); +export const generateGlobalColors = ({ contrastMode = 'aa' }: GlobalGenType): Record => { + const blueTheme = generateThemeForColor(baseColors.blue, contrastMode); + const greenTheme = generateThemeForColor(baseColors.green, contrastMode); + const orangeTheme = generateThemeForColor(baseColors.orange, contrastMode); + const purpleTheme = generateThemeForColor(baseColors.purple, contrastMode); + const redTheme = generateThemeForColor(baseColors.red, contrastMode); + const yellowTheme = generateThemeForColor(baseColors.yellow, contrastMode); return { blue: blueTheme, @@ -187,7 +189,7 @@ export const generateGlobalColors = ({ contrastMode = 'aa' }: GlobalGenType) => * @param contrastMode The contrast mode to use * @returns */ -export const generateColorTheme = ({ colors, contrastMode = 'aa' }: ThemeGenType) => { +export const generateColorTheme = ({ colors, contrastMode = 'aa' }: ThemeGenType): Record => { const accentTheme = generateThemeForColor(colors.accent, contrastMode); const neutralTheme = generateThemeForColor(colors.neutral, contrastMode); const brand1Theme = generateThemeForColor(colors.brand1, contrastMode); diff --git a/packages/cli/src/colors/types.ts b/packages/cli/src/colors/types.ts index 2cd77bc5d9..faee263ad0 100644 --- a/packages/cli/src/colors/types.ts +++ b/packages/cli/src/colors/types.ts @@ -1,9 +1,10 @@ import type { CssColor } from '@adobe/leonardo-contrast-colors'; -export type Mode = 'light' | 'dark' | 'contrast'; +export type ColorMode = 'light' | 'dark' | 'contrast'; export type ContrastMode = 'aa' | 'aaa'; export type ColorNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15; -export type ColorType = 'accent' | 'neutral' | 'brand1' | 'brand2' | 'brand3'; +export type GlobalColors = 'red' | 'blue' | 'green' | 'orange' | 'purple' | 'yellow'; +export type ThemeColors = 'accent' | 'neutral' | 'brand1' | 'brand2' | 'brand3'; export type ColorInfo = { hex: CssColor; number: ColorNumber; diff --git a/packages/cli/src/colors/colorUtils.ts b/packages/cli/src/colors/utils.ts similarity index 94% rename from packages/cli/src/colors/colorUtils.ts rename to packages/cli/src/colors/utils.ts index f4f398340d..4f190e4e73 100644 --- a/packages/cli/src/colors/colorUtils.ts +++ b/packages/cli/src/colors/utils.ts @@ -282,8 +282,8 @@ export const getLightnessFromHex = (hex: string) => { /** * Get the contrast ratio between two HEX colors * - * @param {CssColor} color1 The first color - * @param {CssColor} color2 The second color + * @param color1 The first color + * @param color2 The second color * @returns */ export const getContrastFromHex = (color1: CssColor, color2: CssColor) => { @@ -327,9 +327,9 @@ export const getContrastFromLightness = (lightness: number, mainColor: CssColor, /** * Check if two colors have enough contrast to be used together * - * @param {CssColor} color1 The first color - * @param {CssColor} color2 The second color - * @returns {boolean} If the colors have enough contrast + * @param color1 The first color + * @param color2 The second color + * @returns If the colors have enough contrast */ export const areColorsContrasting = (color1: CssColor, color2: CssColor, type: 'decorative' | 'aa' | 'aaa' = 'aa') => { const contrast = getContrastFromHex(color1, color2); @@ -361,9 +361,24 @@ export const getApcaContrastLc = (textColor: CssColor, backgroundColor: CssColor /** * Check if aa string value is a HEX color * - * @param {string} hex The string to check - * @returns {boolean} If the string is a HEX color + * @param hex The string to check + * @returns If the string is a HEX color */ export const isHexColor = (hex: string) => { return typeof hex === 'string' && hex.length === 6 && !Number.isNaN(Number('0x' + hex)); }; + +/** + * + * @param color + * @returns + */ +export const convertToHex = (color?: string): CssColor => { + if (!color) { + return '#000000'; + } + if (color.startsWith('#')) { + return color as CssColor; + } + return chroma(color).hex() as CssColor; +}; diff --git a/packages/cli/src/init/generateMetadataJson.ts b/packages/cli/src/init/generateMetadataJson.ts index 7d35cf6910..931f2ad72f 100644 --- a/packages/cli/src/init/generateMetadataJson.ts +++ b/packages/cli/src/init/generateMetadataJson.ts @@ -7,8 +7,10 @@ interface Metadata { export default function generateMetadataJson(modes: Array<'Light' | 'Dark' | 'Contrast'>, themes: string[]): Metadata { return { tokenSetOrder: [ - 'primitives/modes/globals', - 'primitives/modes/typography/default', + 'primitives/globals', + 'primitives/size/default', + 'primitives/modes/typography/primary/theme', + 'primitives/modes/typography/secondary/theme', ...modes.flatMap((mode) => [ `primitives/modes/colors/${normalizeTokenSetName(mode)}/global`, ...themes.map( @@ -18,6 +20,7 @@ export default function generateMetadataJson(modes: Array<'Light' | 'Dark' | 'Co ...themes.map((theme) => `themes/${normalizeTokenSetName(theme)}`), 'semantic/color', 'semantic/style', + 'Figma/components', ], }; } diff --git a/packages/cli/src/init/generateThemesJson.ts b/packages/cli/src/init/generateThemesJson.ts index 90e8c71888..b3dfd6f425 100644 --- a/packages/cli/src/init/generateThemesJson.ts +++ b/packages/cli/src/init/generateThemesJson.ts @@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'; import { type ThemeObject, TokenSetStatus } from '@tokens-studio/types'; -import { Theme } from '@adobe/leonardo-contrast-colors'; import { normalizeTokenSetName } from './utils.js'; export default function generateThemesJson( @@ -28,14 +27,14 @@ function generateSizeGroup(): ThemeObject[] { }, group: 'Size', }, - { - id: randomUUID(), - name: 'compact', - selectedTokenSets: { - 'primitives/size/compact': TokenSetStatus.ENABLED, - }, - group: 'Size', - }, + // { + // id: randomUUID(), + // name: 'compact', + // selectedTokenSets: { + // 'primitives/size/compact': TokenSetStatus.ENABLED, + // }, + // group: 'Size', + // }, ]; } diff --git a/packages/cli/src/init/template/default-files/design-tokens/Figma/components.json b/packages/cli/src/init/template/default-files/design-tokens/Figma/components.json new file mode 100644 index 0000000000..1a98c25a31 --- /dev/null +++ b/packages/cli/src/init/template/default-files/design-tokens/Figma/components.json @@ -0,0 +1,22 @@ +{ + "switch": { + "circle": { + "small": { + "$type": "sizing", + "$value": "{sizing.5} - {switch.border}" + }, + "medium": { + "$type": "sizing", + "$value": "{sizing.6} - {switch.border}" + }, + "large": { + "$type": "sizing", + "$value": "{sizing.7} - {switch.border}" + } + }, + "border": { + "$type": "sizing", + "$value": "4" + } + } +} \ No newline at end of file diff --git a/packages/cli/src/init/template/default-files/design-tokens/themes/theme.json b/packages/cli/src/init/template/default-files/design-tokens/themes/theme.json new file mode 100644 index 0000000000..0bb526a0f7 --- /dev/null +++ b/packages/cli/src/init/template/default-files/design-tokens/themes/theme.json @@ -0,0 +1,334 @@ +{ + "color": { + "accent": { + "1": { + "$type": "color", + "$value": "{theme.accent.1}" + }, + "2": { + "$type": "color", + "$value": "{theme.accent.2}" + }, + "3": { + "$type": "color", + "$value": "{theme.accent.3}" + }, + "4": { + "$type": "color", + "$value": "{theme.accent.4}" + }, + "5": { + "$type": "color", + "$value": "{theme.accent.5}" + }, + "6": { + "$type": "color", + "$value": "{theme.accent.6}" + }, + "7": { + "$type": "color", + "$value": "{theme.accent.7}" + }, + "8": { + "$type": "color", + "$value": "{theme.accent.8}" + }, + "9": { + "$type": "color", + "$value": "{theme.accent.9}" + }, + "10": { + "$type": "color", + "$value": "{theme.accent.10}" + }, + "11": { + "$type": "color", + "$value": "{theme.accent.11}" + }, + "12": { + "$type": "color", + "$value": "{theme.accent.12}" + }, + "13": { + "$type": "color", + "$value": "{theme.accent.13}" + }, + "contrast-1": { + "$type": "color", + "$value": "{theme.accent.contrast-1}" + }, + "contrast-2": { + "$type": "color", + "$value": "{theme.accent.contrast-2}" + } + }, + "neutral": { + "1": { + "$type": "color", + "$value": "{theme.neutral.1}" + }, + "2": { + "$type": "color", + "$value": "{theme.neutral.2}" + }, + "3": { + "$type": "color", + "$value": "{theme.neutral.3}" + }, + "4": { + "$type": "color", + "$value": "{theme.neutral.4}" + }, + "5": { + "$type": "color", + "$value": "{theme.neutral.5}" + }, + "6": { + "$type": "color", + "$value": "{theme.neutral.6}" + }, + "7": { + "$type": "color", + "$value": "{theme.neutral.7}" + }, + "8": { + "$type": "color", + "$value": "{theme.neutral.8}" + }, + "9": { + "$type": "color", + "$value": "{theme.neutral.9}" + }, + "10": { + "$type": "color", + "$value": "{theme.neutral.10}" + }, + "11": { + "$type": "color", + "$value": "{theme.neutral.11}" + }, + "12": { + "$type": "color", + "$value": "{theme.neutral.12}" + }, + "13": { + "$type": "color", + "$value": "{theme.neutral.13}" + }, + "contrast-1": { + "$type": "color", + "$value": "{theme.neutral.contrast-1}" + }, + "contrast-2": { + "$type": "color", + "$value": "{theme.neutral.contrast-2}" + } + }, + "brand1": { + "1": { + "$type": "color", + "$value": "{theme.brand1.1}" + }, + "2": { + "$type": "color", + "$value": "{theme.brand1.2}" + }, + "3": { + "$type": "color", + "$value": "{theme.brand1.3}" + }, + "4": { + "$type": "color", + "$value": "{theme.brand1.4}" + }, + "5": { + "$type": "color", + "$value": "{theme.brand1.5}" + }, + "6": { + "$type": "color", + "$value": "{theme.brand1.6}" + }, + "7": { + "$type": "color", + "$value": "{theme.brand1.7}" + }, + "8": { + "$type": "color", + "$value": "{theme.brand1.8}" + }, + "9": { + "$type": "color", + "$value": "{theme.brand1.9}" + }, + "10": { + "$type": "color", + "$value": "{theme.brand1.10}" + }, + "11": { + "$type": "color", + "$value": "{theme.brand1.11}" + }, + "12": { + "$type": "color", + "$value": "{theme.brand1.12}" + }, + "13": { + "$type": "color", + "$value": "{theme.brand1.13}" + }, + "contrast-1": { + "$type": "color", + "$value": "{theme.brand1.contrast-1}" + }, + "contrast-2": { + "$type": "color", + "$value": "{theme.brand1.contrast-2}" + } + }, + "brand2": { + "1": { + "$type": "color", + "$value": "{theme.brand2.1}" + }, + "2": { + "$type": "color", + "$value": "{theme.brand2.2}" + }, + "3": { + "$type": "color", + "$value": "{theme.brand2.3}" + }, + "4": { + "$type": "color", + "$value": "{theme.brand2.4}" + }, + "5": { + "$type": "color", + "$value": "{theme.brand2.5}" + }, + "6": { + "$type": "color", + "$value": "{theme.brand2.6}" + }, + "7": { + "$type": "color", + "$value": "{theme.brand2.7}" + }, + "8": { + "$type": "color", + "$value": "{theme.brand2.8}" + }, + "9": { + "$type": "color", + "$value": "{theme.brand2.9}" + }, + "10": { + "$type": "color", + "$value": "{theme.brand2.10}" + }, + "11": { + "$type": "color", + "$value": "{theme.brand2.11}" + }, + "12": { + "$type": "color", + "$value": "{theme.brand2.12}" + }, + "13": { + "$type": "color", + "$value": "{theme.brand2.13}" + }, + "contrast-1": { + "$type": "color", + "$value": "{theme.brand2.contrast-1}" + }, + "contrast-2": { + "$type": "color", + "$value": "{theme.brand2.contrast-2}" + } + }, + "brand3": { + "1": { + "$type": "color", + "$value": "{theme.brand3.1}" + }, + "2": { + "$type": "color", + "$value": "{theme.brand3.2}" + }, + "3": { + "$type": "color", + "$value": "{theme.brand3.3}" + }, + "4": { + "$type": "color", + "$value": "{theme.brand3.4}" + }, + "5": { + "$type": "color", + "$value": "{theme.brand3.5}" + }, + "6": { + "$type": "color", + "$value": "{theme.brand3.6}" + }, + "7": { + "$type": "color", + "$value": "{theme.brand3.7}" + }, + "8": { + "$type": "color", + "$value": "{theme.brand3.8}" + }, + "9": { + "$type": "color", + "$value": "{theme.brand3.9}" + }, + "10": { + "$type": "color", + "$value": "{theme.brand3.10}" + }, + "11": { + "$type": "color", + "$value": "{theme.brand3.11}" + }, + "12": { + "$type": "color", + "$value": "{theme.brand3.12}" + }, + "13": { + "$type": "color", + "$value": "{theme.brand3.13}" + }, + "contrast-1": { + "$type": "color", + "$value": "{theme.brand3.contrast-1}" + }, + "contrast-2": { + "$type": "color", + "$value": "{theme.brand3.contrast-2}" + } + } + }, + "font": { + "family": { + "$type": "fontFamilies", + "$value": "{theme.main}" + } + }, + "font-weight": { + "medium": { + "$type": "fontWeights", + "$value": "{theme.bold}" + }, + "semibold": { + "$type": "fontWeights", + "$value": "{theme.extra-bold}" + }, + "regular": { + "$type": "fontWeights", + "$value": "{theme.regular}" + } + } +} \ No newline at end of file diff --git a/packages/cli/src/tokens/actions.ts b/packages/cli/src/tokens/build/actions.ts similarity index 100% rename from packages/cli/src/tokens/actions.ts rename to packages/cli/src/tokens/build/actions.ts diff --git a/packages/cli/src/tokens/configs.ts b/packages/cli/src/tokens/build/configs.ts similarity index 100% rename from packages/cli/src/tokens/configs.ts rename to packages/cli/src/tokens/build/configs.ts diff --git a/packages/cli/src/tokens/formats/css.ts b/packages/cli/src/tokens/build/formats/css.ts similarity index 100% rename from packages/cli/src/tokens/formats/css.ts rename to packages/cli/src/tokens/build/formats/css.ts diff --git a/packages/cli/src/tokens/formats/js-tokens.ts b/packages/cli/src/tokens/build/formats/js-tokens.ts similarity index 100% rename from packages/cli/src/tokens/formats/js-tokens.ts rename to packages/cli/src/tokens/build/formats/js-tokens.ts diff --git a/packages/cli/src/tokens/build.ts b/packages/cli/src/tokens/build/index.ts similarity index 98% rename from packages/cli/src/tokens/build.ts rename to packages/cli/src/tokens/build/index.ts index e5fb4f0535..ee54a878bb 100644 --- a/packages/cli/src/tokens/build.ts +++ b/packages/cli/src/tokens/build/index.ts @@ -24,7 +24,7 @@ type Options = { const sd = new StyleDictionary(); -export async function run(options: Options): Promise { +export async function buildTokens(options: Options): Promise { const tokensDir = options.tokens; const storefrontOutDir = path.resolve('../../apps/storefront/tokens'); const outPath = path.resolve(options.out); diff --git a/packages/cli/src/tokens/transformers.ts b/packages/cli/src/tokens/build/transformers.ts similarity index 100% rename from packages/cli/src/tokens/transformers.ts rename to packages/cli/src/tokens/build/transformers.ts diff --git a/packages/cli/src/tokens/utils/entryfile.ts b/packages/cli/src/tokens/build/utils/entryfile.ts similarity index 100% rename from packages/cli/src/tokens/utils/entryfile.ts rename to packages/cli/src/tokens/build/utils/entryfile.ts diff --git a/packages/cli/src/tokens/utils/noCase.ts b/packages/cli/src/tokens/build/utils/noCase.ts similarity index 100% rename from packages/cli/src/tokens/utils/noCase.ts rename to packages/cli/src/tokens/build/utils/noCase.ts diff --git a/packages/cli/src/tokens/utils/permutateThemes.ts b/packages/cli/src/tokens/build/utils/permutateThemes.ts similarity index 100% rename from packages/cli/src/tokens/utils/permutateThemes.ts rename to packages/cli/src/tokens/build/utils/permutateThemes.ts diff --git a/packages/cli/src/tokens/utils/utils.ts b/packages/cli/src/tokens/build/utils/utils.ts similarity index 100% rename from packages/cli/src/tokens/utils/utils.ts rename to packages/cli/src/tokens/build/utils/utils.ts diff --git a/packages/cli/src/tokens/create/README.md b/packages/cli/src/tokens/create/README.md new file mode 100644 index 0000000000..0c35c5e982 --- /dev/null +++ b/packages/cli/src/tokens/create/README.md @@ -0,0 +1,3 @@ +```sh +yarn workspace @digdir/designsystemet designsystemet tokens create --accent AD00BA --neutral 749ACB --brand1 D03539 --brand2 20E53A --brand3 F2F51E --font-family Arial --write ./../../../sandbox/test-tokens-august/design-tokens +``` diff --git a/packages/cli/src/tokens/create/index.ts b/packages/cli/src/tokens/create/index.ts new file mode 100644 index 0000000000..265a772e04 --- /dev/null +++ b/packages/cli/src/tokens/create/index.ts @@ -0,0 +1,191 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import type { CssColor } from '@adobe/leonardo-contrast-colors'; +import * as R from 'ramda'; +import { baseColors, generateScaleForColor } from '../../colors/index.js'; +import type { ColorInfo, ColorMode, ThemeColors } from '../../colors/index.js'; +import generateMetadataJson from '../../init/generateMetadataJson.js'; +import generateThemesJson from '../../init/generateThemesJson.js'; + +type Colors = Record; +type Typography = Record; +type TypographyModes = 'primary' | 'secondary'; + +type CreateTokens = { + colors: Colors; + typography: Typography; + write?: string; +}; + +type File = { + data: string; + path: string; + filePath: string; +}; + +type DesignTokens = Record; +type Tokens2ary = Record>; +type Tokens1ary = Record; +type Tokens = Tokens1ary | Tokens2ary; + +type Collection = 'theme' | 'global'; + +const DIRNAME: string = import.meta.dirname || __dirname; +const DEFAULT_FILES_PATH = path.join(DIRNAME, '../../init/template/default-files/design-tokens/'); + +const createColorTokens = (colorArray: ColorInfo[]): DesignTokens => { + const obj: DesignTokens = {}; + const $type = 'color'; + for (let i = 0; i < colorArray.length; i++) { + if (i === 13 && colorArray.length >= 14) { + obj['contrast-1'] = { + $type, + $value: colorArray[i].hex, + }; + } else if (i === 14 && colorArray.length >= 15) { + obj['contrast-2'] = { + $type, + $value: colorArray[i].hex, + }; + } else { + obj[i + 1] = { $type, $value: colorArray[i].hex }; + } + } + return obj; +}; + +const generateTypographyTokens = ({ family }: Typography): Tokens => { + return { + theme: { + main: { + $type: 'fontFamilies', + $value: family, + }, + bold: { + $type: 'fontWeights', + $value: 'Medium', + }, + 'extra-bold': { + $type: 'fontWeights', + $value: 'Semi bold', + }, + regular: { + $type: 'fontWeights', + $value: 'Regular', + }, + }, + }; +}; + +const generateThemeTokens = (theme: ColorMode, colors: Colors): Tokens => { + const accentColors = generateScaleForColor(colors.accent, theme); + const neutralColors = generateScaleForColor(colors.neutral, theme); + const brand1Colors = generateScaleForColor(colors.brand1, theme); + const brand2Colors = generateScaleForColor(colors.brand2, theme); + const brand3Colors = generateScaleForColor(colors.brand3, theme); + + return { + theme: { + accent: createColorTokens(accentColors), + neutral: createColorTokens(neutralColors), + brand1: createColorTokens(brand1Colors), + brand2: createColorTokens(brand2Colors), + brand3: createColorTokens(brand3Colors), + }, + }; +}; + +const generateColorModeFile = (folder: ColorMode, name: Collection, tokens: Tokens, outPath: string): File => { + const path = `${outPath}/primitives/modes/colors/${folder}`; + return { + data: `${JSON.stringify(tokens, null, 2)}\n`, + path, + filePath: `${path}/${name}.json`, + }; +}; + +const generateTypographyFile = (folder: TypographyModes, name: Collection, tokens: Tokens, outPath: string): File => { + const path = `${outPath}/primitives/modes/typography/${folder}`; + return { + data: `${JSON.stringify(tokens, null, 2)}\n`, + path, + filePath: `${path}/${name}.json`, + }; +}; + +const generateGlobalTokens = (theme: ColorMode) => { + const blueScale = generateScaleForColor(baseColors.blue, theme); + const greenScale = generateScaleForColor(baseColors.green, theme); + const orangeScale = generateScaleForColor(baseColors.orange, theme); + const purpleScale = generateScaleForColor(baseColors.purple, theme); + const redScale = generateScaleForColor(baseColors.red, theme); + const yellowScale = generateScaleForColor(baseColors.yellow, theme); + + return { + global: { + blue: createColorTokens(blueScale), + green: createColorTokens(greenScale), + orange: createColorTokens(orangeScale), + purple: createColorTokens(purpleScale), + red: createColorTokens(redScale), + yellow: createColorTokens(yellowScale), + }, + }; +}; + +export const createTokens = async (opts: CreateTokens) => { + const { colors, write, typography } = opts; + + const tokens = { + colors: { + light: { + theme: generateThemeTokens('light', colors), + global: generateGlobalTokens('light'), + }, + dark: { theme: generateThemeTokens('dark', colors), global: generateGlobalTokens('dark') }, + contrast: { theme: generateThemeTokens('contrast', colors), global: generateGlobalTokens('contrast') }, + }, + typography: { + primary: generateTypographyTokens(typography), + }, + }; + + if (R.isNotNil(write)) { + const targetDir = path.resolve(process.cwd(), String(write)); + await fs.mkdir(targetDir, { recursive: true }); + + // Generate metadata and themes json for Token Studio and build script + console.log('Generating metadata and themes files'); + const $theme = generateThemesJson(['Light', 'Dark', 'Contrast'], ['theme']); + const $metadata = generateMetadataJson(['Light', 'Dark', 'Contrast'], ['theme']); + + await fs.writeFile(path.join(targetDir, '$themes.json'), JSON.stringify($theme, null, 2)); + await fs.writeFile(path.join(targetDir, '$metadata.json'), JSON.stringify($metadata, null, 2)); + + console.log(`Copy files to ${targetDir}`); + await fs.cp(DEFAULT_FILES_PATH, targetDir, { + recursive: true, + }); + + const files: File[] = [ + generateColorModeFile('light', 'theme', tokens.colors.light.theme, targetDir), + generateColorModeFile('light', 'global', tokens.colors.light.global, targetDir), + generateColorModeFile('dark', 'theme', tokens.colors.dark.theme, targetDir), + generateColorModeFile('dark', 'global', tokens.colors.dark.global, targetDir), + generateColorModeFile('contrast', 'theme', tokens.colors.contrast.theme, targetDir), + generateColorModeFile('contrast', 'global', tokens.colors.contrast.global, targetDir), + generateTypographyFile('primary', 'theme', tokens.typography.primary, targetDir), + generateTypographyFile('secondary', 'theme', tokens.typography.primary, targetDir), + ]; + + for (const file of files) { + const path_ = path.resolve(file.path); + const filePath = path.resolve(file.filePath); + console.log(`Writing file ${filePath}`); + await fs.mkdir(path_, { recursive: true }); + await fs.writeFile(filePath, file.data, { encoding: 'utf-8' }); + } + } + + return tokens; +}; diff --git a/packages/cli/src/tokens/index.ts b/packages/cli/src/tokens/index.ts new file mode 100644 index 0000000000..9ff87c8c45 --- /dev/null +++ b/packages/cli/src/tokens/index.ts @@ -0,0 +1 @@ +export { createTokens } from './create/index.js';