🧑‍💻(tokens) add getThemesFromGlobals utils

Export a new util method `getThemesFromGlobals` to easily generate theme with
its variant only by providing a partial globals object. By default it returns
both available theme variants (light & dark). Through options you can prefix
variant property keys, only generate theme with a subset of variant and also
overrides/extend theme.
This commit is contained in:
jbpenrath
2025-12-08 20:35:45 +01:00
committed by Jean-Baptiste PENRATH
parent fe8eb4b802
commit db26e21b88
19 changed files with 1766 additions and 2348 deletions

View File

@@ -1,33 +0,0 @@
/**
* This util script is used to convert Figma's Token Studio JSON color config to TS file.
*
* CLI usage example:
* $ yarn figma-to-cunningham dark.json
*/
import fs from "fs";
import path from "path";
const filePath = process.argv[2];
const data = fs.readFileSync(filePath);
const json = JSON.parse(data.toString());
const output: any = {};
Object.keys(json).forEach((color) => {
console.log("Parsing color:", color);
Object.keys(json[color]).forEach((colorVariation) => {
const colorName = color === "Succes" ? "success" : color.toLowerCase();
output[colorName + "-" + colorVariation] =
json[color][colorVariation].value;
});
});
console.log("Output:", output);
const content = `/**
* /!\\ Please do not edit this file directly. Instead use the FigmaToCunningham.ts converter that generated this file.
*/
export const colors = ${JSON.stringify(output)}
`;
const fileName = path.parse(filePath).name;
const outputPath = `./src/bin/ThemeColors/${fileName}.ts`;
fs.writeFileSync(outputPath, content);
console.log("Successfuly written to", outputPath);

View File

@@ -26,12 +26,11 @@
"lint": "eslint . 'src/**/*.{ts,tsx}'",
"dev": "nodemon --watch 'src/bin' --ext '*' --exec 'yarn build'",
"build-bin": "cd src/bin && rm -rf ../../dist/bin && tsc -p tsconfig.build.json && tsc-alias && chmod +x ../../dist/bin/Main.js",
"build-lib": "cp dist/cunningham-tokens.ts src/lib && cd src/lib && tsc -p tsconfig.json",
"build-lib": "cp src/bin/cunningham.ts src/lib && cp dist/cunningham-tokens.ts src/lib && cd src/lib && tsc -p tsconfig.json",
"build": "yarn build-bin && yarn build-default-theme && yarn build-lib && cd ../.. && ln -sf ../../packages/tokens/dist/bin/Main.js node_modules/.bin/cunningham",
"build-default-theme": "node ./dist/bin/Main.js -o dist -s html -g scss,css,js,ts --utility-classes",
"test": "cross-env FORCE_COLOR=1 jest --runInBand --verbose src",
"test-ci": "cross-env FORCE_COLOR=1 jest --runInBand src",
"figma-to-cunningham": "ts-node FigmaToCunningham.ts"
"test-ci": "cross-env FORCE_COLOR=1 jest --runInBand src"
},
"dependencies": {
"chalk": "4.1.2",

View File

@@ -1,51 +0,0 @@
/**
* /!\ Please do not edit this file directly. Instead use the FigmaToCunningham.ts converter that generated this file.
*/
export const colors = {
'greyscale-100': '#182536',
'greyscale-200': '#303C4B',
'greyscale-300': '#555F6B',
'greyscale-400': '#79818A',
'greyscale-500': '#9EA3AA',
'greyscale-600': '#C2C6CA',
'greyscale-700': '#E7E8EA',
'greyscale-800': '#F3F4F4',
'greyscale-900': '#FAFAFB',
'greyscale-000': '#0C1A2B',
'primary-100': '#3B4C62',
'primary-200': '#4D6481',
'primary-300': '#6381A6',
'primary-400': '#7FA5D5',
'primary-500': '#8CB5EA',
'primary-600': '#A3C4EE',
'primary-700': '#C3D8F4',
'primary-800': '#DDE9F8',
'primary-900': '#F4F8FD',
'success-100': '#EEF8D7',
'success-200': '#D9F1B2',
'success-300': '#BDE985',
'success-400': '#A0E25D',
'success-500': '#76D628',
'success-600': '#5BB520',
'success-700': '#43941A',
'success-800': '#307414',
'success-900': '#225D10',
'warning-100': '#F7F3D5',
'warning-200': '#F0E5AA',
'warning-300': '#E8D680',
'warning-400': '#E3C95F',
'warning-500': '#D9B32B',
'warning-600': '#BD9721',
'warning-700': '#9D7B1C',
'warning-800': '#7E6016',
'warning-900': '#684D12',
'danger-100': '#F8D0D0',
'danger-200': '#F09898',
'danger-300': '#F09898',
'danger-400': '#ED8585',
'danger-500': '#E96666',
'danger-600': '#DD6666',
'danger-700': '#C36666',
'danger-800': '#AE6666',
'danger-900': '#9D6666',
};

View File

@@ -1,51 +0,0 @@
/**
* /!\ Please do not edit this file directly. Instead use the FigmaToCunningham.ts converter that generated this file.
*/
export const colors = {
'greyscale-100': '#FAFAFB',
'greyscale-200': '#F3F4F4',
'greyscale-300': '#E7E8EA',
'greyscale-400': '#C2C6CA',
'greyscale-500': '#9EA3AA',
'greyscale-600': '#79818A',
'greyscale-700': '#555F6B',
'greyscale-800': '#303C4B',
'greyscale-900': '#0C1A2B',
'greyscale-000': '#FFFFFF',
'primary-100': '#EBF2FC',
'primary-200': '#8CB5EA',
'primary-300': '#5894E1',
'primary-400': '#377FDB',
'primary-500': '#055FD2',
'primary-600': '#0556BF',
'primary-700': '#044395',
'primary-800': '#033474',
'primary-900': '#022858',
'success-100': '#EFFCD3',
'success-200': '#DBFAA9',
'success-300': '#BEF27C',
'success-400': '#A0E659',
'success-500': '#76D628',
'success-600': '#5AB81D',
'success-700': '#419A14',
'success-800': '#2C7C0C',
'success-900': '#1D6607',
'warning-100': '#FFF8CD',
'warning-200': '#FFEF9B',
'warning-300': '#FFE469',
'warning-400': '#FFDA43',
'warning-500': '#FFC805',
'warning-600': '#DBA603',
'warning-700': '#B78702',
'warning-800': '#936901',
'warning-900': '#7A5400',
'danger-100': '#F4B0B0',
'danger-200': '#EE8A8A',
'danger-300': '#E65454',
'danger-400': '#E13333',
'danger-500': '#DA0000',
'danger-600': '#C60000',
'danger-700': '#9B0000',
'danger-800': '#780000',
'danger-900': '#5C0000',
};

View File

@@ -1,8 +1,22 @@
/*
This file is hardlinked from `src/shared/cunningham.ts`
to `src/bin/cunningham.ts` and `src/lib/cunningham.ts`
```
cd packages/tokens
ln src/shared/cunningham.ts src/lib/cunningham.ts
ln src/shared/cunningham.ts src/bin/cunningham.ts
```
It is a convenient way to share the same tokens between the bin and the lib
and do not put extra logic to customize build outputs.
*/
export const colors = {
"logo-1": "#377FDB",
"logo-2": "#377FDB",
"logo-1-dark": "#95ABFF",
"logo-2-dark": "#95ABFF",
"logo-1-light": "#377FDB",
"logo-2-light": "#377FDB",
"logo-1-dark": "#C1D6F2",
"logo-2-dark": "#C1D6F2",
"brand-050": "#EAF1FB",
"brand-100": "#D5E4F7",
"brand-150": "#C0D6F4",
@@ -291,46 +305,47 @@ export const colors = {
"pink-850": "#332028",
"pink-900": "#24181D",
"pink-950": "#160F12",
"black-000": "#00000000",
"black-050": "#0000000D",
"black-100": "#0000001A",
"black-150": "#00000026",
"black-200": "#00000033",
"black-250": "#00000040",
"black-300": "#0000004D",
"black-350": "#00000059",
"black-400": "#00000066",
"black-450": "#00000073",
"black-500": "#00000080",
"black-550": "#0000008C",
"black-600": "#00000099",
"black-650": "#000000A6",
"black-700": "#000000B3",
"black-750": "#000000BF",
"black-800": "#000000CC",
"black-850": "#000000D9",
"black-900": "#000000E6",
"black-950": "#000000F2",
"white-000": "#FFFFFF",
"white-050": "#FFFFFF0D",
"white-100": "#FFFFFF1A",
"white-150": "#FFFFFF26",
"white-200": "#FFFFFF33",
"white-250": "#FFFFFF40",
"white-300": "#FFFFFF4D",
"white-350": "#FFFFFF59",
"white-400": "#FFFFFF66",
"white-450": "#FFFFFF73",
"white-500": "#FFFFFF80",
"white-550": "#FFFFFF8C",
"white-600": "#FFFFFF99",
"white-650": "#FFFFFFA6",
"white-700": "#FFFFFFB3",
"white-750": "#FFFFFFBF",
"white-800": "#FFFFFFCC",
"white-850": "#FFFFFFD9",
"white-900": "#FFFFFFE6",
"white-950": "#FFFFFFF2",
"black-000": "#1B1C1D00",
"black-050": "#1B1C1D0D",
"black-100": "#1B1C1D1A",
"black-150": "#1B1C1D26",
"black-200": "#1B1C1D33",
"black-250": "#1B1C1D40",
"black-300": "#1B1C1D4D",
"black-350": "#1B1C1D59",
"black-400": "#1B1C1D66",
"black-450": "#1B1C1D73",
"black-500": "#1B1C1D80",
"black-550": "#1B1C1D8C",
"black-600": "#1B1C1D99",
"black-650": "#1B1C1DA6",
"black-700": "#1B1C1DB2",
"black-750": "#1B1C1DBF",
"black-800": "#1B1C1DCC",
"black-850": "#1B1C1DD9",
"black-900": "#1B1C1DE5",
"black-950": "#101112F2",
"white-000": "#F7F8F800",
"white-050": "#F7F8F80D",
"white-100": "#F7F8F81A",
"white-150": "#F7F8F826",
"white-200": "#F7F8F833",
"white-250": "#F7F8F840",
"white-300": "#F7F8F84D",
"white-350": "#F7F8F859",
"white-400": "#F7F8F866",
"white-450": "#F7F8F873",
"white-500": "#F7F8F880",
"white-550": "#F7F8F88C",
"white-600": "#F7F8F899",
"white-650": "#F7F8F8A6",
"white-700": "#F7F8F8B2",
"white-750": "#F7F8F8BF",
"white-800": "#F7F8F8CC",
"white-850": "#F7F8F8D9",
"white-900": "#F7F8F8E5",
"white-950": "#F7F8F8F2",
"white-975": "#F7F8F8F9",
};
const fontFamilies = {
@@ -374,7 +389,7 @@ const transitions = {
};
const breakpoints = {
xs: 0,
xs: "0px",
sm: "576px",
md: "768px",
lg: "992px",
@@ -425,7 +440,7 @@ export const globals = {
breakpoints,
};
export const contextuaDefault = {
export const contextualDefaultTokens = {
background: {
surface: {
primary: "ref(globals.colors.gray-000)",
@@ -551,13 +566,10 @@ export const contextuaDefault = {
tertiary: "ref(globals.colors.gray-150)",
},
},
text: {
primary: "ref(globals.colors.black-050)",
},
},
content: {
logo1: "ref(globals.colors.logo-1)",
logo2: "ref(globals.colors.logo-2)",
logo1: "ref(globals.colors.logo-1-light)",
logo2: "ref(globals.colors.logo-2-light)",
semantic: {
contextual: {
primary: "ref(globals.colors.white-950)",
@@ -689,11 +701,6 @@ export const contextuaDefault = {
},
},
};
const defaultTheme = {
globals,
contextuals: contextuaDefault,
components: {},
};
export const contextualDarkTokens = {
background: {
@@ -959,7 +966,11 @@ export const contextualDarkTokens = {
export default {
themes: {
default: defaultTheme,
default: {
globals,
contextuals: contextualDefaultTokens,
components: {},
},
dark: {
globals,
contextuals: contextualDarkTokens,

View File

@@ -164,10 +164,10 @@ describe("Cunningham Bin", () => {
expect(fs.existsSync(cssTokensFile)).toEqual(true);
expect(fs.readFileSync(cssTokensFile).toString()).toMatchInlineSnapshot(`
":root {
--c--globals--colors--logo-1: #377FDB;
--c--globals--colors--logo-2: #377FDB;
--c--globals--colors--logo-1-dark: #95ABFF;
--c--globals--colors--logo-2-dark: #95ABFF;
--c--globals--colors--logo-1-light: #377FDB;
--c--globals--colors--logo-2-light: #377FDB;
--c--globals--colors--logo-1-dark: #C1D6F2;
--c--globals--colors--logo-2-dark: #C1D6F2;
--c--globals--colors--brand-050: #EAF1FB;
--c--globals--colors--brand-100: #D5E4F7;
--c--globals--colors--brand-150: #C0D6F4;
@@ -456,46 +456,47 @@ describe("Cunningham Bin", () => {
--c--globals--colors--pink-850: #332028;
--c--globals--colors--pink-900: #24181D;
--c--globals--colors--pink-950: #160F12;
--c--globals--colors--black-000: #00000000;
--c--globals--colors--black-050: #0000000D;
--c--globals--colors--black-100: #0000001A;
--c--globals--colors--black-150: #00000026;
--c--globals--colors--black-200: #00000033;
--c--globals--colors--black-250: #00000040;
--c--globals--colors--black-300: #0000004D;
--c--globals--colors--black-350: #00000059;
--c--globals--colors--black-400: #00000066;
--c--globals--colors--black-450: #00000073;
--c--globals--colors--black-500: #00000080;
--c--globals--colors--black-550: #0000008C;
--c--globals--colors--black-600: #00000099;
--c--globals--colors--black-650: #000000A6;
--c--globals--colors--black-700: #000000B3;
--c--globals--colors--black-750: #000000BF;
--c--globals--colors--black-800: #000000CC;
--c--globals--colors--black-850: #000000D9;
--c--globals--colors--black-900: #000000E6;
--c--globals--colors--black-950: #000000F2;
--c--globals--colors--white-000: #FFFFFF;
--c--globals--colors--white-050: #FFFFFF0D;
--c--globals--colors--white-100: #FFFFFF1A;
--c--globals--colors--white-150: #FFFFFF26;
--c--globals--colors--white-200: #FFFFFF33;
--c--globals--colors--white-250: #FFFFFF40;
--c--globals--colors--white-300: #FFFFFF4D;
--c--globals--colors--white-350: #FFFFFF59;
--c--globals--colors--white-400: #FFFFFF66;
--c--globals--colors--white-450: #FFFFFF73;
--c--globals--colors--white-500: #FFFFFF80;
--c--globals--colors--white-550: #FFFFFF8C;
--c--globals--colors--white-600: #FFFFFF99;
--c--globals--colors--white-650: #FFFFFFA6;
--c--globals--colors--white-700: #FFFFFFB3;
--c--globals--colors--white-750: #FFFFFFBF;
--c--globals--colors--white-800: #FFFFFFCC;
--c--globals--colors--white-850: #FFFFFFD9;
--c--globals--colors--white-900: #FFFFFFE6;
--c--globals--colors--white-950: #FFFFFFF2;
--c--globals--colors--black-000: #1B1C1D00;
--c--globals--colors--black-050: #1B1C1D0D;
--c--globals--colors--black-100: #1B1C1D1A;
--c--globals--colors--black-150: #1B1C1D26;
--c--globals--colors--black-200: #1B1C1D33;
--c--globals--colors--black-250: #1B1C1D40;
--c--globals--colors--black-300: #1B1C1D4D;
--c--globals--colors--black-350: #1B1C1D59;
--c--globals--colors--black-400: #1B1C1D66;
--c--globals--colors--black-450: #1B1C1D73;
--c--globals--colors--black-500: #1B1C1D80;
--c--globals--colors--black-550: #1B1C1D8C;
--c--globals--colors--black-600: #1B1C1D99;
--c--globals--colors--black-650: #1B1C1DA6;
--c--globals--colors--black-700: #1B1C1DB2;
--c--globals--colors--black-750: #1B1C1DBF;
--c--globals--colors--black-800: #1B1C1DCC;
--c--globals--colors--black-850: #1B1C1DD9;
--c--globals--colors--black-900: #1B1C1DE5;
--c--globals--colors--black-950: #101112F2;
--c--globals--colors--white-000: #F7F8F800;
--c--globals--colors--white-050: #F7F8F80D;
--c--globals--colors--white-100: #F7F8F81A;
--c--globals--colors--white-150: #F7F8F826;
--c--globals--colors--white-200: #F7F8F833;
--c--globals--colors--white-250: #F7F8F840;
--c--globals--colors--white-300: #F7F8F84D;
--c--globals--colors--white-350: #F7F8F859;
--c--globals--colors--white-400: #F7F8F866;
--c--globals--colors--white-450: #F7F8F873;
--c--globals--colors--white-500: #F7F8F880;
--c--globals--colors--white-550: #F7F8F88C;
--c--globals--colors--white-600: #F7F8F899;
--c--globals--colors--white-650: #F7F8F8A6;
--c--globals--colors--white-700: #F7F8F8B2;
--c--globals--colors--white-750: #F7F8F8BF;
--c--globals--colors--white-800: #F7F8F8CC;
--c--globals--colors--white-850: #F7F8F8D9;
--c--globals--colors--white-900: #F7F8F8E5;
--c--globals--colors--white-950: #F7F8F8F2;
--c--globals--colors--white-975: #F7F8F8F9;
--c--globals--transitions--ease-in: cubic-bezier(0.32, 0, 0.67, 0);
--c--globals--transitions--ease-out: cubic-bezier(0.33, 1, 0.68, 1);
--c--globals--transitions--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
@@ -553,7 +554,7 @@ describe("Cunningham Bin", () => {
--c--globals--spacings--5xl: 4.5rem;
--c--globals--spacings--6xl: 6rem;
--c--globals--spacings--7xl: 7.5rem;
--c--globals--breakpoints--xs: 0;
--c--globals--breakpoints--xs: 0px;
--c--globals--breakpoints--sm: 576px;
--c--globals--breakpoints--md: 768px;
--c--globals--breakpoints--lg: 992px;
@@ -637,9 +638,8 @@ describe("Cunningham Bin", () => {
--c--contextuals--background--palette--gray--primary: var(--c--globals--colors--gray-500);
--c--contextuals--background--palette--gray--secondary: var(--c--globals--colors--gray-350);
--c--contextuals--background--palette--gray--tertiary: var(--c--globals--colors--gray-150);
--c--contextuals--background--text--primary: var(--c--globals--colors--black-050);
--c--contextuals--content--logo1: var(--c--globals--colors--logo-1);
--c--contextuals--content--logo2: var(--c--globals--colors--logo-2);
--c--contextuals--content--logo1: var(--c--globals--colors--logo-1-light);
--c--contextuals--content--logo2: var(--c--globals--colors--logo-2-light);
--c--contextuals--content--semantic--contextual--primary: var(--c--globals--colors--white-950);
--c--contextuals--content--semantic--overlay--primary: var(--c--globals--colors--white-950);
--c--contextuals--content--semantic--brand--primary: var(--c--globals--colors--brand-700);
@@ -704,10 +704,10 @@ describe("Cunningham Bin", () => {
--c--theme--colors--primary: typescript;
}
.cunningham-theme--dark{
--c--globals--colors--logo-1: #377FDB;
--c--globals--colors--logo-2: #377FDB;
--c--globals--colors--logo-1-dark: #95ABFF;
--c--globals--colors--logo-2-dark: #95ABFF;
--c--globals--colors--logo-1-light: #377FDB;
--c--globals--colors--logo-2-light: #377FDB;
--c--globals--colors--logo-1-dark: #C1D6F2;
--c--globals--colors--logo-2-dark: #C1D6F2;
--c--globals--colors--brand-050: #EAF1FB;
--c--globals--colors--brand-100: #D5E4F7;
--c--globals--colors--brand-150: #C0D6F4;
@@ -996,46 +996,47 @@ describe("Cunningham Bin", () => {
--c--globals--colors--pink-850: #332028;
--c--globals--colors--pink-900: #24181D;
--c--globals--colors--pink-950: #160F12;
--c--globals--colors--black-000: #00000000;
--c--globals--colors--black-050: #0000000D;
--c--globals--colors--black-100: #0000001A;
--c--globals--colors--black-150: #00000026;
--c--globals--colors--black-200: #00000033;
--c--globals--colors--black-250: #00000040;
--c--globals--colors--black-300: #0000004D;
--c--globals--colors--black-350: #00000059;
--c--globals--colors--black-400: #00000066;
--c--globals--colors--black-450: #00000073;
--c--globals--colors--black-500: #00000080;
--c--globals--colors--black-550: #0000008C;
--c--globals--colors--black-600: #00000099;
--c--globals--colors--black-650: #000000A6;
--c--globals--colors--black-700: #000000B3;
--c--globals--colors--black-750: #000000BF;
--c--globals--colors--black-800: #000000CC;
--c--globals--colors--black-850: #000000D9;
--c--globals--colors--black-900: #000000E6;
--c--globals--colors--black-950: #000000F2;
--c--globals--colors--white-000: #FFFFFF;
--c--globals--colors--white-050: #FFFFFF0D;
--c--globals--colors--white-100: #FFFFFF1A;
--c--globals--colors--white-150: #FFFFFF26;
--c--globals--colors--white-200: #FFFFFF33;
--c--globals--colors--white-250: #FFFFFF40;
--c--globals--colors--white-300: #FFFFFF4D;
--c--globals--colors--white-350: #FFFFFF59;
--c--globals--colors--white-400: #FFFFFF66;
--c--globals--colors--white-450: #FFFFFF73;
--c--globals--colors--white-500: #FFFFFF80;
--c--globals--colors--white-550: #FFFFFF8C;
--c--globals--colors--white-600: #FFFFFF99;
--c--globals--colors--white-650: #FFFFFFA6;
--c--globals--colors--white-700: #FFFFFFB3;
--c--globals--colors--white-750: #FFFFFFBF;
--c--globals--colors--white-800: #FFFFFFCC;
--c--globals--colors--white-850: #FFFFFFD9;
--c--globals--colors--white-900: #FFFFFFE6;
--c--globals--colors--white-950: #FFFFFFF2;
--c--globals--colors--black-000: #1B1C1D00;
--c--globals--colors--black-050: #1B1C1D0D;
--c--globals--colors--black-100: #1B1C1D1A;
--c--globals--colors--black-150: #1B1C1D26;
--c--globals--colors--black-200: #1B1C1D33;
--c--globals--colors--black-250: #1B1C1D40;
--c--globals--colors--black-300: #1B1C1D4D;
--c--globals--colors--black-350: #1B1C1D59;
--c--globals--colors--black-400: #1B1C1D66;
--c--globals--colors--black-450: #1B1C1D73;
--c--globals--colors--black-500: #1B1C1D80;
--c--globals--colors--black-550: #1B1C1D8C;
--c--globals--colors--black-600: #1B1C1D99;
--c--globals--colors--black-650: #1B1C1DA6;
--c--globals--colors--black-700: #1B1C1DB2;
--c--globals--colors--black-750: #1B1C1DBF;
--c--globals--colors--black-800: #1B1C1DCC;
--c--globals--colors--black-850: #1B1C1DD9;
--c--globals--colors--black-900: #1B1C1DE5;
--c--globals--colors--black-950: #101112F2;
--c--globals--colors--white-000: #F7F8F800;
--c--globals--colors--white-050: #F7F8F80D;
--c--globals--colors--white-100: #F7F8F81A;
--c--globals--colors--white-150: #F7F8F826;
--c--globals--colors--white-200: #F7F8F833;
--c--globals--colors--white-250: #F7F8F840;
--c--globals--colors--white-300: #F7F8F84D;
--c--globals--colors--white-350: #F7F8F859;
--c--globals--colors--white-400: #F7F8F866;
--c--globals--colors--white-450: #F7F8F873;
--c--globals--colors--white-500: #F7F8F880;
--c--globals--colors--white-550: #F7F8F88C;
--c--globals--colors--white-600: #F7F8F899;
--c--globals--colors--white-650: #F7F8F8A6;
--c--globals--colors--white-700: #F7F8F8B2;
--c--globals--colors--white-750: #F7F8F8BF;
--c--globals--colors--white-800: #F7F8F8CC;
--c--globals--colors--white-850: #F7F8F8D9;
--c--globals--colors--white-900: #F7F8F8E5;
--c--globals--colors--white-950: #F7F8F8F2;
--c--globals--colors--white-975: #F7F8F8F9;
--c--globals--transitions--ease-in: cubic-bezier(0.32, 0, 0.67, 0);
--c--globals--transitions--ease-out: cubic-bezier(0.33, 1, 0.68, 1);
--c--globals--transitions--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
@@ -1093,7 +1094,7 @@ describe("Cunningham Bin", () => {
--c--globals--spacings--5xl: 4.5rem;
--c--globals--spacings--6xl: 6rem;
--c--globals--spacings--7xl: 7.5rem;
--c--globals--breakpoints--xs: 0;
--c--globals--breakpoints--xs: 0px;
--c--globals--breakpoints--sm: 576px;
--c--globals--breakpoints--md: 768px;
--c--globals--breakpoints--lg: 992px;

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
import { buildRefs } from "./index";
import { buildRefs, getThemesFromGlobals } from "./index";
describe("buildRefs", () => {
it("should replace raw values by ref keys", () => {
@@ -19,3 +19,49 @@ describe("buildRefs", () => {
});
});
});
describe("getThemesFromGlobals", () => {
it("should return a theme from a set of globals", () => {
const themes = getThemesFromGlobals({
colors: { "brand-500": "blue" },
font: { families: { base: "Comic Sans MS" } },
});
expect(Object.keys(themes)).toEqual(["light", "dark"]);
expect(themes.light.globals.colors["brand-500"]).toEqual("blue");
expect(themes.dark.globals.colors["brand-500"]).toEqual("blue");
expect(themes.light.globals.font.families.base).toEqual("Comic Sans MS");
expect(themes.dark.globals.font.families.base).toEqual("Comic Sans MS");
expect(themes.light.contextuals).toBeDefined();
expect(themes.dark.contextuals).toBeDefined();
});
it("should allow to prefix the theme names", () => {
const themes = getThemesFromGlobals({}, { prefix: "custom" });
expect(Object.keys(themes)).toEqual(["custom-light", "custom-dark"]);
});
it("should allow to get theme for a subset of variants", () => {
const themes = getThemesFromGlobals({}, { variants: ["light"] });
expect(Object.keys(themes)).toEqual(["light"]);
});
it("should allow to override/extend themes", () => {
const themes = getThemesFromGlobals(
{},
{
overrides: {
components: {
button: {
"font-family": "Papyrus",
},
},
},
},
);
expect(themes.light.components.button["font-family"]).toEqual("Papyrus");
});
});

View File

@@ -1,4 +1,6 @@
import { tokens } from "./cunningham-tokens";
import { buildRefs } from "./utils/buildRefs";
import { getThemesFromGlobals } from "./utils/getThemesFromGlobals";
export type Configuration = typeof tokens;
export type DefaultTokens = (typeof tokens)["themes"]["default"];
@@ -6,38 +8,7 @@ export type DefaultTokens = (typeof tokens)["themes"]["default"];
export const defaultTokens = tokens.themes.default;
export const defaultThemes = tokens.themes;
/**
* Transform such object:
* {
* theme: {
* colors: {
* "primary-500": "blue"
* }
* }
* }
*
* to:
* {
* theme: {
* colors: {
* "primary-500": "ref(theme.colors.primary-500)"
* }
* }
* }
* @param tokens_
*/
export const buildRefs = <T extends Object>(tokens_: T): T => {
const buildRefsAux = (upperKey: string, subTokens: any) => {
if (typeof subTokens === "object") {
const obj: any = {};
Object.entries(subTokens).forEach(([key, value]) => {
obj[key] = buildRefsAux((upperKey ? upperKey + "." : "") + key, value);
});
return obj;
}
return "ref(" + upperKey + ")";
};
return buildRefsAux("", tokens_);
};
export const defaultTokenRefs = buildRefs(defaultTokens);
export { buildRefs };
export { getThemesFromGlobals };

View File

@@ -0,0 +1,33 @@
/**
* Transform such object:
* {
* theme: {
* colors: {
* "primary-500": "blue"
* }
* }
* }
*
* to:
* {
* theme: {
* colors: {
* "primary-500": "ref(theme.colors.primary-500)"
* }
* }
* }
* @param tokens_
*/
export const buildRefs = <T extends Object>(tokens_: T): T => {
const buildRefsAux = (upperKey: string, subTokens: any) => {
if (typeof subTokens === "object") {
const obj: any = {};
Object.entries(subTokens).forEach(([key, value]) => {
obj[key] = buildRefsAux((upperKey ? upperKey + "." : "") + key, value);
});
return obj;
}
return "ref(" + upperKey + ")";
};
return buildRefsAux("", tokens_);
};

View File

@@ -0,0 +1,61 @@
// deepmerge is not available as a module, so we need to import it as a commonjs module...
import deepmerge = require("deepmerge");
import {
contextualDefaultTokens,
contextualDarkTokens,
globals as defaultGlobals,
} from "../cunningham";
type GlobalTokens = typeof defaultGlobals;
type PartialExtendableNested<T> = {
[K in keyof T]?: T[K] extends object ? PartialExtendableNested<T[K]> : T[K];
} & Record<PropertyKey, any>;
const THEME_VARIANTS = ["light", "dark"] as const;
type ThemeVariant = (typeof THEME_VARIANTS)[number];
const CONTEXTUAL_TOKENS_MAP = {
light: contextualDefaultTokens,
dark: contextualDarkTokens,
};
interface Options {
prefix?: string;
variants?: readonly ThemeVariant[];
overrides?: Record<string, unknown>;
}
/**
* Generates theme objects from global tokens and optional overrides.
*
* @param globals - A partial global tokens object.
* @param options - Additional options for generating themes.
* @param options.prefix - Optional prefix for the theme keys.
* @param options.variants - Theme variants to generate (e.g., ['light', 'dark']).
* @param options.overrides - Optional overrides/extensions to apply to the generated themes.
* @returns An object mapping each theme variant (with optional prefix) to its corresponding tokens.
*/
export const getThemesFromGlobals = (
globals: PartialExtendableNested<GlobalTokens> = {},
options: Options = {},
) => {
const variants = options.variants || THEME_VARIANTS;
return variants.reduce(
(themes, variant) => {
const variantKey = options.prefix
? `${options.prefix}-${variant}`
: variant;
themes[variantKey] = deepmerge(
{
globals,
contextuals: CONTEXTUAL_TOKENS_MAP[variant],
},
options.overrides || {},
);
return themes;
},
{} as Record<string, any>,
);
};