Skip to content

Commit 0a23ecf

Browse files
Replicate original PR
Co-Authored-By: Edward Gargan <[email protected]>
1 parent 621b835 commit 0a23ecf

File tree

21 files changed

+2584
-168
lines changed

21 files changed

+2584
-168
lines changed

.changeset/thin-buttons-appear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@guardian/source': minor
3+
---
4+
5+
generate CSS/SCSS files for typography, palette, breakpoints

libs/@guardian/source/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
"import": "./dist/foundations.js",
1111
"require": "./dist/foundations.cjs"
1212
},
13+
"./foundations/breakpoints.scss": "./dist/foundations/__generated__/breakpoints.scss",
14+
"./foundations/palette.css": "./dist/foundations/__generated__/palette.css",
15+
"./foundations/typography.css": "./dist/foundations/__generated__/typography.css",
1316
"./react-components": {
1417
"types": "./dist/react-components.d.ts",
1518
"import": "./dist/react-components.js",
@@ -67,10 +70,13 @@
6770
"jest": "30.0.5",
6871
"lightningcss": "1.30.0",
6972
"mkdirp": "3.0.1",
73+
"postcss": "8.5.6",
74+
"postcss-scss": "4.0.9",
7075
"prettier": "3.3.3",
7176
"react": "18.2.0",
7277
"react-dom": "18.2.0",
7378
"rollup": "4.50.1",
79+
"rollup-plugin-copy": "3.5.0",
7480
"storybook": "8.6.4",
7581
"ts-jest": "29.4.0",
7682
"tslib": "2.6.2",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1+
import copy from 'rollup-plugin-copy';
12
import config from '../../../configs/rollup/rollup.config.js';
23

34
export default config({
45
input: {
56
foundations: 'src/foundations/index.ts',
67
'react-components': 'src/react-components/index.ts',
78
},
9+
plugins: [
10+
copy({
11+
targets: [
12+
{
13+
src: 'src/foundations/__generated__/*.{scss,css}',
14+
dest: 'dist/foundations/__generated__',
15+
},
16+
],
17+
}),
18+
],
819
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* eslint-disable import/no-default-export -- cobalt plugins do this */
2+
3+
// @ts-check
4+
5+
import { defaultTransformer } from '@cobalt-ui/plugin-js';
6+
import { set } from '@cobalt-ui/utils';
7+
import { template } from '../../lib/template.js';
8+
9+
/**
10+
* @param {{ filename: string; }} options
11+
* @returns {import('@cobalt-ui/core').Plugin}
12+
*/
13+
export default function pluginBreakpointsScss(options) {
14+
return {
15+
name: 'plugin-breakpoints-scss',
16+
17+
config(/* config */) {},
18+
async build({ tokens /*, rawSchema, metadata */ }) {
19+
const TOKEN_GROUP = 'breakpoint';
20+
21+
const breakpointTokens = tokens.filter((token) =>
22+
token.id.startsWith(TOKEN_GROUP),
23+
);
24+
25+
/**
26+
* @type {Object.<string, string>}
27+
*/
28+
const transformedTokens = {};
29+
30+
// we can re-use the default transformer from `@cobalt-ui/plugin-js`
31+
for (const token of breakpointTokens) {
32+
set(transformedTokens, token.id, defaultTransformer(token));
33+
}
34+
35+
const breakpointPairs = [];
36+
37+
for (const breakpoints of Object.values(transformedTokens)) {
38+
for (const [name, value] of Object.entries(breakpoints)) {
39+
breakpointPairs.push({ name, value });
40+
}
41+
}
42+
43+
breakpointPairs.sort((a, b) => parseFloat(a.value) - parseFloat(b.value));
44+
45+
const breakpointEntries = breakpointPairs.map(
46+
({ name, value }) => `\t${name}: ${value},`,
47+
);
48+
49+
const scssSource = `$breakpoints: (\n${breakpointEntries.join('\n')}\n);`;
50+
51+
return [
52+
{
53+
filename: options.filename,
54+
contents: template(import.meta.filename, scssSource),
55+
},
56+
];
57+
},
58+
};
59+
}

libs/@guardian/source/src/design-tokens/cobalt-plugins/breakpoints.js renamed to libs/@guardian/source/src/design-tokens/cobalt-plugins/breakpoints/typescript.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44

55
import { defaultTransformer, serializeJS } from '@cobalt-ui/plugin-js';
66
import { set } from '@cobalt-ui/utils';
7-
import { pxStringToNumber } from '../lib/convert-value.js';
8-
import { getCommentId } from '../lib/get-comment-id.js';
9-
import { template } from '../lib/template.js';
7+
import { pxStringToNumber } from '../../lib/convert-value.js';
8+
import { getCommentId } from '../../lib/get-comment-id.js';
9+
import { template } from '../../lib/template.js';
1010

1111
/**
1212
* @param {{ filename: string; }} options
1313
* @returns {import('@cobalt-ui/core').Plugin}
1414
*/
1515
export default function pluginBreakpoints(options) {
1616
return {
17-
name: 'plugin-breakpoints',
17+
name: 'plugin-breakpoints-typescript',
1818

1919
config(/* config */) {},
2020
async build({ tokens, rawSchema /*, metadata */ }) {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* eslint-disable import/no-default-export -- cobalt plugins do this */
2+
3+
// @ts-check
4+
5+
import { defaultTransformer } from '@cobalt-ui/plugin-js';
6+
import { set } from '@cobalt-ui/utils';
7+
import { camelToKebab } from '../../lib/case.js';
8+
import { template } from '../../lib/template.js';
9+
10+
/**
11+
* @param {string[]} variableDecls eg. ["--src-brand-100: #001536;", "--src-brand-300: #041F4A;"]
12+
*
13+
* NOTE: we don't bother with proper indentation here, prettier will sort that out at build
14+
*/
15+
const cssTemplate = (variableDecls) => `:root { ${variableDecls.join('\n')} }
16+
`;
17+
18+
/**
19+
* @param {{ filename: string; }} options
20+
* @returns {import('@cobalt-ui/core').Plugin}
21+
*/
22+
export default function pluginPaletteCss(options) {
23+
return {
24+
name: 'plugin-palette-css',
25+
26+
config(/* config */) {},
27+
async build({ tokens /*, rawSchema, metadata */ }) {
28+
const TOKEN_GROUP = 'palette';
29+
30+
/** @type {Object.<string, string>} */
31+
const transformedTokens = {};
32+
33+
const paletteTokens = tokens.filter((token) =>
34+
token.id.startsWith(TOKEN_GROUP),
35+
);
36+
37+
// we can re-use the default transformer from `@cobalt-ui/plugin-js`
38+
for (const token of paletteTokens) {
39+
set(transformedTokens, token.id, defaultTransformer(token));
40+
}
41+
42+
const cssVariablesDecls = [];
43+
44+
for (const tokens of Object.values(transformedTokens)) {
45+
// eg. [ "brand", { "100": "#001536", "300": "#041F4A", ... } ]
46+
for (const [category, shades] of Object.entries(tokens)) {
47+
// eg. [ "100", "#001536" ]
48+
for (const [shade, color] of Object.entries(shades)) {
49+
const varName = `--src-${camelToKebab(category)}-${shade}`;
50+
cssVariablesDecls.push(`\t${varName}: ${color};`);
51+
}
52+
}
53+
}
54+
55+
return [
56+
{
57+
filename: options.filename,
58+
contents: template(
59+
import.meta.filename,
60+
cssTemplate(cssVariablesDecls),
61+
),
62+
},
63+
];
64+
},
65+
};
66+
}

libs/@guardian/source/src/design-tokens/cobalt-plugins/palette.js renamed to libs/@guardian/source/src/design-tokens/cobalt-plugins/palette/typescript.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44

55
import { defaultTransformer, serializeJS } from '@cobalt-ui/plugin-js';
66
import { set } from '@cobalt-ui/utils';
7-
import { getCommentId } from '../lib/get-comment-id.js';
8-
import { template } from '../lib/template.js';
7+
import { getCommentId } from '../../lib/get-comment-id.js';
8+
import { template } from '../../lib/template.js';
99

1010
/**
1111
* @param {{ filename: string; }} options
1212
* @returns {import('@cobalt-ui/core').Plugin}
1313
*/
1414
export default function pluginPalette(options) {
1515
return {
16-
name: 'plugin-palette',
16+
name: 'plugin-palette-typescript',
1717

1818
config(/* config */) {},
1919
async build({ tokens, rawSchema /*, metadata */ }) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/** @param {string} fontSize */
2+
export const textDecorationThickness = (fontSize) => {
3+
switch (fontSize) {
4+
case '20px':
5+
case '24px':
6+
case '28px':
7+
return '3px';
8+
case '34px':
9+
return '4px';
10+
case '42px':
11+
return '5px';
12+
case '50px':
13+
case '64px':
14+
case '70px':
15+
return '6px';
16+
default:
17+
return '2px';
18+
}
19+
};
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* eslint-disable import/no-default-export -- cobalt plugins do this */
2+
3+
// @ts-check
4+
5+
import { defaultTransformer } from '@cobalt-ui/plugin-js';
6+
import { set } from '@cobalt-ui/utils';
7+
import { camelToKebab } from '../../lib/case.js';
8+
import { fontArrayToString, pxStringToRem } from '../../lib/convert-value.js';
9+
import { template } from '../../lib/template.js';
10+
import { textDecorationThickness } from './common.js';
11+
12+
/**
13+
* @typedef {Object} TypographyPreset
14+
* @property {string[]} fontFamily
15+
* @property {string} fontSize
16+
* @property {number} lineHeight
17+
* @property {number} fontWeight
18+
* @property {string} fontStyle
19+
*/
20+
21+
const GROUP_PREFIX = 'typographyPresets.';
22+
23+
/**
24+
* Converts eg. "headlineBold24" to "src-headline-bold-24"
25+
*
26+
* @param {string} presetName
27+
*/
28+
const classNameFromPreset = (presetName) => {
29+
return `.src-${camelToKebab(presetName).replace(/(\d+)$/, '-$1')}`;
30+
};
31+
32+
/**
33+
*
34+
* @param {string} preset
35+
* @param {TypographyPreset} properties
36+
*/
37+
const presetClass = (preset, properties) => `${classNameFromPreset(preset)} {
38+
font-family: ${fontArrayToString(properties.fontFamily)};
39+
font-size: ${pxStringToRem(properties.fontSize)};
40+
line-height: ${properties.lineHeight};
41+
font-weight: ${properties.fontWeight};
42+
font-style: ${properties.fontStyle};
43+
--source-text-decoration-thickness: ${textDecorationThickness(properties.fontSize)};
44+
}`;
45+
46+
/**
47+
* @param {{ filename: string; }} options
48+
* @returns {import('@cobalt-ui/core').Plugin}
49+
*/
50+
export default function pluginTypographyCss(options) {
51+
return {
52+
name: 'plugin-typography-css',
53+
54+
config(/* config */) {},
55+
async build({ tokens /* metadata, rawSchema */ }) {
56+
/** @type {Object.<string, string>} */
57+
const transformedTokens = {};
58+
59+
const typographyTokens = tokens.filter((token) =>
60+
token.id.startsWith(GROUP_PREFIX),
61+
);
62+
63+
// we can re-use the default transformer from `@cobalt-ui/plugin-js`
64+
for (const token of typographyTokens) {
65+
set(transformedTokens, token.id, defaultTransformer(token));
66+
}
67+
68+
/** @type {Object.<!string, TypographyPreset>} */
69+
const typographyPresets = transformedTokens.typographyPresets;
70+
71+
const cssClasses = Object.entries(typographyPresets)
72+
.map(([preset, properties]) => presetClass(preset, properties))
73+
.join('\n\n');
74+
75+
const cssSource = cssClasses;
76+
77+
return [
78+
{
79+
filename: options.filename,
80+
contents: template(import.meta.filename, cssSource),
81+
},
82+
];
83+
},
84+
};
85+
}

libs/@guardian/source/src/design-tokens/cobalt-plugins/typography.js renamed to libs/@guardian/source/src/design-tokens/cobalt-plugins/typography/typescript.js

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,10 @@
44

55
import { defaultTransformer } from '@cobalt-ui/plugin-js';
66
import { set } from '@cobalt-ui/utils';
7-
import { fontArrayToString, pxStringToRem } from '../lib/convert-value.js';
8-
import { getCommentId } from '../lib/get-comment-id.js';
9-
import { template } from '../lib/template.js';
10-
11-
const GROUP_PREFIX = 'typographyPresets.';
12-
13-
/** @param {string} fontSize */
14-
const textDecorationThickness = (fontSize) => {
15-
switch (fontSize) {
16-
case '20px':
17-
case '24px':
18-
case '28px':
19-
return '3px';
20-
case '34px':
21-
return '4px';
22-
case '42px':
23-
return '5px';
24-
case '50px':
25-
case '64px':
26-
case '70px':
27-
return '6px';
28-
default:
29-
return '2px';
30-
}
31-
};
7+
import { fontArrayToString, pxStringToRem } from '../../lib/convert-value.js';
8+
import { getCommentId } from '../../lib/get-comment-id.js';
9+
import { template } from '../../lib/template.js';
10+
import { textDecorationThickness } from './common.js';
3211

3312
/**
3413
* @typedef {Object} TypographyPreset
@@ -39,6 +18,8 @@ const textDecorationThickness = (fontSize) => {
3918
* @property {string} fontStyle
4019
*/
4120

21+
const GROUP_PREFIX = 'typographyPresets.';
22+
4223
/**
4324
*
4425
* @param {string} preset
@@ -69,7 +50,7 @@ export const ${preset}Object = {
6950
*/
7051
export default function pluginBreakpoints(options) {
7152
return {
72-
name: 'plugin-typography',
53+
name: 'plugin-typography-typescript',
7354

7455
config(/* config */) {},
7556
async build({ tokens /* metadata, rawSchema */ }) {

0 commit comments

Comments
 (0)