diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index b35469a..ce17c59 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -40,7 +40,7 @@ jobs: run: npm run build-storybook # ๋˜๋Š” npm run build-storybook - name: Run Chromatic - uses: chromaui/action@12.2.0 + uses: chromaui/action@v1 with: # ๐Ÿ‘‡ Chromatic ํ”„๋กœ์ ํŠธ ํ† ํฐ (Secrets์— ์ €์žฅ ๊ถŒ์žฅ) projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} diff --git a/app/tokens.css b/app/tokens.css index f2f2add..09e619c 100644 --- a/app/tokens.css +++ b/app/tokens.css @@ -1,2 +1,74 @@ @theme { -} + --radius-2: 0.125rem; + --radius-4: 0.25rem; + --radius-8: 0.5rem; + --radius-12: 0.75rem; + --radius-16: 1rem; + --color-label-inverse: #ffffff; + --color-label-normal: #33363d; + --color-label-strong: #1e2124; + --color-label-alternative: #464c53; + --color-label-primary: #5d5fef; + --color-label-disabled: #8a949e; + --color-label-assistive: #6d7882; + --color-background-normal: #f7f7f8; + --color-background-strong: #33363d; + --color-background-primary: #5d5fef; + --color-background-alternative: #e6e8ea; + --color-background-disabled: #b1b8be; + --color-status-negative: #ea3429; + --color-status-positive: #228738; + --spacing-gap-2: 0.125rem; + --spacing-gap-4: 0.25rem; + --spacing-gap-8: 0.5rem; + --spacing-gap-12: 0.75rem; + --spacing-gap-16: 1rem; + --spacing-gap-20: 1.25rem; + --spacing-padding-2: 0.125rem; + --spacing-padding-4: 0.25rem; + --spacing-padding-8: 0.5rem; + --spacing-padding-12: 0.75rem; + --spacing-padding-16: 1rem; + --spacing-padding-20: 1.25rem; + --radius-round: 62.438rem; + --border-normal: 0.062rem solid #cdd1d5; + --font-fontFamilies-suit-variable: SUIT Variable; + --leading-lineHeights-0: 140%; + --leading-lineHeights-1: 120%; + --leading-lineHeights-2: 140%; + --leading-lineHeights-3: 140%; + --leading-lineHeights-4: 140%; + --leading-lineHeights-5: 120%; + --leading-lineHeights-6: 120%; + --leading-lineHeights-7: 140%; + --font-weight-fontWeights-suit-variable-0: Bold; + --font-weight-fontWeights-suit-variable-1: Regular; + --font-weight-fontWeights-suit-variable-2: Medium; + --text-fontSize-0: 24; + --text-fontSize-1: 16; + --text-fontSize-2: 16; + --text-fontSize-3: 14; + --text-fontSize-4: 14; + --text-fontSize-5: 20; + --text-fontSize-6: 20; + --text-fontSize-7: 12; + --tracking-letterSpacing-0: -1%; + --tracking-letterSpacing-1: -1%; + --tracking-letterSpacing-2: -1%; + --tracking-letterSpacing-3: -1%; + --tracking-letterSpacing-4: -1%; + --tracking-letterSpacing-5: -1%; + --tracking-letterSpacing-6: -1%; + --tracking-letterSpacing-7: -1%; + --non-mapped-paragraphSpacing-0: 0; + --non-mapped-paragraphSpacing-1: 0; + --non-mapped-paragraphSpacing-2: 0; + --non-mapped-paragraphSpacing-3: 0; + --non-mapped-paragraphSpacing-4: 0; + --non-mapped-paragraphSpacing-5: 0; + --non-mapped-paragraphSpacing-6: 0; + --non-mapped-paragraphSpacing-7: 0; + --non-mapped-textCase-none: none; + --non-mapped-textDecoration-none: none; + --spacing-paragraphIndent-0: 0px; +} \ No newline at end of file diff --git a/sd-tokens.config.mjs b/sd-tokens.config.mjs index 50e9722..8bf3599 100644 --- a/sd-tokens.config.mjs +++ b/sd-tokens.config.mjs @@ -1,49 +1,128 @@ import StyleDictionary from "style-dictionary"; +import { usesReferences } from "style-dictionary/utils"; +/** + * tailwind์—์„œ ์‚ฌ์šฉ๋˜๋Š” prefix ๋งคํ•‘ + */ const token2TailwindTypeMap = { color: "color", - sizing: "dimension", - spacing: "dimension", - dimension: "dimension", - borderWidth: "border-witdh", - borderRadius: "radius", - border: "color", + sizing: "spacing", + spacing: "spacing", + dimension: "spacing", + // border๋Š” ํ•ฉ์ณ์ ธ์„œ ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•จ. + borderWidth: "border-witdh", // ์–˜๋Š” ์ผ๋‹จ ์—†์–ด์ ธ์•ผํ•จ. + borderRadius: "radius", // ์–˜๋Š” ๋”ฐ๋กœ์ž„. + border: "border", + // opacity: "opacity", boxShadow: "shadow", + // ํฐํŠธ๋„ ์ฒดํฌํ•ด๋ณผ ๊ฒƒ. fontFamilies: "font", fontWeights: "font-weight", lineHeights: "leading", - fontSizes: "font-size", + fontSizes: "text", letterSpacing: "tracking", + + // paragraphSpacing๋Š” token type์— ์กด์žฌ๋Š” ํ•˜์ง€๋งŒ ๊ฐ’์„ 0์œผ๋กœ ํ•ด ์•ˆ์“ฐ๋Š” ๋“ฏ. tailwind์—” ์—†์Œ. }; -// ์ปค์Šคํ…€ ํฌ๋งท ๋“ฑ๋ก +/** + * ํŒŒ์‹ฑ์—์„œ ์•„์˜ˆ ์ œ์™ธ์‹œํ‚ฌ ํ† ํฐ๋“ค. + * tailwind์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์œผ๋ฉด์„œ ์—๋Ÿฌ๋ฅผ ์ผ์œผํ‚ค๋Š” ๊ฒฝ์šฐ๋“ค. + * EX) + * semantic์˜ ํ† ํฐ์„ ์ฐธ์กฐ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” semantic ํ† ํฐ์˜ ๊ฒฝ์šฐ + */ +const excludingTokenNames = ["title", "body", "caption"]; + +/** + * tailwind์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก formatting + */ StyleDictionary.registerFormat({ name: "css/theme-variables", format: function ({ dictionary }) { return [ "@theme {", - ...dictionary.allTokens.map( - (prop) => - ` --${token2TailwindTypeMap?.[prop.type] ?? "non-mapped"}-${prop.name}: ${prop.value};`, - ), + ...dictionary.allTokens + .filter((prop) => !prop.name.includes("primitive")) + .map((prop) => { + //test + console.log("prop.name, prop.type: ", prop.name, prop.type); + const nameFromPath = prop.path.slice(1).join("-"); + + return ` --${token2TailwindTypeMap?.[prop.type] ?? "non-mapped"}-${nameFromPath}: ${prop.value};`; + }), "}", ].join("\n"); }, }); +/** + * ์ฐธ์กฐ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋Š” ํ† ํฐ๋“ค์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌ. + * ํ”ผ์ฐธ์กฐ๊ฐ’์€ Primitive๋กœ ๊ฐ€์ •ํ•จ. + */ +StyleDictionary.registerTransform({ + name: "value/cssVarRef", + type: "value", + transitive: true, + filter: function (token) { + // ์ด ํ† ํฐ์ด ๋‹ค๋ฅธ ํ† ํฐ์„ ์ฐธ์กฐํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + const filterRes = + typeof token.original.value === "string" && + token.original.value.startsWith("{") && + token.original.value.endsWith("}"); + + return filterRes; + }, + transform: function (token, options) { + const originalValue = token.original.value; + if (usesReferences(originalValue)) return undefined; + + throw new Error("transform ๋‚ด๋ถ€ transform defer ์—๋Ÿฌ"); + }, +}); + +/** + * ์•„๋ž˜๋Š” ์‚ฌ์šฉ๋  ํ† ํฐ๋“ค์— ๋Œ€ํ•ด Semantic์—์„œ์˜ ์ฐธ์กฐ๊ฐ’์€ Primitive์˜ ๊ฒƒ์œผ๋กœ + * ์ฐธ์กฐ ๊ฒฝ๋กœ ์žฌ๊ตฌ์„ฑํ•˜๋Š” ์ž‘์—…. + */ + +const convertRef = (stringValue, setName) => { + if (stringValue.startsWith("{")) + // ์ฐธ์กฐ์ธ "{","}"๋‚ด๋ถ€์— setName์ถ”๊ฐ€ + return `{${setName}.${stringValue.slice(1, -1)}}`; + + // ์ฐธ์กฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด + return stringValue; +}; + const recursliveParsingHelper = (setName, curKey, curValue) => { // ์ข…๋ฃŒ ์กฐ๊ฑด if (Object.keys(curValue).includes("type")) { - const prevValueHandled = curValue.value.slice(1, -1); - return { ...curValue, value: `{${setName}.${prevValueHandled}}` }; + const newValue = + typeof curValue.value === "string" + ? convertRef(curValue.value, setName) + : // object์ธ ๊ฒฝ์šฐ + Object.entries(curValue.value).reduce( + (acc, [tokenEleKey, tokenELeValue]) => { + if (tokenELeValue.startsWith("{")) + return { + ...acc, + [tokenEleKey]: convertRef(tokenELeValue, setName), + }; + return { ...acc, [tokenEleKey]: tokenELeValue }; + }, + {}, + ); + return { + [curKey]: { ...curValue, value: newValue }, + }; } const resultValue = Object.entries(curValue).reduce( (acc, [nextKey, innerObj]) => { - const resValue = recursliveParsingHelper(setName, nextKey, innerObj); - return { ...acc, [nextKey]: resValue }; + const resToken = recursliveParsingHelper(setName, nextKey, innerObj); + return { ...acc, ...resToken }; }, {}, ); @@ -58,22 +137,29 @@ StyleDictionary.registerParser({ const convertedParsed = Object.entries(parsed).reduce( (accAllTokens, [setName, tokens]) => { - if (setName.startsWith("Primitive") || setName.startsWith("$")) + if (setName.startsWith("Primitive") || setName.startsWith("$")) { return { ...accAllTokens, [setName]: tokens }; + } - const primitiveSet = setName.replace("Semantic", "Primitive"); + const primitiveTargetSet = setName.replace("Semantic", "Primitive"); const semanticTokens = Object.entries(tokens).reduce( (accSemanticTokens, [key, value]) => { + // ์ œ์™ธ์‹œํ‚ฌ ํ† ํฐ์€ ํŒŒ์‹ฑ ์ œ์™ธ. + if (excludingTokenNames.includes(key)) + return { ...accSemanticTokens }; + const newSemanticTokens = recursliveParsingHelper( - primitiveSet, + primitiveTargetSet, key, value, ); + return { ...accSemanticTokens, ...newSemanticTokens }; }, {}, ); + return { ...accAllTokens, [setName]: semanticTokens }; }, {}, @@ -83,6 +169,10 @@ StyleDictionary.registerParser({ }, }); +/** + * ํ† ํฐ ์ฒ˜๋ฆฌ ์„ค์ • + */ + export default { source: ["tokens/**/*.json"], @@ -90,6 +180,8 @@ export default { platforms: { css: { transformGroup: "css", + + transforms: ["value/cssVarRef"], buildPath: "app/", //๋ณ€ํ™˜ํ•œ ํŒŒ์ผ์„ ์ €์žฅํ•  ๊ฒฝ๋กœ files: [ { diff --git a/tokens/design-tokens.json b/tokens/design-tokens.json index 5edcf18..dbf93f3 100644 --- a/tokens/design-tokens.json +++ b/tokens/design-tokens.json @@ -803,9 +803,6 @@ }, "$themes": [], "$metadata": { - "tokenSetOrder": [ - "Primitive/light", - "Semantic/light" - ] + "tokenSetOrder": ["Primitive/light", "Semantic/light"] } -} \ No newline at end of file +}