Skip to content

Commit

Permalink
refactor: single CSS file for theme (#2204)
Browse files Browse the repository at this point in the history
fixes #2202 
Converted the CSS file for a single to include all css generated instead
of just imports for the individual files.
  • Loading branch information
mimarz authored Aug 7, 2024
1 parent a1deb7b commit a9f0528
Show file tree
Hide file tree
Showing 19 changed files with 7,683 additions and 53 deletions.
6 changes: 6 additions & 0 deletions .changeset/five-apricots-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@digdir/designsystemet-theme': patch
'@digdir/designsystemet': patch
---

refactor: single CSS file for theme
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@
"html-css-class-completion.includeGlobPattern": "packages/css/**/*.{css,html}",
"[github-actions-workflow]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
2 changes: 1 addition & 1 deletion packages/cli/src/tokens/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const makeEntryFile: Action = {

const generateImportUrls = R.pipe(
sortLightmodeFirst,
R.map((file): string => `@import url('./${theme}/${file.toString()}');`),
R.map((file): string => `@import url('${theme}/${file.toString()}');`),
R.join('\n'),
);

Expand Down
45 changes: 29 additions & 16 deletions packages/cli/src/tokens/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as R from 'ramda';
import StyleDictionary from 'style-dictionary';

import * as configs from './configs.js';
import { makeEntryFile } from './utils/entryfile.js';

const { permutateThemes, getConfigs } = configs;

Expand All @@ -26,7 +27,7 @@ const sd = new StyleDictionary();
export async function run(options: Options): Promise<void> {
const tokensDir = options.tokens;
const storefrontOutDir = path.resolve('../../apps/storefront/tokens');
const tokensOutDir = path.resolve(options.out);
const outPath = path.resolve(options.out);

const $themes = JSON.parse(fs.readFileSync(path.resolve(`${tokensDir}/$themes.json`), 'utf-8')) as ThemeObject[];

Expand All @@ -43,9 +44,9 @@ export async function run(options: Options): Promise<void> {
const colormodeThemes = R.filter((val) => val.typography === 'primary', themes);
const semanticThemes = R.filter((val) => val.mode === 'light' && val.typography === 'primary', themes);

const colorModeConfigs = getConfigs(configs.colorModeVariables, tokensOutDir, tokensDir, colormodeThemes);
const semanticConfigs = getConfigs(configs.semanticVariables, tokensOutDir, tokensDir, semanticThemes);
const typographyConfigs = getConfigs(configs.typographyCSS, tokensOutDir, tokensDir, typographyThemes);
const colorModeConfigs = getConfigs(configs.colorModeVariables, outPath, tokensDir, colormodeThemes);
const semanticConfigs = getConfigs(configs.semanticVariables, outPath, tokensDir, semanticThemes);
const typographyConfigs = getConfigs(configs.typographyCSS, outPath, tokensDir, typographyThemes);
const storefrontConfigs = getConfigs(configs.typescriptTokens, storefrontOutDir, tokensDir, colormodeThemes);

if (typographyConfigs.length > 0) {
Expand All @@ -62,30 +63,30 @@ export async function run(options: Options): Promise<void> {
);
}

if (semanticConfigs.length > 0) {
console.log(`\n🍱 Building ${chalk.green('semantic')}`);
if (colorModeConfigs.length > 0) {
console.log(`\n🍱 Building ${chalk.green('color-mode')}`);

await Promise.all(
semanticConfigs.map(async ({ theme, config, semantic }) => {
console.log(`👷 ${theme} - ${semantic}`);
colorModeConfigs.map(async ({ theme, mode, config }) => {
console.log(`👷 ${theme} - ${mode}`);

const typographyClasses = await sd.extend(config);
const themeVariablesSD = await sd.extend(config);

return typographyClasses.buildAllPlatforms();
return themeVariablesSD.buildAllPlatforms();
}),
);
}

if (colorModeConfigs.length > 0) {
console.log(`\n🍱 Building ${chalk.green('color-mode')}`);
if (semanticConfigs.length > 0) {
console.log(`\n🍱 Building ${chalk.green('semantic')}`);

await Promise.all(
colorModeConfigs.map(async ({ theme, mode, config }) => {
console.log(`👷 ${theme} - ${mode}`);
semanticConfigs.map(async ({ theme, config, semantic }) => {
console.log(`👷 ${theme} - ${semantic}`);

const themeVariablesSD = await sd.extend(config);
const typographyClasses = await sd.extend(config);

return themeVariablesSD.buildAllPlatforms();
return typographyClasses.buildAllPlatforms();
}),
);
}
Expand All @@ -103,4 +104,16 @@ export async function run(options: Options): Promise<void> {
}),
);
}

if (semanticConfigs.length > 0) {
console.log(`\n🍱 Building ${chalk.green('CSS file')}`);

await Promise.all(
semanticConfigs.map(async ({ theme }) => {
console.log(`👷 ${theme}.css`);

return makeEntryFile({ theme, outPath, buildPath: path.resolve(`${outPath}/${theme}`) });
}),
);
}
}
5 changes: 0 additions & 5 deletions packages/cli/src/tokens/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import StyleDictionary from 'style-dictionary';
import type { Config, TransformedToken } from 'style-dictionary/types';
import { outputReferencesFilter } from 'style-dictionary/utils';

import { makeEntryFile } from './actions.js';
import * as formats from './formats/css.js';
import { jsTokens } from './formats/js-tokens.js';
import { nameKebab, sizeRem, typographyShorthand } from './transformers.js';
Expand All @@ -30,8 +29,6 @@ StyleDictionary.registerFormat(formats.colormode);
StyleDictionary.registerFormat(formats.semantic);
StyleDictionary.registerFormat(formats.typography);

StyleDictionary.registerAction(makeEntryFile);

const dsTransformers = [
nameKebab.name,
`ts/resolveMath`,
Expand Down Expand Up @@ -95,7 +92,6 @@ export const colorModeVariables: GetConfig = ({ mode = 'light', outPath, theme }
prefix,
buildPath: `${outPath}/${theme}/`,
transforms: dsTransformers,
actions: [makeEntryFile.name],
files: [
{
destination: `color-mode/${mode}.css`,
Expand Down Expand Up @@ -142,7 +138,6 @@ export const semanticVariables: GetConfig = ({ outPath, theme }) => {
prefix,
buildPath: `${outPath}/${theme}/`,
transforms: dsTransformers,
actions: [makeEntryFile.name],
files: [
{
destination: `semantic.css`,
Expand Down
11 changes: 8 additions & 3 deletions packages/cli/src/tokens/formats/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ export const colormode: Format = {

const content = `{\n${allTokens.map(format).join('\n')}\n${colorSchemeProperty}}\n`;
const autoSelectorContent = ['light', 'dark'].includes(mode_) ? prefersColorScheme(mode_, content) : '';
const body = R.isNotNil(layer)
? `@layer ${layer} {\n${selector} ${content} ${autoSelectorContent}\n}\n`
: `${selector} ${content} ${autoSelectorContent}\n`;

return header + `@layer ${layer} {\n${selector} ${content} ${autoSelectorContent}\n}\n`;
return header + body;
},
};

Expand Down Expand Up @@ -74,8 +77,9 @@ export const semantic: Format = {

const formattedVariables = formatTokens(allTokens);
const content = `{\n${formattedVariables.join('\n')}\n}\n`;
const body = R.isNotNil(layer) ? `@layer ${layer} {\n${selector} ${content}\n}\n` : `${selector} ${content}\n`;

return header + `@layer ${layer} {\n${selector} ${content}\n}\n`;
return header + body;
},
};

Expand Down Expand Up @@ -193,7 +197,8 @@ export const typography: Format = {
const classes = formattedTokens.classes.join('\n');
const variables = formattedTokens.variables.join('\n');
const content = selector ? `${selector} {\n${variables}\n${classes}\n}` : classes;
const body = R.isNotNil(layer) ? `@layer ${layer} {\n${content}\n}` : content;

return header + `@layer ${layer} {\n${content}\n}\n`;
return header + body;
},
};
44 changes: 44 additions & 0 deletions packages/cli/src/tokens/utils/entryfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import path from 'node:path';
import chalk from 'chalk';
import glob from 'fast-glob';
import fs from 'fs-extra';
import * as R from 'ramda';

const sortLightmodeFirst = R.sortWith<string>([R.descend(R.includes('light')), R.descend(R.includes('secondary'))]);

const header = `@charset "UTF-8";
@layer ds.reset, ds.theme, ds.base, ds.utilities, ds.components;
\n`;

const sortAndConcat = R.pipe(
sortLightmodeFirst,
R.map((file): string => {
try {
const content = fs.readFileSync(file, 'utf-8').toString();
return content;
} catch (e) {
console.error(`Error reading file: ${file}`);
return '';
}
}),
R.join('\n'),
);

type EntryFile = (options: {
outPath: string;
buildPath: string;
theme: string;
}) => Promise<undefined>;

/**
* Creates a CSS entry file that imports base CSS files for a theme
*/
export const makeEntryFile: EntryFile = async ({ outPath, buildPath, theme }) => {
const writePath = `${outPath}/${theme}.css`;

const files = await glob(`**/*`, { cwd: buildPath });
const content = header + sortAndConcat(files.map((file) => `${buildPath}/${file}`));

await fs.writeFile(writePath, content);
};
Loading

0 comments on commit a9f0528

Please sign in to comment.