Skip to content

Commit

Permalink
feat(cli): ✨ Add option for defining theme name (#2374)
Browse files Browse the repository at this point in the history
resolves #2334

- Added new option to `--theme` to cli for defining theme name.
  - Will add new theme or overwrite existing theme.
- Added new script in cli (`update:template`) for updating template
files from our "master" `design-tokens` in root.
- Added textfield in theme-builder tokens modal for defining theme name.
- Not a fan of having code, but commented our template generation for
`$metadata` and `$themes` as this is an internal improvement that can be
done later.
- Some updates to components used in theme-builder to get desired user
interface.
  • Loading branch information
mimarz authored Sep 16, 2024
1 parent b201787 commit 4fb70d1
Show file tree
Hide file tree
Showing 20 changed files with 3,337 additions and 86 deletions.
8 changes: 5 additions & 3 deletions apps/_components/src/CodeSnippet/CodeSnippet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ const plugins = [
];

type CodeSnippetProps = {
language?: 'css' | 'html' | 'ts' | 'markdown' | 'js' | 'json' | 'sh';
language?: 'css' | 'html' | 'ts' | 'markdown' | 'json';
children?: string;
className?: string;
syntax?: string;
};

const CodeSnippet = ({
language = 'markdown',
children = '',
className,
syntax = 'js',
}: CodeSnippetProps) => {
const [toolTipText, setToolTipText] = useState('Kopier');
const [snippet, setSnippet] = useState('');
Expand All @@ -47,7 +49,7 @@ const CodeSnippet = ({
) {
try {
const formatted = await format(children, {
parser: language === 'ts' ? 'typescript' : language,
parser: language === 'ts' ? 'babel-ts' : language,
plugins,
});
setSnippet(formatted);
Expand Down Expand Up @@ -92,7 +94,7 @@ const CodeSnippet = ({
</Tooltip>
<SyntaxHighlighter
style={nightOwl}
language={language}
language={syntax}
customStyle={{
fontSize: '15px',
margin: 0,
Expand Down
2 changes: 1 addition & 1 deletion apps/theme/components/TokenModal/TokenModal.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
}

.snippet {
max-height: 350px;
max-height: 250px;
overflow: auto;
}

Expand Down
24 changes: 21 additions & 3 deletions apps/theme/components/TokenModal/TokenModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Link,
Modal,
Paragraph,
Textfield,
} from '@digdir/designsystemet-react';
import { createTokens } from '@digdir/designsystemet/tokens/create.js';
import { CodeSnippet } from '@repo/components';
Expand Down Expand Up @@ -38,13 +39,15 @@ export const TokenModal = ({

const [lightThemeSnippet, setLightThemeSnippet] = useState('');
const [darkThemeSnippet, setDarkThemeSnippet] = useState('');
const [themeName, setThemeName] = useState('theme');

const cliSnippet = `npx @digdir/designsystemet tokens create \\
--accent "${accentColor}" \\
--neutral "${neutralColor}" \\
--brand1 "${brand1Color}" \\
--brand2 "${brand2Color}" \\
--brand3 "${brand3Color}" \\
--theme "${themeName}" \\
--write
`;

Expand All @@ -58,6 +61,7 @@ export const TokenModal = ({
brand3: brand3Color,
},
typography: { fontFamily: 'Inter' },
themeName: 'theme',
});

setLightThemeSnippet(toFigmaSnippet(tokens.colors.light.theme));
Expand Down Expand Up @@ -93,6 +97,20 @@ export const TokenModal = ({
<Heading level={3} size='xs' spacing>
Alt 1. Design tokens
</Heading>
<Textfield
label='Navn på tema'
description="Kun bokstaver, tall og bindestrek. Eks: 'mitt-tema'"
value={themeName}
onChange={(e) => {
const value = e.currentTarget.value
.replace(/\s+/g, '-')
.replace(/[^A-Z0-9-]+/gi, '')
.toLowerCase();

setThemeName(value);
}}
style={{ marginBlock: 'var(--ds-spacing-4)' }}
></Textfield>
<Paragraph spacing>
Kopier kommandosnutten under og kjør på maskinen din for å generere
alle design tokens (json-filer). Sørg for at du har{' '}
Expand All @@ -105,7 +123,7 @@ export const TokenModal = ({
className={classes.snippet}
style={{ marginBottom: 'var(--ds-spacing-8)' }}
>
<CodeSnippet language='js'>{cliSnippet}</CodeSnippet>
<CodeSnippet syntax='shell'>{cliSnippet}</CodeSnippet>
</div>
<Heading level={3} size='xs' spacing>
Alt 2. Figma plugin
Expand Down Expand Up @@ -138,15 +156,15 @@ export const TokenModal = ({
Light Mode
</Heading>
<div className={classes.snippet}>
<CodeSnippet language='js'>{lightThemeSnippet}</CodeSnippet>
<CodeSnippet language='json'>{lightThemeSnippet}</CodeSnippet>
</div>
</div>
<div className={classes.column}>
<Heading level={4} size='2xs' spacing>
Dark Mode
</Heading>
<div className={classes.snippet}>
<CodeSnippet language='js'>{darkThemeSnippet}</CodeSnippet>
<CodeSnippet language='json'>{darkThemeSnippet}</CodeSnippet>
</div>
</div>
</div>
Expand Down
32 changes: 17 additions & 15 deletions packages/cli/bin/designsystemet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ program.name('Designsystemet').description('CLI for working with Designsystemet'

function makeTokenCommands() {
const tokenCmd = createCommand('tokens');
const DEFAULT_TOKENSDIR = './design-tokens';

tokenCmd
.command('build')
.description('Build Designsystemet tokens')
.option('-t, --tokens <string>', `Path to ${chalk.blue('design-tokens')}`, './design-tokens')
.option('-t, --tokens <string>', `Path to ${chalk.blue('design-tokens')}`, DEFAULT_TOKENSDIR)
.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 tokens = typeof opts.tokens === 'string' ? opts.tokens : DEFAULT_TOKENSDIR;
const out = typeof opts.out === 'string' ? opts.out : './dist/tokens';
const preview = opts.preview;
console.log(`Bulding tokens in ${chalk.green(tokens)}`);
Expand All @@ -32,20 +33,21 @@ function makeTokenCommands() {
tokenCmd
.command('create')
.description('Create Designsystemet tokens')
.option('-w, --write [string]', `Output directory for created ${chalk.blue('design-tokens')}`)
.option('-a, --accent <number>', `Accent hex color`)
.option('-n, --neutral <number>', `Neutral hex color`)
.option('-b1, --brand1 <number>', `Brand1 hex color`)
.option('-b2, --brand2 <number>', `Brand2 hex color`)
.option('-b3, --brand3 <number>', `Brand3 hex color`)
.option('-f, --font-family <string>', `Font family`)
.requiredOption('-a, --accent <number>', `Accent hex color`)
.requiredOption('-n, --neutral <number>', `Neutral hex color`)
.requiredOption('-b1, --brand1 <number>', `Brand1 hex color`)
.requiredOption('-b2, --brand2 <number>', `Brand2 hex color`)
.requiredOption('-b3, --brand3 <number>', `Brand3 hex color`)
.option('-w, --write [string]', `Output directory for created ${chalk.blue('design-tokens')}`, DEFAULT_TOKENSDIR)
.option('-f, --font-family <string>', `Font family`, 'Inter')
.option('--theme <string>', `Theme name`, 'theme')
.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 { theme, fontFamily } = opts;
console.log(`Creating tokens with options ${chalk.green(JSON.stringify(opts, null, 2))}`);
const write = typeof opts.write === 'boolean' ? DEFAULT_TOKENSDIR : opts.write;

const props = {
themeName: theme,
colors: {
accent: convertToHex(opts.accent),
neutral: convertToHex(opts.neutral),
Expand All @@ -54,14 +56,14 @@ function makeTokenCommands() {
brand3: convertToHex(opts.brand3),
},
typography: {
fontFamily: family,
fontFamily: fontFamily,
},
};

const tokens = createTokens(props);

if (write) {
await writeTokens(write, tokens);
await writeTokens({ writeDir: write, tokens, themeName: theme });
}

return Promise.resolve();
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"build:swc": "yarn clean && swc src bin --copy-files -d dist && yarn build:types",
"build:types": "tsc --emitDeclarationOnly --declaration",
"clean": "rimraf dist",
"clean:theme": "yarn workspace @digdir/designsystemet-theme clean"
"clean:theme": "yarn workspace @digdir/designsystemet-theme clean",
"update:template": "tsx ./src/tokens/template.ts"
},
"dependencies": {
"@adobe/leonardo-contrast-colors": "^1.0.0",
Expand Down
22 changes: 13 additions & 9 deletions packages/cli/src/tokens/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { Colors, Tokens, Tokens1ary, TokensSet, Typography } from './types.
export type CreateTokensOptions = {
colors: Colors;
typography: Typography;
themeName: string;
};

const createColorTokens = (colorArray: ColorInfo[]): Tokens1ary => {
Expand All @@ -28,9 +29,9 @@ const createColorTokens = (colorArray: ColorInfo[]): Tokens1ary => {
return obj;
};

const generateTypographyTokens = ({ fontFamily }: Typography): TokensSet => {
const generateTypographyTokens = (themeName: string, { fontFamily }: Typography): TokensSet => {
return {
theme: {
[themeName]: {
main: {
$type: 'fontFamilies',
$value: fontFamily ?? 'Inter',
Expand All @@ -51,15 +52,15 @@ const generateTypographyTokens = ({ fontFamily }: Typography): TokensSet => {
};
};

const generateThemeTokens = (theme: ColorMode, colors: Colors): TokensSet => {
const generateThemeTokens = (themeName: string, theme: ColorMode, colors: Colors): TokensSet => {
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: {
[themeName]: {
accent: createColorTokens(accentColors),
neutral: createColorTokens(neutralColors),
brand1: createColorTokens(brand1Colors),
Expand Down Expand Up @@ -90,19 +91,22 @@ const generateGlobalTokens = (theme: ColorMode) => {
};

export const createTokens = (opts: CreateTokensOptions) => {
const { colors, typography } = opts;
const { colors, typography, themeName: name } = opts;

const tokens: Tokens = {
colors: {
light: {
theme: generateThemeTokens('light', colors),
[name]: generateThemeTokens(name, 'light', colors),
global: generateGlobalTokens('light'),
},
dark: { theme: generateThemeTokens('dark', colors), global: generateGlobalTokens('dark') },
contrast: { theme: generateThemeTokens('contrast', colors), global: generateGlobalTokens('contrast') },
dark: { [name]: generateThemeTokens(name, 'dark', colors), global: generateGlobalTokens('dark') },
contrast: {
[name]: generateThemeTokens(name, 'contrast', colors),
global: generateGlobalTokens('contrast'),
},
},
typography: {
primary: generateTypographyTokens(typography),
primary: generateTypographyTokens(name, typography),
},
};

Expand Down
3 changes: 0 additions & 3 deletions packages/cli/src/tokens/create/README.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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"
}
}
}
Loading

0 comments on commit 4fb70d1

Please sign in to comment.