From cce8eccf5b6a555359d18f1a3db618ebe31d9088 Mon Sep 17 00:00:00 2001 From: Nathan Vasse Date: Tue, 26 Sep 2023 11:38:22 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(theme)=20add=20themes=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is made in order to be able to handle natively multiple themes for the future light/dark themes and also allow consumers to create their own. --- .changeset/yellow-ghosts-shake.md | 7 ++ packages/react/cunningham.ts | 6 +- .../src/components/Button/index.spec.tsx | 4 +- .../src/components/Provider/index.spec.tsx | 33 ++++++ .../react/src/components/Provider/index.tsx | 15 +++ packages/react/src/index.ts | 9 +- packages/react/src/types/index.ts | 4 + packages/tokens/jest.config.ts | 1 + .../src/bin/Generators/CssGenerator.spec.ts | 90 +++++++++++----- .../tokens/src/bin/Generators/CssGenerator.ts | 32 ++++-- .../src/bin/Generators/JsGenerator.spec.ts | 6 +- .../src/bin/Generators/SassGenerator.spec.ts | 82 ++++++++------ .../src/bin/Generators/TsGenerator.spec.ts | 6 +- packages/tokens/src/bin/Generators/index.ts | 2 +- .../tokens/src/bin/__mocks__/cunningham.ts | 72 ++++++++----- packages/tokens/src/bin/cunningham.ts | 93 ++++------------ .../tokens/src/bin/tests/Cunningham.spec.ts | 100 ++++++++++++------ .../tokens/src/bin/tests/assets/cunningham.js | 14 ++- .../tokens/src/bin/tests/assets/cunningham.ts | 14 ++- .../expected-default-cunningham-tokens.css | 13 --- .../assets/expected-js-cunningham-tokens.css | 13 --- ...with-utility-classes-cunningham-tokens.css | 28 ----- packages/tokens/src/lib/cunningham-tokens.ts | 2 +- packages/tokens/src/lib/index.ts | 5 +- 24 files changed, 370 insertions(+), 281 deletions(-) create mode 100644 .changeset/yellow-ghosts-shake.md delete mode 100644 packages/tokens/src/bin/tests/assets/expected-default-cunningham-tokens.css delete mode 100644 packages/tokens/src/bin/tests/assets/expected-js-cunningham-tokens.css delete mode 100644 packages/tokens/src/bin/tests/assets/expected-with-utility-classes-cunningham-tokens.css 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(" + + + {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: