✨(tokens) add token references
Previously we were not fully using CSS variables as values used in CSS were hard-coded one. It wasn't keeping the variable references. This was causing issues when customizing the theme, because editing colors was not enough, it was needed to customize also the tokens using these variables. Now by introducing ref() we can delegate how to deal with these directly to the generators themselves.
This commit is contained in:
5
.changeset/chatty-garlics-exercise.md
Normal file
5
.changeset/chatty-garlics-exercise.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@openfun/cunningham-tokens": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add token references
|
||||||
@@ -3,7 +3,7 @@ import type { JestConfigWithTsJest } from "ts-jest";
|
|||||||
const jestConfig: JestConfigWithTsJest = {
|
const jestConfig: JestConfigWithTsJest = {
|
||||||
preset: "ts-jest",
|
preset: "ts-jest",
|
||||||
testEnvironment: "node",
|
testEnvironment: "node",
|
||||||
moduleDirectories: ["node_modules", "src/bin"],
|
moduleDirectories: ["node_modules", "src/bin", "src/lib"],
|
||||||
setupFiles: ["<rootDir>/src/bin/tests/Setup.ts"],
|
setupFiles: ["<rootDir>/src/bin/tests/Setup.ts"],
|
||||||
transform: {
|
transform: {
|
||||||
"^.+\\.tsx?$": [
|
"^.+\\.tsx?$": [
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { flatify } from "Utils/Flatify";
|
import { flatify } from "Utils/Flatify";
|
||||||
import Config from "Config";
|
import Config from "Config";
|
||||||
import { Generator } from "Generators/index";
|
import { Generator, resolveRefs } from "Generators/index";
|
||||||
import { put } from "Utils/Files";
|
import { put } from "Utils/Files";
|
||||||
import { Tokens } from "TokensGenerator";
|
import { Tokens } from "TokensGenerator";
|
||||||
|
|
||||||
export const cssGenerator: Generator = async (tokens, opts) => {
|
export const cssGenerator: Generator = async (tokens, opts) => {
|
||||||
|
// Replace refs by CSS variables.
|
||||||
|
tokens = resolveRefs(tokens, (ref) => {
|
||||||
|
const cssVar =
|
||||||
|
"--" +
|
||||||
|
Config.sass.varPrefix +
|
||||||
|
ref.replaceAll(".", Config.sass.varSeparator);
|
||||||
|
return `var(${cssVar})`;
|
||||||
|
});
|
||||||
|
|
||||||
const flatTokens = flatify(tokens, Config.sass.varSeparator);
|
const flatTokens = flatify(tokens, Config.sass.varSeparator);
|
||||||
const cssVars = Object.keys(flatTokens).reduce((acc, token) => {
|
const cssVars = Object.keys(flatTokens).reduce((acc, token) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ describe("JsGenerator", () => {
|
|||||||
await run(["", "", "-g", "js"]);
|
await run(["", "", "-g", "js"]);
|
||||||
expect(fs.existsSync(tokensFile)).toEqual(true);
|
expect(fs.existsSync(tokensFile)).toEqual(true);
|
||||||
expect(fs.readFileSync(tokensFile).toString()).toMatchInlineSnapshot(`
|
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"}}};
|
"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"}}};
|
||||||
"
|
"
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { Generator } from "Generators/index";
|
import { Generator, resolveRefs, resolveRefValue } from "Generators/index";
|
||||||
import Config from "Config";
|
import Config from "Config";
|
||||||
import { put } from "Utils/Files";
|
import { put } from "Utils/Files";
|
||||||
import { Tokens } from "TokensGenerator";
|
import { Tokens } from "TokensGenerator";
|
||||||
|
|
||||||
export const jsGenerator: Generator = async (tokens, opts) => {
|
export const jsGenerator: Generator = async (tokens, opts) => {
|
||||||
|
tokens = resolveRefs(tokens, resolveRefValue);
|
||||||
const dest = path.join(opts.path, Config.tokenFilename + ".js");
|
const dest = path.join(opts.path, Config.tokenFilename + ".js");
|
||||||
put(dest, await jsGeneratorContent(tokens));
|
put(dest, await jsGeneratorContent(tokens));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { Generator } from "Generators/index";
|
import { Generator, resolveRefs, resolveRefValue } from "Generators/index";
|
||||||
import Config from "Config";
|
import Config from "Config";
|
||||||
import { put } from "Utils/Files";
|
import { put } from "Utils/Files";
|
||||||
import { Tokens } from "TokensGenerator";
|
import { Tokens } from "TokensGenerator";
|
||||||
|
|
||||||
export const sassGenerator: Generator = async (tokens, opts) => {
|
export const sassGenerator: Generator = async (tokens, opts) => {
|
||||||
|
tokens = resolveRefs(tokens, resolveRefValue);
|
||||||
const sassContent = generateSassMaps(tokens);
|
const sassContent = generateSassMaps(tokens);
|
||||||
const outputPath = path.join(opts.path, Config.tokenFilename + ".scss");
|
const outputPath = path.join(opts.path, Config.tokenFilename + ".scss");
|
||||||
put(outputPath, sassContent);
|
put(outputPath, sassContent);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ describe("TsGenerator", () => {
|
|||||||
await run(["", "", "-g", "ts"]);
|
await run(["", "", "-g", "ts"]);
|
||||||
expect(fs.existsSync(tokensFile)).toEqual(true);
|
expect(fs.existsSync(tokensFile)).toEqual(true);
|
||||||
expect(fs.readFileSync(tokensFile).toString()).toMatchInlineSnapshot(`
|
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"}}};
|
"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"}}};
|
||||||
"
|
"
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { Generator } from "Generators/index";
|
import { Generator, resolveRefs, resolveRefValue } from "Generators/index";
|
||||||
import Config from "Config";
|
import Config from "Config";
|
||||||
import { put } from "Utils/Files";
|
import { put } from "Utils/Files";
|
||||||
import { jsGeneratorContent } from "Generators/JsGenerator";
|
import { jsGeneratorContent } from "Generators/JsGenerator";
|
||||||
|
|
||||||
export const tsGenerator: Generator = async (tokens, opts) => {
|
export const tsGenerator: Generator = async (tokens, opts) => {
|
||||||
|
tokens = resolveRefs(tokens, resolveRefValue);
|
||||||
const dest = path.join(opts.path, Config.tokenFilename + ".ts");
|
const dest = path.join(opts.path, Config.tokenFilename + ".ts");
|
||||||
put(dest, await jsGeneratorContent(tokens));
|
put(dest, await jsGeneratorContent(tokens));
|
||||||
};
|
};
|
||||||
|
|||||||
103
packages/tokens/src/bin/Generators/index.spec.ts
Normal file
103
packages/tokens/src/bin/Generators/index.spec.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { resolveRefs } from "Generators/index";
|
||||||
|
import { resolve } from "Utils/resolve";
|
||||||
|
import { Tokens } from "TokensGenerator";
|
||||||
|
|
||||||
|
describe("resolveRefs", () => {
|
||||||
|
it("should replace root level refs", async () => {
|
||||||
|
const tokens = {
|
||||||
|
a: "b",
|
||||||
|
c: "ref(a)",
|
||||||
|
};
|
||||||
|
const res = resolveRefs(
|
||||||
|
tokens as unknown as Tokens,
|
||||||
|
(ref, resolvingTokens) => {
|
||||||
|
return resolve(resolvingTokens, ref);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(res).toEqual({
|
||||||
|
a: "b",
|
||||||
|
c: "b",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("should replace root level refs with custom callback returning same value", async () => {
|
||||||
|
const tokens = {
|
||||||
|
a: "b",
|
||||||
|
c: "ref(a)",
|
||||||
|
d: "ref(zzzz)",
|
||||||
|
};
|
||||||
|
const res = resolveRefs(tokens as unknown as Tokens, () => {
|
||||||
|
return "idem";
|
||||||
|
});
|
||||||
|
expect(res).toEqual({
|
||||||
|
a: "b",
|
||||||
|
c: "idem",
|
||||||
|
d: "idem",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should replace nested refs", async () => {
|
||||||
|
const tokens = {
|
||||||
|
a: "b",
|
||||||
|
c: {
|
||||||
|
c1: {
|
||||||
|
c2: "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
d: {
|
||||||
|
d1: "ref(c.c1.c2)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const res = resolveRefs(
|
||||||
|
tokens as unknown as Tokens,
|
||||||
|
(ref, resolvingTokens) => {
|
||||||
|
return resolve(resolvingTokens, ref);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(res).toEqual({
|
||||||
|
a: "b",
|
||||||
|
c: {
|
||||||
|
c1: {
|
||||||
|
c2: "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
d: {
|
||||||
|
d1: "value",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("should handle transitive refs", async () => {
|
||||||
|
const tokens = {
|
||||||
|
a: "value",
|
||||||
|
b: "ref(a)",
|
||||||
|
c: "ref(b)",
|
||||||
|
d: "ref(c)",
|
||||||
|
e: "ref(d)",
|
||||||
|
};
|
||||||
|
const res = resolveRefs(
|
||||||
|
tokens as unknown as Tokens,
|
||||||
|
(ref, resolvingTokens) => {
|
||||||
|
return resolve(resolvingTokens, ref);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(res).toEqual({
|
||||||
|
a: "value",
|
||||||
|
b: "value",
|
||||||
|
c: "value",
|
||||||
|
d: "value",
|
||||||
|
e: "value",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("should throw on maximum iterations", async () => {
|
||||||
|
const tokens = {
|
||||||
|
a: "value",
|
||||||
|
b: "ref(a)",
|
||||||
|
};
|
||||||
|
expect(() => {
|
||||||
|
resolveRefs(tokens as unknown as Tokens, (ref) => {
|
||||||
|
return `ref(${ref})`;
|
||||||
|
});
|
||||||
|
}).toThrow(
|
||||||
|
"Maximum resolveRefs iterations: please reduce usage of chained references."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,6 +3,7 @@ import { sassGenerator } from "Generators/SassGenerator";
|
|||||||
import { jsGenerator } from "Generators/JsGenerator";
|
import { jsGenerator } from "Generators/JsGenerator";
|
||||||
import { Tokens } from "TokensGenerator";
|
import { Tokens } from "TokensGenerator";
|
||||||
import { tsGenerator } from "Generators/TsGenerator";
|
import { tsGenerator } from "Generators/TsGenerator";
|
||||||
|
import { resolve } from "Utils/resolve";
|
||||||
|
|
||||||
export type Generator = (
|
export type Generator = (
|
||||||
tokens: Tokens,
|
tokens: Tokens,
|
||||||
@@ -15,3 +16,81 @@ export const Generators: Record<string, Generator> = {
|
|||||||
js: jsGenerator,
|
js: jsGenerator,
|
||||||
ts: tsGenerator,
|
ts: tsGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const resolveRefs = (
|
||||||
|
tokens: Tokens,
|
||||||
|
callback: (ref: string, tokens: Tokens) => string
|
||||||
|
): Tokens => {
|
||||||
|
let refsCount = 0;
|
||||||
|
let resolved = tokens;
|
||||||
|
|
||||||
|
// Each time we encounter a leaf with a value matching ref(...) we replacing it with the return value of callback(..).
|
||||||
|
const resolveRefsAux = (toResolve_: any) => {
|
||||||
|
if (typeof toResolve_ !== "object") {
|
||||||
|
const matches = /^ref\((.+)\)$/gm.exec(toResolve_);
|
||||||
|
if (!matches) {
|
||||||
|
return toResolve_;
|
||||||
|
}
|
||||||
|
refsCount++;
|
||||||
|
return callback(matches[1], resolved);
|
||||||
|
}
|
||||||
|
const resolvedSub: any = {};
|
||||||
|
Object.entries(toResolve_).forEach(([key, value]) => {
|
||||||
|
resolvedSub[key] = resolveRefsAux(value);
|
||||||
|
});
|
||||||
|
return resolvedSub;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We need to resolveRefsAux until there is not refs found. In most cases there will be only two iterations
|
||||||
|
// ( one for resolving actual ref() and one another to make sure there were 0 ref() left ).
|
||||||
|
// But in some cases resolved ref() could result in a new ref(), so in the following example we will
|
||||||
|
// have three iterations:
|
||||||
|
//
|
||||||
|
// tokens:
|
||||||
|
// A = "foo"
|
||||||
|
// B = ref(A)
|
||||||
|
// C = ref(B)
|
||||||
|
//
|
||||||
|
// A = "foo"
|
||||||
|
// B = "foo"
|
||||||
|
// C = ref(A)
|
||||||
|
// it = 1
|
||||||
|
//
|
||||||
|
// A = "foo"
|
||||||
|
// B = "foo"
|
||||||
|
// C = "foo"
|
||||||
|
// it = 2
|
||||||
|
//
|
||||||
|
// A = "foo"
|
||||||
|
// B = "foo"
|
||||||
|
// C = "foo"
|
||||||
|
// it = 3
|
||||||
|
const maxIterations = 10;
|
||||||
|
let iterations = 0;
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
do {
|
||||||
|
// Prevent infinite loops.
|
||||||
|
if (iterations >= maxIterations) {
|
||||||
|
throw new Error(
|
||||||
|
"Maximum resolveRefs iterations: please reduce usage of chained references."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
refsCount = 0;
|
||||||
|
resolved = resolveRefsAux(resolved);
|
||||||
|
iterations++;
|
||||||
|
} while (refsCount > 0);
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is meant to be given as callback to resolveRefs.
|
||||||
|
*
|
||||||
|
* It simply retrieves the actual value of "ref" ( dot notation like "theme.colors.primary-500" ) inside
|
||||||
|
* the resolvingTokens nested object.
|
||||||
|
* @param ref
|
||||||
|
* @param resolvingTokens
|
||||||
|
*/
|
||||||
|
export const resolveRefValue = (ref: string, resolvingTokens: Tokens) => {
|
||||||
|
return resolve(resolvingTokens, ref);
|
||||||
|
};
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ export const buildTheme = async () => {
|
|||||||
const config = await getConfig();
|
const config = await getConfig();
|
||||||
const tokens = tokensGenerator(config);
|
const tokens = tokensGenerator(config);
|
||||||
const { generators } = options;
|
const { generators } = options;
|
||||||
await Promise.allSettled(
|
// Promise.all() is used to propagates upward thrown errors.
|
||||||
|
await Promise.all(
|
||||||
generators.map((generator: string) => {
|
generators.map((generator: string) => {
|
||||||
if (!Generators[generator]) {
|
if (!Generators[generator]) {
|
||||||
throw new Error('The generator "' + generator + '" does not exist.');
|
throw new Error('The generator "' + generator + '" does not exist.');
|
||||||
|
|||||||
9
packages/tokens/src/bin/Utils/resolve.ts
Normal file
9
packages/tokens/src/bin/Utils/resolve.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export const resolve = (
|
||||||
|
object: Record<string, any>,
|
||||||
|
path: string,
|
||||||
|
separator: string = "."
|
||||||
|
): any => {
|
||||||
|
return path.split(separator).reduce((acc, cur) => {
|
||||||
|
return acc[cur];
|
||||||
|
}, object);
|
||||||
|
};
|
||||||
@@ -23,5 +23,8 @@ module.exports = {
|
|||||||
transitions: {
|
transitions: {
|
||||||
ease: "linear",
|
ease: "linear",
|
||||||
},
|
},
|
||||||
|
input: {
|
||||||
|
"border-color": "ref(theme.colors.ternary-900)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,4 +8,5 @@
|
|||||||
--c--theme--font--families--base: Roboto;
|
--c--theme--font--families--base: Roboto;
|
||||||
--c--theme--spacings--s: 1rem;
|
--c--theme--spacings--s: 1rem;
|
||||||
--c--theme--transitions--ease: linear;
|
--c--theme--transitions--ease: linear;
|
||||||
|
--c--theme--input--border-color: var(--c--theme--colors--ternary-900);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,5 @@
|
|||||||
--c--theme--font--families--base: Roboto;
|
--c--theme--font--families--base: Roboto;
|
||||||
--c--theme--spacings--s: 1rem;
|
--c--theme--spacings--s: 1rem;
|
||||||
--c--theme--transitions--ease: linear;
|
--c--theme--transitions--ease: linear;
|
||||||
|
--c--theme--input--border-color: var(--c--theme--colors--ternary-900);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
--c--theme--font--families--base: Roboto;
|
--c--theme--font--families--base: Roboto;
|
||||||
--c--theme--spacings--s: 1rem;
|
--c--theme--spacings--s: 1rem;
|
||||||
--c--theme--transitions--ease: linear;
|
--c--theme--transitions--ease: linear;
|
||||||
|
--c--theme--input--border-color: var(--c--theme--colors--ternary-900);
|
||||||
} .clr-primary { color: var(--c--theme--colors--primary); }
|
} .clr-primary { color: var(--c--theme--colors--primary); }
|
||||||
.clr-secondary { color: var(--c--theme--colors--secondary); }
|
.clr-secondary { color: var(--c--theme--colors--secondary); }
|
||||||
.clr-ternary-900 { color: var(--c--theme--colors--ternary-900); }
|
.clr-ternary-900 { color: var(--c--theme--colors--ternary-900); }
|
||||||
|
|||||||
@@ -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"}},"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 = {"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":{},"light":{},"regular":{},"medium":{},"bold":{},"extrabold":{},"black":{}},"families":{"base":"\"Roboto Flex Variable\", sans-serif","accent":"\"Roboto Flex Variable\", sans-serif"}},"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"}}};
|
||||||
|
|||||||
21
packages/tokens/src/lib/index.spec.ts
Normal file
21
packages/tokens/src/lib/index.spec.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { buildRefs } from "./index";
|
||||||
|
|
||||||
|
describe("buildRefs", () => {
|
||||||
|
it("should replace raw values by ref keys", () => {
|
||||||
|
expect(
|
||||||
|
buildRefs({
|
||||||
|
theme: {
|
||||||
|
colors: {
|
||||||
|
"primary-500": "blue",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
theme: {
|
||||||
|
colors: {
|
||||||
|
"primary-500": "ref(theme.colors.primary-500)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,3 +2,39 @@ import { tokens } from "./cunningham-tokens";
|
|||||||
|
|
||||||
export type DefaultTokens = typeof tokens;
|
export type DefaultTokens = typeof tokens;
|
||||||
export const defaultTokens = tokens;
|
export const defaultTokens = tokens;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|||||||
@@ -8,4 +8,4 @@
|
|||||||
"outDir": "../../dist/lib",
|
"outDir": "../../dist/lib",
|
||||||
"declaration": true
|
"declaration": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user