diff --git a/.changeset/yellow-ghosts-shake.md b/.changeset/yellow-ghosts-shake.md
new file mode 100644
index 0000000..8b81c12
--- /dev/null
+++ b/.changeset/yellow-ghosts-shake.md
@@ -0,0 +1,7 @@
+---
+"@openfun/cunningham-tokens": major
+"@openfun/cunningham-react": major
+"cunningham-demo": major
+---
+
+add themes management
diff --git a/packages/react/cunningham.ts b/packages/react/cunningham.ts
index 10c3a53..ff7e494 100644
--- a/packages/react/cunningham.ts
+++ b/packages/react/cunningham.ts
@@ -32,5 +32,9 @@ files.forEach((file) => {
});
export default {
- components,
+ themes: {
+ default: {
+ components,
+ },
+ },
};
diff --git a/packages/react/src/components/Button/index.spec.tsx b/packages/react/src/components/Button/index.spec.tsx
index 42b93fb..91f4981 100644
--- a/packages/react/src/components/Button/index.spec.tsx
+++ b/packages/react/src/components/Button/index.spec.tsx
@@ -68,6 +68,8 @@ describe("", () => {
it("uses custom token", async () => {
await buildTheme();
const tokens = await loadTokens();
- expect(tokens.components.button["border-radius"]).toBeDefined();
+ expect(
+ tokens.themes.default.components.button["border-radius"],
+ ).toBeDefined();
});
});
diff --git a/packages/react/src/components/Provider/index.spec.tsx b/packages/react/src/components/Provider/index.spec.tsx
index 75fcd73..84c7360 100644
--- a/packages/react/src/components/Provider/index.spec.tsx
+++ b/packages/react/src/components/Provider/index.spec.tsx
@@ -153,4 +153,37 @@ describe("", () => {
};
render(, { wrapper: Wrapper });
});
+
+ it("should change theme by updating :root classes", async () => {
+ const Wrapper = (props: PropsWithChildren) => {
+ const [theme, setTheme] = useState("default");
+ return (
+
+
+
+
+ {props.children}
+
+ );
+ };
+ render();
+
+ expect(document.querySelector(":root")).toHaveClass(
+ "cunningham-theme--default",
+ );
+
+ const user = userEvent.setup();
+ const darkButton = screen.getByRole("button", { name: "Dark" });
+ await user.click(darkButton);
+
+ expect(document.querySelector(":root")).toHaveClass(
+ "cunningham-theme--dark",
+ );
+ const customButton = screen.getByRole("button", { name: "Custom" });
+ await user.click(customButton);
+
+ expect(Array.from(document.querySelector(":root")!.classList)).toEqual([
+ "cunningham-theme--custom",
+ ]);
+ });
});
diff --git a/packages/react/src/components/Provider/index.tsx b/packages/react/src/components/Provider/index.tsx
index 5a8d924..0fb49ff 100644
--- a/packages/react/src/components/Provider/index.tsx
+++ b/packages/react/src/components/Provider/index.tsx
@@ -2,6 +2,7 @@ import React, {
createContext,
PropsWithChildren,
useContext,
+ useEffect,
useMemo,
} from "react";
import * as enUS from ":/locales/en-US.json";
@@ -30,10 +31,13 @@ export const useCunningham = () => {
interface Props extends PropsWithChildren {
customLocales?: Record;
currentLocale?: string;
+ theme?: string;
}
export const DEFAULT_LOCALE = Locales.enUS;
+export const DEFAULT_THEME = "default";
export const SUPPORTED_LOCALES = Object.values(Locales);
+const THEME_CLASSNAME_PREFIX = "cunningham-theme--";
const findTranslation = (
key: string,
@@ -46,6 +50,7 @@ const findTranslation = (
export const CunninghamProvider = ({
currentLocale = DEFAULT_LOCALE,
customLocales,
+ theme = DEFAULT_THEME,
children,
}: Props) => {
const locales: Record = useMemo(
@@ -83,6 +88,16 @@ export const CunninghamProvider = ({
[currentLocale, locales],
);
+ useEffect(() => {
+ const root = document.querySelector(":root")!;
+ root.classList.forEach((className) => {
+ if (className.startsWith(THEME_CLASSNAME_PREFIX)) {
+ root.classList.remove(className);
+ }
+ });
+ root.classList.add(THEME_CLASSNAME_PREFIX + theme);
+ }, [theme]);
+
return (
{children}
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index ea46110..9cca523 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -1,5 +1,5 @@
import "./index.scss";
-import { PartialNested } from ":/types";
+import { PartialExtendableNested, PartialNested } from ":/types";
import { tokens } from "./cunningham-tokens";
export * from "./components/Button";
@@ -19,5 +19,8 @@ export * from "./components/Pagination";
export * from "./components/Popover";
export * from "./components/Provider";
-export type DefaultTokens = PartialNested;
-export const defaultTokens = tokens;
+export type DefaultTokens = PartialNested;
+export const defaultTokens = tokens.themes.default;
+export type Configuration = {
+ themes: Record>;
+};
diff --git a/packages/react/src/types/index.ts b/packages/react/src/types/index.ts
index d864376..0404056 100644
--- a/packages/react/src/types/index.ts
+++ b/packages/react/src/types/index.ts
@@ -1,3 +1,7 @@
export type PartialNested = {
[K in keyof T]?: T extends object ? PartialNested : T[K];
};
+
+export type PartialExtendableNested = {
+ [K in keyof T]?: T[K] extends object ? PartialExtendableNested : T[K];
+} & Record;
diff --git a/packages/tokens/jest.config.ts b/packages/tokens/jest.config.ts
index a2a7f52..9620312 100644
--- a/packages/tokens/jest.config.ts
+++ b/packages/tokens/jest.config.ts
@@ -13,6 +13,7 @@ const jestConfig: JestConfigWithTsJest = {
{ tsconfig: "./src/bin/tsconfig.json" },
],
},
+ prettierPath: null,
};
export default jestConfig;
diff --git a/packages/tokens/src/bin/Generators/CssGenerator.spec.ts b/packages/tokens/src/bin/Generators/CssGenerator.spec.ts
index b5dc3a4..543b2a8 100644
--- a/packages/tokens/src/bin/Generators/CssGenerator.spec.ts
+++ b/packages/tokens/src/bin/Generators/CssGenerator.spec.ts
@@ -9,7 +9,7 @@ jest.mock("../Paths", () => ({
}));
describe("CssGenerator", () => {
- beforeAll(() => {
+ beforeEach(() => {
jest.spyOn(console, "log").mockImplementation(() => {});
cleanup(__dirname);
});
@@ -23,25 +23,34 @@ describe("CssGenerator", () => {
expect(fs.existsSync(cssTokensFile)).toEqual(false);
await run(["", "", "-g", "css", opt, "html"]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
- expect(fs.readFileSync(cssTokensFile).toString()).toEqual(
- fs
- .readFileSync(
- path.join(
- __dirname,
- "..",
- "tests",
- "assets",
- "expected-default-" + Config.tokenFilename + ".css",
- ),
- )
- .toString()
- .replace(":root", "html"),
- );
+ expect(fs.readFileSync(cssTokensFile).toString()).toMatchInlineSnapshot(`
+"html {
+ --c--theme--colors--primary: #055FD2;
+ --c--theme--colors--secondary: #DA0000;
+ --c--theme--colors--ternary-900: #022858;
+ --c--theme--colors--ogre-odor-is-orange-indeed: #FD5240;
+ --c--theme--font--sizes--m: 1rem;
+ --c--theme--font--weights--medium: 400;
+ --c--theme--font--families--base: Roboto;
+ --c--theme--spacings--s: 1rem;
+ --c--theme--transitions--ease: linear;
+ --c--theme--input--border-color: var(--c--theme--colors--ternary-900);
+ --c--components--button--font-family: Times New Roman;
+}
+.cunningham-theme--dark{
+ --c--theme--colors--primary: black;
+}
+.cunningham-theme--custom{
+ --c--theme--colors--primary: green;
+}
+"
+`);
};
it("Runs with -s options.", async () => {
await testSelector("-s");
});
+
it("Runs with --selector options.", async () => {
await testSelector("--selector");
});
@@ -50,19 +59,42 @@ describe("CssGenerator", () => {
expect(fs.existsSync(cssTokensFile)).toEqual(false);
await run(["", "", "-g", "css", "-s", "html", "--utility-classes"]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
- expect(fs.readFileSync(cssTokensFile).toString()).toEqual(
- fs
- .readFileSync(
- path.join(
- __dirname,
- "..",
- "tests",
- "assets",
- "expected-with-utility-classes-" + Config.tokenFilename + ".css",
- ),
- )
- .toString()
- .replace(":root", "html"),
- );
+ expect(fs.readFileSync(cssTokensFile).toString()).toMatchInlineSnapshot(`
+"html {
+ --c--theme--colors--primary: #055FD2;
+ --c--theme--colors--secondary: #DA0000;
+ --c--theme--colors--ternary-900: #022858;
+ --c--theme--colors--ogre-odor-is-orange-indeed: #FD5240;
+ --c--theme--font--sizes--m: 1rem;
+ --c--theme--font--weights--medium: 400;
+ --c--theme--font--families--base: Roboto;
+ --c--theme--spacings--s: 1rem;
+ --c--theme--transitions--ease: linear;
+ --c--theme--input--border-color: var(--c--theme--colors--ternary-900);
+ --c--components--button--font-family: Times New Roman;
+}
+.cunningham-theme--dark{
+ --c--theme--colors--primary: black;
+}
+.cunningham-theme--custom{
+ --c--theme--colors--primary: green;
+} .clr-primary { color: var(--c--theme--colors--primary); }
+.clr-secondary { color: var(--c--theme--colors--secondary); }
+.clr-ternary-900 { color: var(--c--theme--colors--ternary-900); }
+.clr-ogre-odor-is-orange-indeed { color: var(--c--theme--colors--ogre-odor-is-orange-indeed); }
+.bg-primary { background-color: var(--c--theme--colors--primary); }
+.bg-secondary { background-color: var(--c--theme--colors--secondary); }
+.bg-ternary-900 { background-color: var(--c--theme--colors--ternary-900); }
+.bg-ogre-odor-is-orange-indeed { background-color: var(--c--theme--colors--ogre-odor-is-orange-indeed); }
+.fw-medium { font-weight: var(--c--theme--font--weights--medium); }
+.fs-m {
+ font-size: var(--c--theme--font--sizes--m);
+ letter-spacing: var(--c--theme--font--letterspacings--m);
+ }
+.f-base { font-family: var(--c--theme--font--families--base); }
+.m-s { margin: var(--c--theme--spacings--s); }.mb-s { margin-bottom: var(--c--theme--spacings--s); }.mt-s { margin-top: var(--c--theme--spacings--s); }.ml-s { margin-left: var(--c--theme--spacings--s); }.mr-s { margin-right: var(--c--theme--spacings--s); }
+.p-s { padding: var(--c--theme--spacings--s); }.pb-s { padding-bottom: var(--c--theme--spacings--s); }.pt-s { padding-top: var(--c--theme--spacings--s); }.pl-s { padding-left: var(--c--theme--spacings--s); }.pr-s { padding-right: var(--c--theme--spacings--s); }
+"
+`);
});
});
diff --git a/packages/tokens/src/bin/Generators/CssGenerator.ts b/packages/tokens/src/bin/Generators/CssGenerator.ts
index db20eaf..7e0aa3d 100644
--- a/packages/tokens/src/bin/Generators/CssGenerator.ts
+++ b/packages/tokens/src/bin/Generators/CssGenerator.ts
@@ -5,6 +5,8 @@ import { Generator, resolveRefs } from "Generators/index";
import { put } from "Utils/Files";
import { Tokens } from "TokensGenerator";
+export const THEME_CLASSNAME_PREFIX = "cunningham-theme--";
+
export const cssGenerator: Generator = async (tokens, opts) => {
// Replace refs by CSS variables.
tokens = resolveRefs(tokens, (ref) => {
@@ -15,7 +17,9 @@ export const cssGenerator: Generator = async (tokens, opts) => {
return `var(${cssVar})`;
});
- const flatTokens = flatify(tokens, Config.sass.varSeparator);
+ const { default: defaultTheme, ...otherThemes } = tokens.themes;
+
+ const flatTokens = flatify(defaultTheme, Config.sass.varSeparator);
const cssVars = Object.keys(flatTokens).reduce((acc, token) => {
return (
acc +
@@ -25,6 +29,18 @@ export const cssGenerator: Generator = async (tokens, opts) => {
);
}, "");
let cssContent = `${opts.selector} {\n${cssVars}}`;
+
+ Object.entries(otherThemes).forEach(([themeName, themeTokens]) => {
+ const flatTokensOther = flatify(themeTokens, Config.sass.varSeparator);
+ const themeCssVars = Object.keys(flatTokensOther).reduce((acc, token) => {
+ return (
+ acc +
+ `\t--${Config.sass.varPrefix}${token}: ${flatTokensOther[token]};\n`
+ );
+ }, "");
+ cssContent += `\n.${THEME_CLASSNAME_PREFIX}${themeName}{\n${themeCssVars}}`;
+ });
+
if (opts.utilityClasses) {
cssContent += ` ${generateClasses(tokens)}`;
}
@@ -53,7 +69,7 @@ function generateColorClasses(tokens: Tokens) {
* @param tokens
*/
function generateBgClasses(tokens: Tokens) {
- return Object.keys(tokens.theme.colors).map(
+ return Object.keys(tokens.themes.default.theme.colors).map(
(key) =>
`.bg-${key} { background-color: var(--${Config.sass.varPrefix}theme--colors--${key}); }`,
);
@@ -66,7 +82,7 @@ function generateBgClasses(tokens: Tokens) {
* @param tokens
*/
function generateClrClasses(tokens: Tokens) {
- return Object.keys(tokens.theme.colors).map(
+ return Object.keys(tokens.themes.default.theme.colors).map(
(key) =>
`.clr-${key} { color: var(--${Config.sass.varPrefix}theme--colors--${key}); }`,
);
@@ -87,7 +103,7 @@ function generateFontClasses(tokens: Tokens) {
* @param tokens
*/
function generateFwClasses(tokens: Tokens) {
- return Object.keys(tokens.theme.font.weights).map(
+ return Object.keys(tokens.themes.default.theme.font.weights).map(
(key) =>
`.fw-${key} { font-weight: var(--${Config.sass.varPrefix}theme--font--weights--${key}); }`,
);
@@ -100,7 +116,7 @@ function generateFwClasses(tokens: Tokens) {
* @param tokens
*/
function generateFsClasses(tokens: Tokens) {
- return Object.keys(tokens.theme.font.sizes).map(
+ return Object.keys(tokens.themes.default.theme.font.sizes).map(
(key) =>
`.fs-${key} {
font-size: var(--${Config.sass.varPrefix}theme--font--sizes--${key});
@@ -116,7 +132,7 @@ function generateFsClasses(tokens: Tokens) {
* @param tokens
*/
function generateFClasses(tokens: Tokens) {
- return Object.keys(tokens.theme.font.families).map(
+ return Object.keys(tokens.themes.default.theme.font.families).map(
(key) =>
`.f-${key} { font-family: var(--${Config.sass.varPrefix}theme--font--families--${key}); }`,
);
@@ -133,7 +149,7 @@ function generateSpacingClasses(tokens: Tokens) {
* @param tokens
*/
function generateMarginClasses(tokens: Tokens) {
- return Object.keys(tokens.theme.spacings).map(
+ return Object.keys(tokens.themes.default.theme.spacings).map(
(key) =>
`.m-${key} { margin: var(--${Config.sass.varPrefix}theme--spacings--${key}); }` +
`.mb-${key} { margin-bottom: var(--${Config.sass.varPrefix}theme--spacings--${key}); }` +
@@ -150,7 +166,7 @@ function generateMarginClasses(tokens: Tokens) {
* @param tokens
*/
function generatePaddingClasses(tokens: Tokens) {
- return Object.keys(tokens.theme.spacings).map(
+ return Object.keys(tokens.themes.default.theme.spacings).map(
(key) =>
`.p-${key} { padding: var(--${Config.sass.varPrefix}theme--spacings--${key}); }` +
`.pb-${key} { padding-bottom: var(--${Config.sass.varPrefix}theme--spacings--${key}); }` +
diff --git a/packages/tokens/src/bin/Generators/JsGenerator.spec.ts b/packages/tokens/src/bin/Generators/JsGenerator.spec.ts
index b8188a8..9f76876 100644
--- a/packages/tokens/src/bin/Generators/JsGenerator.spec.ts
+++ b/packages/tokens/src/bin/Generators/JsGenerator.spec.ts
@@ -24,8 +24,8 @@ describe("JsGenerator", () => {
await run(["", "", "-g", "js"]);
expect(fs.existsSync(tokensFile)).toEqual(true);
expect(fs.readFileSync(tokensFile).toString()).toMatchInlineSnapshot(`
- "export const tokens = {"theme":{"colors":{"primary":"#055FD2","secondary":"#DA0000","ternary-900":"#022858","ogre-odor-is-orange-indeed":"#FD5240"},"font":{"sizes":{"m":"1rem"},"weights":{"medium":400},"families":{"base":"Roboto"}},"spacings":{"s":"1rem"},"transitions":{"ease":"linear"},"input":{"border-color":"#022858"}},"components":{"button":{"font-family":"Times New Roman"}}};
- "
- `);
+"export const tokens = {"themes":{"default":{"theme":{"colors":{"primary":"#055FD2","secondary":"#DA0000","ternary-900":"#022858","ogre-odor-is-orange-indeed":"#FD5240"},"font":{"sizes":{"m":"1rem"},"weights":{"medium":400},"families":{"base":"Roboto"}},"spacings":{"s":"1rem"},"transitions":{"ease":"linear"},"input":{"border-color":"#022858"}},"components":{"button":{"font-family":"Times New Roman"}}},"dark":{"theme":{"colors":{"primary":"black"}}},"custom":{"theme":{"colors":{"primary":"green"}}}}};
+"
+`);
});
});
diff --git a/packages/tokens/src/bin/Generators/SassGenerator.spec.ts b/packages/tokens/src/bin/Generators/SassGenerator.spec.ts
index 6b59367..8759c7f 100644
--- a/packages/tokens/src/bin/Generators/SassGenerator.spec.ts
+++ b/packages/tokens/src/bin/Generators/SassGenerator.spec.ts
@@ -25,40 +25,58 @@ describe("SassGenerator", () => {
expect(fs.existsSync(sassFile)).toEqual(true);
expect(fs.readFileSync(sassFile).toString()).toMatchInlineSnapshot(`
- "$theme: (
- 'colors': (
- 'primary': #055FD2,
- 'secondary': #DA0000,
- 'ternary-900': #022858,
- 'ogre-odor-is-orange-indeed': #FD5240
+"$themes: (
+ 'default': (
+ 'theme': (
+ 'colors': (
+ 'primary': #055FD2,
+ 'secondary': #DA0000,
+ 'ternary-900': #022858,
+ 'ogre-odor-is-orange-indeed': #FD5240
+ ),
+ 'font': (
+ 'sizes': (
+ 'm': 1rem
),
- 'font': (
- 'sizes': (
- 'm': 1rem
- ),
- 'weights': (
- 'medium': 400
- ),
- 'families': (
- 'base': Roboto
- )
+ 'weights': (
+ 'medium': 400
),
- 'spacings': (
- 's': 1rem
- ),
- 'transitions': (
- 'ease': linear
- ),
- 'input': (
- 'border-color': #022858
+ 'families': (
+ 'base': Roboto
)
- ) !default;
- $components: (
- 'button': (
- 'font-family': Times New Roman
- )
- ) !default;
- "
- `);
+ ),
+ 'spacings': (
+ 's': 1rem
+ ),
+ 'transitions': (
+ 'ease': linear
+ ),
+ 'input': (
+ 'border-color': #022858
+ )
+ ),
+ 'components': (
+ 'button': (
+ 'font-family': Times New Roman
+ )
+ )
+ ),
+ 'dark': (
+ 'theme': (
+ 'colors': (
+ 'primary': black
+ )
+ )
+ ),
+ 'custom': (
+ 'theme': (
+ 'colors': (
+ 'primary': green
+ )
+ )
+ )
+) !default;
+"
+`);
});
});
diff --git a/packages/tokens/src/bin/Generators/TsGenerator.spec.ts b/packages/tokens/src/bin/Generators/TsGenerator.spec.ts
index c3896e0..cda1c58 100644
--- a/packages/tokens/src/bin/Generators/TsGenerator.spec.ts
+++ b/packages/tokens/src/bin/Generators/TsGenerator.spec.ts
@@ -24,8 +24,8 @@ describe("TsGenerator", () => {
await run(["", "", "-g", "ts"]);
expect(fs.existsSync(tokensFile)).toEqual(true);
expect(fs.readFileSync(tokensFile).toString()).toMatchInlineSnapshot(`
- "export const tokens = {"theme":{"colors":{"primary":"#055FD2","secondary":"#DA0000","ternary-900":"#022858","ogre-odor-is-orange-indeed":"#FD5240"},"font":{"sizes":{"m":"1rem"},"weights":{"medium":400},"families":{"base":"Roboto"}},"spacings":{"s":"1rem"},"transitions":{"ease":"linear"},"input":{"border-color":"#022858"}},"components":{"button":{"font-family":"Times New Roman"}}};
- "
- `);
+"export const tokens = {"themes":{"default":{"theme":{"colors":{"primary":"#055FD2","secondary":"#DA0000","ternary-900":"#022858","ogre-odor-is-orange-indeed":"#FD5240"},"font":{"sizes":{"m":"1rem"},"weights":{"medium":400},"families":{"base":"Roboto"}},"spacings":{"s":"1rem"},"transitions":{"ease":"linear"},"input":{"border-color":"#022858"}},"components":{"button":{"font-family":"Times New Roman"}}},"dark":{"theme":{"colors":{"primary":"black"}}},"custom":{"theme":{"colors":{"primary":"green"}}}}};
+"
+`);
});
});
diff --git a/packages/tokens/src/bin/Generators/index.ts b/packages/tokens/src/bin/Generators/index.ts
index f4b65ad..dd1eb5a 100644
--- a/packages/tokens/src/bin/Generators/index.ts
+++ b/packages/tokens/src/bin/Generators/index.ts
@@ -92,5 +92,5 @@ export const resolveRefs = (
* @param resolvingTokens
*/
export const resolveRefValue = (ref: string, resolvingTokens: Tokens) => {
- return resolve(resolvingTokens, ref);
+ return resolve(resolvingTokens.themes.default, ref);
};
diff --git a/packages/tokens/src/bin/__mocks__/cunningham.ts b/packages/tokens/src/bin/__mocks__/cunningham.ts
index cc1bd34..0f2dd8a 100644
--- a/packages/tokens/src/bin/__mocks__/cunningham.ts
+++ b/packages/tokens/src/bin/__mocks__/cunningham.ts
@@ -1,35 +1,53 @@
module.exports = {
- theme: {
- colors: {
- primary: "#055FD2",
- secondary: "#DA0000",
- "ternary-900": "#022858",
- "ogre-odor-is-orange-indeed": "#FD5240",
- },
- font: {
- sizes: {
- m: "1rem",
+ themes: {
+ default: {
+ theme: {
+ colors: {
+ primary: "#055FD2",
+ secondary: "#DA0000",
+ "ternary-900": "#022858",
+ "ogre-odor-is-orange-indeed": "#FD5240",
+ },
+ font: {
+ sizes: {
+ m: "1rem",
+ },
+ weights: {
+ medium: 400,
+ },
+ families: {
+ base: "Roboto",
+ },
+ },
+ spacings: {
+ s: "1rem",
+ },
+ transitions: {
+ ease: "linear",
+ },
+ input: {
+ "border-color": "ref(theme.colors.ternary-900)",
+ },
},
- weights: {
- medium: 400,
- },
- families: {
- base: "Roboto",
+ components: {
+ button: {
+ "font-family": "Times New Roman",
+ },
},
},
- spacings: {
- s: "1rem",
+ dark: {
+ theme: {
+ colors: {
+ primary: "black",
+ },
+ },
},
- transitions: {
- ease: "linear",
- },
- input: {
- "border-color": "ref(theme.colors.ternary-900)",
- },
- },
- components: {
- button: {
- "font-family": "Times New Roman",
+ custom: {
+ theme: {
+ colors: {
+ primary: "green",
+ },
+ },
},
},
};
diff --git a/packages/tokens/src/bin/cunningham.ts b/packages/tokens/src/bin/cunningham.ts
index 39c6b6e..5edaf91 100644
--- a/packages/tokens/src/bin/cunningham.ts
+++ b/packages/tokens/src/bin/cunningham.ts
@@ -1,31 +1,8 @@
-const colorsGreyscale = {
- "greyscale-000": "#FFFFFF",
- "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",
-};
-
-const colorsPrimary = {
- "primary-text": colorsGreyscale["greyscale-000"],
- "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",
-};
+import { colors as defaultColors } from "./ThemeColors/default";
+import { colors as darkColors } from "./ThemeColors/dark";
const colorsSecondary = {
- "secondary-text": colorsGreyscale["greyscale-700"],
+ "secondary-text": "ref(theme.colors.greyscale-700)",
"secondary-100": "#F2F7FC",
"secondary-200": "#EBF3FA",
"secondary-300": "#E2EEF8",
@@ -37,21 +14,8 @@ const colorsSecondary = {
"secondary-900": "#596067",
};
-const colorsSuccess = {
- "success-text": colorsGreyscale["greyscale-000"],
- "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",
-};
-
const colorsInfo = {
- "info-text": colorsGreyscale["greyscale-000"],
+ "info-text": "ref(theme.colors.greyscale-000)",
"info-100": "#EBF2FC",
"info-200": "#8CB5EA",
"info-300": "#5894E1",
@@ -63,32 +27,6 @@ const colorsInfo = {
"info-900": "#022858",
};
-const colorsWarning = {
- "warning-text": colorsGreyscale["greyscale-000"],
- "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",
-};
-
-const colorsDanger = {
- "danger-text": colorsGreyscale["greyscale-000"],
- "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",
-};
-
const fontSizes = {
h1: "1.75rem",
h2: "1.375rem",
@@ -144,16 +82,16 @@ const transitions = {
duration: "250ms",
};
-export default {
+const defaultTheme = {
theme: {
colors: {
- ...colorsPrimary,
...colorsSecondary,
- ...colorsGreyscale,
- ...colorsSuccess,
...colorsInfo,
- ...colorsWarning,
- ...colorsDanger,
+ ...defaultColors,
+ "primary-text": "ref(theme.colors.greyscale-000)",
+ "success-text": "ref(theme.colors.greyscale-000)",
+ "warning-text": "ref(theme.colors.greyscale-000)",
+ "danger-text": "ref(theme.colors.greyscale-000)",
},
font: {
sizes: fontSizes,
@@ -165,3 +103,14 @@ export default {
transitions,
},
};
+
+export default {
+ themes: {
+ default: defaultTheme,
+ dark: {
+ theme: {
+ colors: darkColors,
+ },
+ },
+ },
+};
diff --git a/packages/tokens/src/bin/tests/Cunningham.spec.ts b/packages/tokens/src/bin/tests/Cunningham.spec.ts
index 47fc7da..35231c2 100644
--- a/packages/tokens/src/bin/tests/Cunningham.spec.ts
+++ b/packages/tokens/src/bin/tests/Cunningham.spec.ts
@@ -55,17 +55,28 @@ describe("Cunningham Bin", () => {
expect(fs.existsSync(cssTokensFile)).toEqual(false);
await run(["", "", "-g", "css"]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
- expect(fs.readFileSync(cssTokensFile).toString()).toEqual(
- fs
- .readFileSync(
- path.join(
- __dirname,
- "assets",
- "expected-default-" + Config.tokenFilename + ".css",
- ),
- )
- .toString(),
- );
+ expect(fs.readFileSync(cssTokensFile).toString()).toMatchInlineSnapshot(`
+":root {
+ --c--theme--colors--primary: #055FD2;
+ --c--theme--colors--secondary: #DA0000;
+ --c--theme--colors--ternary-900: #022858;
+ --c--theme--colors--ogre-odor-is-orange-indeed: #FD5240;
+ --c--theme--font--sizes--m: 1rem;
+ --c--theme--font--weights--medium: 400;
+ --c--theme--font--families--base: Roboto;
+ --c--theme--spacings--s: 1rem;
+ --c--theme--transitions--ease: linear;
+ --c--theme--input--border-color: var(--c--theme--colors--ternary-900);
+ --c--components--button--font-family: Times New Roman;
+}
+.cunningham-theme--dark{
+ --c--theme--colors--primary: black;
+}
+.cunningham-theme--custom{
+ --c--theme--colors--primary: green;
+}
+"
+`);
});
it("Runs with existing JS config file using local values.", async () => {
@@ -83,17 +94,28 @@ describe("Cunningham Bin", () => {
await run(["", "", "-g", "css"]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
- expect(fs.readFileSync(cssTokensFile).toString()).toEqual(
- fs
- .readFileSync(
- path.join(
- __dirname,
- "assets",
- "expected-js-" + Config.tokenFilename + ".css",
- ),
- )
- .toString(),
- );
+ expect(fs.readFileSync(cssTokensFile).toString()).toMatchInlineSnapshot(`
+":root {
+ --c--theme--colors--primary: AntiqueWhite;
+ --c--theme--colors--secondary: #DA0000;
+ --c--theme--colors--ternary-900: #022858;
+ --c--theme--colors--ogre-odor-is-orange-indeed: #FD5240;
+ --c--theme--font--sizes--m: 1rem;
+ --c--theme--font--weights--medium: 400;
+ --c--theme--font--families--base: Roboto;
+ --c--theme--spacings--s: 1rem;
+ --c--theme--transitions--ease: linear;
+ --c--theme--input--border-color: var(--c--theme--colors--ternary-900);
+ --c--components--button--font-family: Times New Roman;
+}
+.cunningham-theme--dark{
+ --c--theme--colors--primary: black;
+}
+.cunningham-theme--custom{
+ --c--theme--colors--primary: green;
+}
+"
+`);
});
it("Runs with existing TS config file using local values.", async () => {
@@ -113,7 +135,6 @@ describe("Cunningham Bin", () => {
// TS will always work )
await runBin(`-g css -cwd ${__dirname}`);
- // await run(["", "", "-g", "css"]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
expect(fs.readFileSync(cssTokensFile).toString()).toContain(`
\t--c--theme--colors--primary: typescript;`);
@@ -125,17 +146,28 @@ describe("Cunningham Bin", () => {
expect(fs.existsSync(cssTokensFile)).toEqual(false);
await run(["", "", "-g", "css", opt, outputDir]);
expect(fs.existsSync(cssTokensFile)).toEqual(true);
- expect(fs.readFileSync(cssTokensFile).toString()).toEqual(
- fs
- .readFileSync(
- path.join(
- __dirname,
- "assets",
- "expected-default-" + Config.tokenFilename + ".css",
- ),
- )
- .toString(),
- );
+ expect(fs.readFileSync(cssTokensFile).toString()).toMatchInlineSnapshot(`
+":root {
+ --c--theme--colors--primary: #055FD2;
+ --c--theme--colors--secondary: #DA0000;
+ --c--theme--colors--ternary-900: #022858;
+ --c--theme--colors--ogre-odor-is-orange-indeed: #FD5240;
+ --c--theme--font--sizes--m: 1rem;
+ --c--theme--font--weights--medium: 400;
+ --c--theme--font--families--base: Roboto;
+ --c--theme--spacings--s: 1rem;
+ --c--theme--transitions--ease: linear;
+ --c--theme--input--border-color: var(--c--theme--colors--ternary-900);
+ --c--components--button--font-family: Times New Roman;
+}
+.cunningham-theme--dark{
+ --c--theme--colors--primary: black;
+}
+.cunningham-theme--custom{
+ --c--theme--colors--primary: green;
+}
+"
+`);
};
it("Runs with -o options.", async () => {
diff --git a/packages/tokens/src/bin/tests/assets/cunningham.js b/packages/tokens/src/bin/tests/assets/cunningham.js
index eb83877..9373a3d 100644
--- a/packages/tokens/src/bin/tests/assets/cunningham.js
+++ b/packages/tokens/src/bin/tests/assets/cunningham.js
@@ -1,7 +1,11 @@
module.exports = {
- theme: {
- colors: {
- primary: "AntiqueWhite",
- },
- },
+ themes: {
+ default: {
+ theme: {
+ colors: {
+ primary: "AntiqueWhite",
+ },
+ },
+ }
+ }
};
diff --git a/packages/tokens/src/bin/tests/assets/cunningham.ts b/packages/tokens/src/bin/tests/assets/cunningham.ts
index 484e361..e6b7a21 100644
--- a/packages/tokens/src/bin/tests/assets/cunningham.ts
+++ b/packages/tokens/src/bin/tests/assets/cunningham.ts
@@ -1,7 +1,11 @@
export default {
- theme: {
- colors: {
- primary: "typescript",
- },
- },
+ themes: {
+ default: {
+ theme: {
+ colors: {
+ primary: "typescript",
+ },
+ },
+ }
+ }
};
diff --git a/packages/tokens/src/bin/tests/assets/expected-default-cunningham-tokens.css b/packages/tokens/src/bin/tests/assets/expected-default-cunningham-tokens.css
deleted file mode 100644
index 00ad945..0000000
--- a/packages/tokens/src/bin/tests/assets/expected-default-cunningham-tokens.css
+++ /dev/null
@@ -1,13 +0,0 @@
-:root {
- --c--theme--colors--primary: #055FD2;
- --c--theme--colors--secondary: #DA0000;
- --c--theme--colors--ternary-900: #022858;
- --c--theme--colors--ogre-odor-is-orange-indeed: #FD5240;
- --c--theme--font--sizes--m: 1rem;
- --c--theme--font--weights--medium: 400;
- --c--theme--font--families--base: Roboto;
- --c--theme--spacings--s: 1rem;
- --c--theme--transitions--ease: linear;
- --c--theme--input--border-color: var(--c--theme--colors--ternary-900);
- --c--components--button--font-family: Times New Roman;
-}
diff --git a/packages/tokens/src/bin/tests/assets/expected-js-cunningham-tokens.css b/packages/tokens/src/bin/tests/assets/expected-js-cunningham-tokens.css
deleted file mode 100644
index 22d299b..0000000
--- a/packages/tokens/src/bin/tests/assets/expected-js-cunningham-tokens.css
+++ /dev/null
@@ -1,13 +0,0 @@
-:root {
- --c--theme--colors--primary: AntiqueWhite;
- --c--theme--colors--secondary: #DA0000;
- --c--theme--colors--ternary-900: #022858;
- --c--theme--colors--ogre-odor-is-orange-indeed: #FD5240;
- --c--theme--font--sizes--m: 1rem;
- --c--theme--font--weights--medium: 400;
- --c--theme--font--families--base: Roboto;
- --c--theme--spacings--s: 1rem;
- --c--theme--transitions--ease: linear;
- --c--theme--input--border-color: var(--c--theme--colors--ternary-900);
- --c--components--button--font-family: Times New Roman;
-}
diff --git a/packages/tokens/src/bin/tests/assets/expected-with-utility-classes-cunningham-tokens.css b/packages/tokens/src/bin/tests/assets/expected-with-utility-classes-cunningham-tokens.css
deleted file mode 100644
index 0c0aee6..0000000
--- a/packages/tokens/src/bin/tests/assets/expected-with-utility-classes-cunningham-tokens.css
+++ /dev/null
@@ -1,28 +0,0 @@
-html {
- --c--theme--colors--primary: #055FD2;
- --c--theme--colors--secondary: #DA0000;
- --c--theme--colors--ternary-900: #022858;
- --c--theme--colors--ogre-odor-is-orange-indeed: #FD5240;
- --c--theme--font--sizes--m: 1rem;
- --c--theme--font--weights--medium: 400;
- --c--theme--font--families--base: Roboto;
- --c--theme--spacings--s: 1rem;
- --c--theme--transitions--ease: linear;
- --c--theme--input--border-color: var(--c--theme--colors--ternary-900);
- --c--components--button--font-family: Times New Roman;
-} .clr-primary { color: var(--c--theme--colors--primary); }
-.clr-secondary { color: var(--c--theme--colors--secondary); }
-.clr-ternary-900 { color: var(--c--theme--colors--ternary-900); }
-.clr-ogre-odor-is-orange-indeed { color: var(--c--theme--colors--ogre-odor-is-orange-indeed); }
-.bg-primary { background-color: var(--c--theme--colors--primary); }
-.bg-secondary { background-color: var(--c--theme--colors--secondary); }
-.bg-ternary-900 { background-color: var(--c--theme--colors--ternary-900); }
-.bg-ogre-odor-is-orange-indeed { background-color: var(--c--theme--colors--ogre-odor-is-orange-indeed); }
-.fw-medium { font-weight: var(--c--theme--font--weights--medium); }
-.fs-m {
- font-size: var(--c--theme--font--sizes--m);
- letter-spacing: var(--c--theme--font--letterspacings--m);
- }
-.f-base { font-family: var(--c--theme--font--families--base); }
-.m-s { margin: var(--c--theme--spacings--s); }.mb-s { margin-bottom: var(--c--theme--spacings--s); }.mt-s { margin-top: var(--c--theme--spacings--s); }.ml-s { margin-left: var(--c--theme--spacings--s); }.mr-s { margin-right: var(--c--theme--spacings--s); }
-.p-s { padding: var(--c--theme--spacings--s); }.pb-s { padding-bottom: var(--c--theme--spacings--s); }.pt-s { padding-top: var(--c--theme--spacings--s); }.pl-s { padding-left: var(--c--theme--spacings--s); }.pr-s { padding-right: var(--c--theme--spacings--s); }
diff --git a/packages/tokens/src/lib/cunningham-tokens.ts b/packages/tokens/src/lib/cunningham-tokens.ts
index aded102..269ffbe 100644
--- a/packages/tokens/src/lib/cunningham-tokens.ts
+++ b/packages/tokens/src/lib/cunningham-tokens.ts
@@ -1 +1 @@
-export const tokens = {"theme":{"colors":{"primary-text":"#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","secondary-text":"#555F6B","secondary-100":"#F2F7FC","secondary-200":"#EBF3FA","secondary-300":"#E2EEF8","secondary-400":"#DDEAF7","secondary-500":"#D4E5F5","secondary-600":"#C1D0DF","secondary-700":"#97A3AE","secondary-800":"#757E87","secondary-900":"#596067","greyscale-000":"#FFFFFF","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","success-text":"#FFFFFF","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","info-text":"#FFFFFF","info-100":"#EBF2FC","info-200":"#8CB5EA","info-300":"#5894E1","info-400":"#377FDB","info-500":"#055FD2","info-600":"#0556BF","info-700":"#044395","info-800":"#033474","info-900":"#022858","warning-text":"#FFFFFF","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-text":"#FFFFFF","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"},"font":{"sizes":{"h1":"1.75rem","h2":"1.375rem","h3":"1.125rem","h4":"0.8125rem","h5":"0.625rem","h6":"0.5rem","l":"1rem","m":"0.8125rem","s":"0.6875rem"},"weights":{"thin":200,"light":300,"regular":400,"medium":500,"bold":600,"extrabold":700,"black":800},"families":{"base":"\"Roboto Flex Variable\", sans-serif","accent":"\"Roboto Flex Variable\", sans-serif"},"letterSpacings":{"h1":"normal","h2":"normal","h3":"normal","h4":"normal","h5":"1px","h6":"normal","l":"normal","m":"normal","s":"normal"}},"spacings":{"xl":"4rem","l":"3rem","b":"1.625rem","s":"1rem","t":"0.5rem","st":"0.25rem"},"transitions":{"ease-in":"cubic-bezier(0.32, 0, 0.67, 0)","ease-out":"cubic-bezier(0.33, 1, 0.68, 1)","ease-in-out":"cubic-bezier(0.65, 0, 0.35, 1)","duration":"250ms"}}};
+export const tokens = {"themes":{"default":{"theme":{"colors":{"secondary-text":"#555F6B","secondary-100":"#F2F7FC","secondary-200":"#EBF3FA","secondary-300":"#E2EEF8","secondary-400":"#DDEAF7","secondary-500":"#D4E5F5","secondary-600":"#C1D0DF","secondary-700":"#97A3AE","secondary-800":"#757E87","secondary-900":"#596067","info-text":"#FFFFFF","info-100":"#EBF2FC","info-200":"#8CB5EA","info-300":"#5894E1","info-400":"#377FDB","info-500":"#055FD2","info-600":"#0556BF","info-700":"#044395","info-800":"#033474","info-900":"#022858","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","primary-text":"#FFFFFF","success-text":"#FFFFFF","warning-text":"#FFFFFF","danger-text":"#FFFFFF"},"font":{"sizes":{"h1":"1.75rem","h2":"1.375rem","h3":"1.125rem","h4":"0.8125rem","h5":"0.625rem","h6":"0.5rem","l":"1rem","m":"0.8125rem","s":"0.6875rem"},"weights":{"thin":200,"light":300,"regular":400,"medium":500,"bold":600,"extrabold":700,"black":800},"families":{"base":"\"Roboto Flex Variable\", sans-serif","accent":"\"Roboto Flex Variable\", sans-serif"},"letterSpacings":{"h1":"normal","h2":"normal","h3":"normal","h4":"normal","h5":"1px","h6":"normal","l":"normal","m":"normal","s":"normal"}},"spacings":{"xl":"4rem","l":"3rem","b":"1.625rem","s":"1rem","t":"0.5rem","st":"0.25rem"},"transitions":{"ease-in":"cubic-bezier(0.32, 0, 0.67, 0)","ease-out":"cubic-bezier(0.33, 1, 0.68, 1)","ease-in-out":"cubic-bezier(0.65, 0, 0.35, 1)","duration":"250ms"}}},"dark":{"theme":{"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"}}}}};
diff --git a/packages/tokens/src/lib/index.ts b/packages/tokens/src/lib/index.ts
index 00de3f2..f15ad60 100644
--- a/packages/tokens/src/lib/index.ts
+++ b/packages/tokens/src/lib/index.ts
@@ -1,7 +1,8 @@
import { tokens } from "./cunningham-tokens";
-export type DefaultTokens = typeof tokens;
-export const defaultTokens = tokens;
+export type Configuration = typeof tokens;
+export type DefaultTokens = (typeof tokens)["themes"]["default"];
+export const defaultTokens = tokens.themes.default;
/**
* Transform such object: