unholy-design-tokens 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.github/workflows/lint-format-build.yml +35 -0
  2. package/.github/workflows/publish_release.yml +18 -0
  3. package/.prettierrc +9 -0
  4. package/ANALOGY_HOUSE.md +139 -0
  5. package/ANALOGY_I18NEXT.md +160 -0
  6. package/LICENSE +201 -0
  7. package/README.md +90 -0
  8. package/WHY_THIS_EXISTS.md +74 -0
  9. package/bin/build-tokens.ts +34 -0
  10. package/build/css/bg/bg.css +60 -0
  11. package/build/css/border/border.css +51 -0
  12. package/build/css/colors.css +204 -0
  13. package/build/css/conditional.css +8 -0
  14. package/build/css/cube/cube.block.css +18 -0
  15. package/build/css/cube/cube.composition.css +16 -0
  16. package/build/css/cube/cube.utility.css +185 -0
  17. package/build/css/font/font.css +24 -0
  18. package/build/css/space.css +20 -0
  19. package/build/css/text/text.css +48 -0
  20. package/build/css/themes/private-theme.css +228 -0
  21. package/build/css/themes/public-theme.css +228 -0
  22. package/build/css/variant/variant.css +42 -0
  23. package/build/css/variants.css +167 -0
  24. package/build/types/theme.d.ts +932 -0
  25. package/build/types/tokens.ts +653 -0
  26. package/dist/bin/build-tokens.js +27 -0
  27. package/dist/scripts/build-style-dictionary.js +32 -0
  28. package/dist/scripts/generate-typography-tokens.js +125 -0
  29. package/dist/src/colors/color.config.js +45 -0
  30. package/dist/src/colors/color.filter.js +19 -0
  31. package/dist/src/colors/color.formatter.js +25 -0
  32. package/dist/src/colors/index.js +2 -0
  33. package/dist/src/cube-css/cube.config.js +42 -0
  34. package/dist/src/cube-css/cube.formatter.js +89 -0
  35. package/dist/src/style-dictionary.config.js +143 -0
  36. package/dist/src/type-declarations/type-declarations.config.js +29 -0
  37. package/dist/src/type-declarations/type-declarations.formatter.js +111 -0
  38. package/dist/src/utils/helpers.js +9 -0
  39. package/dist/src/utils/index.js +4 -0
  40. package/dist/src/utils/template.js +83 -0
  41. package/dist/src/utils/tokens.js +80 -0
  42. package/dist/src/utils/utopia.js +19 -0
  43. package/eslint.config.js +67 -0
  44. package/package.json +60 -0
  45. package/scripts/build-style-dictionary.ts +44 -0
  46. package/scripts/generate-typography-tokens.ts +138 -0
  47. package/src/LICENSE +201 -0
  48. package/src/README.md +88 -0
  49. package/src/colors/color.config.ts +48 -0
  50. package/src/colors/color.filter.ts +28 -0
  51. package/src/colors/color.formatter.ts +43 -0
  52. package/src/colors/index.ts +6 -0
  53. package/src/cube-css/cube.config.ts +50 -0
  54. package/src/cube-css/cube.formatter.ts +104 -0
  55. package/src/formatters/spacing.js +95 -0
  56. package/src/style-dictionary.config.ts +151 -0
  57. package/src/theme/README.md +256 -0
  58. package/src/theme/cube-theme-addon.js +44 -0
  59. package/src/theme/helper.js +38 -0
  60. package/src/theme/index.js +6 -0
  61. package/src/theme/theme.config.js +42 -0
  62. package/src/theme/theme.filter.js +42 -0
  63. package/src/theme/theme.formatter.js +71 -0
  64. package/src/tokens/1 - primitives/README.md +58 -0
  65. package/src/tokens/1 - primitives/border.json +54 -0
  66. package/src/tokens/1 - primitives/breakpoint.json +10 -0
  67. package/src/tokens/1 - primitives/color-pool.json +266 -0
  68. package/src/tokens/1 - primitives/color.json +266 -0
  69. package/src/tokens/1 - primitives/font-scale.json +27 -0
  70. package/src/tokens/1 - primitives/font.json +23 -0
  71. package/src/tokens/1 - primitives/shadow.json +26 -0
  72. package/src/tokens/1 - primitives/space.json +27 -0
  73. package/src/tokens/2 - semantic/README.md +49 -0
  74. package/src/tokens/2 - semantic/border.json +27 -0
  75. package/src/tokens/2 - semantic/color.json +263 -0
  76. package/src/tokens/2 - semantic/details.md +1 -0
  77. package/src/tokens/2 - semantic/layout.json +52 -0
  78. package/src/tokens/2 - semantic/radius.json +13 -0
  79. package/src/tokens/2 - semantic/shadow.json +19 -0
  80. package/src/tokens/2 - semantic/spacing.json +25 -0
  81. package/src/tokens/3 - intent/README.md +43 -0
  82. package/src/tokens/3 - intent/background.json +135 -0
  83. package/src/tokens/3 - intent/color.json +265 -0
  84. package/src/tokens/3 - intent/font.json +61 -0
  85. package/src/tokens/3 - intent/text +67 -0
  86. package/src/tokens/README.md +176 -0
  87. package/src/tokens/color/brand.json +316 -0
  88. package/src/tokens/component/theming.json +69 -0
  89. package/src/tokens/conditional.json +40 -0
  90. package/src/tokens/custom/4 - (OPTIONAL) cube css/README.md +38 -0
  91. package/src/tokens/custom/4 - (OPTIONAL) cube css/block.json +24 -0
  92. package/src/tokens/custom/4 - (OPTIONAL) cube css/composition.json +26 -0
  93. package/src/tokens/custom/4 - (OPTIONAL) cube css/global.json +15 -0
  94. package/src/tokens/custom/4 - (OPTIONAL) cube css/utility.json +224 -0
  95. package/src/tokens/custom/OKlch/color.json +61 -0
  96. package/src/tokens/custom/OKlch/state.json +107 -0
  97. package/src/tokens/custom/OKlch/theme-color.json +34 -0
  98. package/src/tokens/custom/OKlch/variant.json +67 -0
  99. package/src/tokens/custom/components/highlighted.json +16 -0
  100. package/src/tokens/state.js +29 -0
  101. package/src/tokens/theme-color.json +34 -0
  102. package/src/type-declarations/type-declarations.config.ts +34 -0
  103. package/src/type-declarations/type-declarations.formatter.ts +122 -0
  104. package/src/utils/helpers.ts +11 -0
  105. package/src/utils/index.ts +4 -0
  106. package/src/utils/template.ts +110 -0
  107. package/src/utils/tokens.ts +95 -0
  108. package/src/utils/utopia.ts +36 -0
  109. package/tailwind.md +720 -0
  110. package/tsconfig.json +19 -0
  111. package/turbowatch.ts +14 -0
@@ -0,0 +1,122 @@
1
+ import StyleDictionary from 'style-dictionary'
2
+ import type { Format } from 'style-dictionary/types'
3
+
4
+ /**
5
+ * Generates a TypeScript interface declaration for all design tokens.
6
+ *
7
+ * @type {{ name: string; format: ({ dictionary }: FormatFnArguments) => string; }}
8
+ *
9
+ * Will return:
10
+ * export interface YThemeToken {
11
+ * tokenName1: string;
12
+ * tokenName2: string;
13
+ * ...
14
+ * }
15
+ */
16
+ StyleDictionary.registerFormat({
17
+ name: 'typescript/types-declaration',
18
+ format: function ({ dictionary }) {
19
+ const props = dictionary.tokens
20
+ console.log('📟 - props → ', props)
21
+ // const types = props.map((token) => ` '${token.name}': string;`).join("\n");
22
+
23
+ // return `export interface YThemeToken {\n${types}\n}\n`;
24
+ },
25
+ })
26
+
27
+ /**
28
+ * Generates a TypeScript interface declaration for all design tokens.
29
+ *
30
+ * Will return:
31
+ * export interface YThemeToken {
32
+ * tokenName1: string;
33
+ * tokenName2: string;
34
+ * ...
35
+ * }
36
+ *
37
+ * where tokenName is the name of the token e.g. 'color-primary-100'
38
+ */
39
+ export const tokensDeclarationFormatter: Format = {
40
+ name: 'typescript/tokens-declaration',
41
+ format: function ({ dictionary }) {
42
+ const myMap = dictionary.tokenMap
43
+ let types = ''
44
+ for (const value of myMap.values()) {
45
+ const name = value.name
46
+ types += ` '${name}': string;\n`
47
+ }
48
+
49
+ return `export interface YThemeToken {\n${types}\n}\n`
50
+ },
51
+ }
52
+
53
+ /**
54
+ * @type {{ name: string; format: ({ dictionary }: FormatFnArguments) => string; }}
55
+ *
56
+ * Will return:
57
+ * export interface DesignTokens {
58
+ * border: {
59
+ * width: {
60
+ * base: string;
61
+ * scale: string;
62
+ * };
63
+ * };
64
+ * ...
65
+ * }
66
+ */
67
+ export const typesDeclarationFormatter: Format = {
68
+ name: 'typescript/types-declaration',
69
+ format: function ({ dictionary }) {
70
+ const interfaceBody = generateNestedInterface(dictionary.tokens, 1)
71
+ return `/**
72
+ * Do not edit directly, this file was auto-generated.
73
+ */
74
+
75
+ export interface DesignTokens {
76
+ ${interfaceBody}}
77
+ `
78
+ },
79
+ }
80
+
81
+ /**
82
+ * Check if an object is a leaf token (has $value or value property)
83
+ */
84
+ function isToken(obj: Record<string, unknown>): boolean {
85
+ return obj && typeof obj === 'object' && ('$value' in obj || 'value' in obj)
86
+ }
87
+
88
+ /**
89
+ * Escape property name if it contains special characters
90
+ */
91
+ function formatKey(key: string): string {
92
+ // If key starts with a number or contains special chars, quote it
93
+ if (/^[0-9]/.test(key) || /[^a-zA-Z0-9_$]/.test(key)) {
94
+ return `'${key}'`
95
+ }
96
+ return key
97
+ }
98
+
99
+ /**
100
+ * Recursively generate nested interface from token tree
101
+ */
102
+ function generateNestedInterface(obj: Record<string, unknown>, depth = 0): string {
103
+ const indent = ' '.repeat(depth)
104
+ let result = ''
105
+
106
+ for (const key of Object.keys(obj)) {
107
+ const value = obj[key]
108
+
109
+ if (isToken(value as Record<string, unknown>)) {
110
+ // Leaf token - output as string type
111
+ result += `${indent}${formatKey(key)}: string;\n`
112
+ } else if (typeof value === 'object' && value !== null) {
113
+ // Nested object - recurse
114
+ const nested = generateNestedInterface(value as Record<string, unknown>, depth + 1)
115
+ if (nested.trim()) {
116
+ result += `${indent}${formatKey(key)}: {\n${nested}${indent}};\n`
117
+ }
118
+ }
119
+ }
120
+
121
+ return result
122
+ }
@@ -0,0 +1,11 @@
1
+ export function capitalizeFirstLetter(string: string): string {
2
+ return string.charAt(0).toUpperCase() + string.slice(1);
3
+ }
4
+
5
+ export function toKebab(s: string | number): string {
6
+ return String(s)
7
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
8
+ .replace(/[_.]/g, "-")
9
+
10
+ .toLowerCase();
11
+ }
@@ -0,0 +1,4 @@
1
+ export * from './helpers'
2
+ export * from './template'
3
+ export * from './tokens'
4
+ export * from './utopia'
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Utility functions for template generation.
3
+ * https://styledictionary.com/reference/hooks/formats/#using-a-template--templating-engine-to-create-a-format
4
+ */
5
+ import { capitalizeFirstLetter } from "./helpers.js";
6
+
7
+ export {
8
+ generateHeader,
9
+ generateSubheader,
10
+ formatInLayer,
11
+ generateFigmaHeaderReference,
12
+ };
13
+
14
+ const startComment = "/* ";
15
+ const endComment = " */";
16
+ const headerLength = 60;
17
+ const subheaderLength = 20;
18
+ const separatorChar = "-";
19
+ const headerSeparator = " ";
20
+
21
+ function generateSeparator(length: number): string {
22
+ return `${startComment}${separatorChar.repeat(length)}${endComment}`;
23
+ }
24
+
25
+ /**
26
+ * Will generate a comment block like this:
27
+ /* ------------------------------------------------------------ *\/
28
+ /* Title *\/
29
+ /* ------------------------------------------------------------ *\/
30
+ *
31
+ */
32
+ function generateCommentBlock(title: string, totalLength: number, separatorChar: string, indent = ""): string {
33
+ let titleTotalLength = totalLength - title.length;
34
+ let halfLength = Math.floor(titleTotalLength / 2);
35
+ let titleSeparator = headerSeparator.repeat(halfLength);
36
+
37
+ if (totalLength % 2 !== 0) {
38
+ titleSeparator += separatorChar;
39
+ }
40
+
41
+ return (
42
+ `\n${indent}${generateSeparator(totalLength)}` +
43
+ `\n${indent}${startComment}${titleSeparator}${title}${titleSeparator}${endComment}` +
44
+ `\n${indent}${generateSeparator(totalLength)}\n`
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Generates a header comment block for a given header string.
50
+ * /* ------------------------------------------------------------ *\/
51
+ * /* Header *\/
52
+ * /* ------------------------------------------------------------ *\/
53
+ */
54
+ function generateHeader(header: string): string {
55
+ if (!header) return "";
56
+
57
+ return generateCommentBlock(
58
+ capitalizeFirstLetter(header),
59
+ headerLength,
60
+ separatorChar,
61
+ "",
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Generates a subheader comment block for a given subheader string.
67
+ * /* ---------------------- \/*
68
+ * /* Subheader *\/
69
+ * /* ---------------------- *\/
70
+ */
71
+ function generateSubheader(subheader: string): string {
72
+ if (!subheader) return "";
73
+
74
+ return generateCommentBlock(
75
+ capitalizeFirstLetter(subheader),
76
+ subheaderLength,
77
+ separatorChar,
78
+ " ",
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Formats content within a CSS layer block.
84
+ * @layer name {
85
+ * :root {
86
+ * --variable: value;
87
+ * }
88
+ * }
89
+ */
90
+ function formatInLayer(name: string, content: string): string {
91
+ let result = `@layer ${name} {`;
92
+ result += `\n`;
93
+ result += ` :root {`;
94
+ result += `\n`;
95
+ result += `${content}`;
96
+ result += ` }\n`;
97
+ result += `}\n`;
98
+ return result;
99
+ }
100
+
101
+ /**
102
+ * Generates a Figma header reference comment block
103
+ * /* Collection name: <collectionName> *\/
104
+ * /* Mode: <mode> *\/
105
+ *
106
+ * See https://www.figma.com/community/plugin/1470777269812001046/css-variables-import-export
107
+ */
108
+ function generateFigmaHeaderReference(collectionName: string, mode = "Light"): string {
109
+ return `/* Collection name: ${collectionName} */\n/* Mode: ${mode} */`;
110
+ }
@@ -0,0 +1,95 @@
1
+ import { Dictionary, Token } from 'style-dictionary'
2
+ import { LocalOptions, TransformedToken } from 'style-dictionary/types'
3
+ import { getReferences, usesReferences } from 'style-dictionary/utils'
4
+ import { toKebab } from './helpers.js'
5
+
6
+ interface RefToken extends TransformedToken {
7
+ ref?: string[]
8
+ }
9
+
10
+ export function tokenName(token: Token) {
11
+ const attrs = token.attributes
12
+ const path = attrs?.item ? [attrs.category, attrs.type, attrs.item] : token.path
13
+ return path.join('-')
14
+ }
15
+
16
+ // TODO:
17
+ export function resolveTokenReferences(
18
+ token: Token,
19
+ options: LocalOptions,
20
+ dictionary: Dictionary,
21
+ privateVar = false,
22
+ ) {
23
+ const originalValue = token.original?.value
24
+ // if (!shouldOutputReferences(token, options, dictionary)) {
25
+ // return originalValue != null
26
+ // ? cssValue(originalValue)
27
+ // : cssValue(token.value);
28
+ // }
29
+
30
+ if (!originalValue) return cssValue(token.value)
31
+
32
+ // if (
33
+ // (originalValueParsed && originalValueParsed.startsWith("var(--")) ||
34
+ // originalValueParsed.startsWith("calc") ||
35
+ // originalValueParsed.startsWith("clamp")
36
+ // ) {
37
+ // ("📟 - cssValue(originalValue) → ", cssValue(originalValue));
38
+ // return originalValue;
39
+ // }
40
+ if (!usesReferences(originalValue)) return cssValue(token.value)
41
+ if (!shouldOutputReferences(token, options, dictionary)) {
42
+ return rewriteOriginalRefs(originalValue, dictionary.tokens, privateVar)
43
+ } else {
44
+ return cssValue(originalValue)
45
+ }
46
+ }
47
+ export function refToTokenRefString(ref: RefToken) {
48
+ // Verify if the ref is a style-dictionary reference object pattern or a direct var(--...) string
49
+ const refPath = ref.ref ?? ref.path
50
+ return `{${refPath.join('.')}}`
51
+ }
52
+
53
+ export function refToCssVar(ref: RefToken, privateVar: boolean) {
54
+ return `var(--${privateVar ? '_' : ''}${toKebab(ref.name)})`
55
+ }
56
+
57
+ const cssValue = (v: unknown): string => {
58
+ if (v == null) return ''
59
+ if (typeof v === 'string') return v
60
+ if (typeof v === 'number') return String(v)
61
+ if (typeof v === 'boolean') return v ? 'true' : 'false'
62
+ if (Array.isArray(v)) return v.join(',')
63
+ return JSON.stringify(v)
64
+ }
65
+
66
+ export function rewriteOriginalRefs(originalValue: unknown, dictionaryTokens: Dictionary['tokens'], privateVar: boolean): string | null {
67
+ if (typeof originalValue !== 'string' || !usesReferences(originalValue)) return null
68
+
69
+ const refs = getReferences(originalValue, dictionaryTokens)
70
+ let out = originalValue
71
+
72
+ for (const ref of refs) {
73
+ out = out.replaceAll(refToTokenRefString(ref), refToCssVar(ref, privateVar))
74
+ }
75
+
76
+ return out
77
+ }
78
+
79
+ export function shouldOutputReferences(token: Token, options: LocalOptions, dictionary: Dictionary): boolean {
80
+ const originalValue = token.original?.value
81
+ if (!usesReferences(originalValue)) return false
82
+ const originalValueParsed = cssValue(originalValue)
83
+ if (
84
+ (originalValueParsed && originalValueParsed.startsWith('var(--')) ||
85
+ originalValueParsed.startsWith('calc') ||
86
+ originalValueParsed.startsWith('clamp')
87
+ ) {
88
+ return false
89
+ }
90
+
91
+ const { usesDtcg, outputReferences } = options ?? {}
92
+ return typeof outputReferences === 'function'
93
+ ? outputReferences(token as TransformedToken, { dictionary, usesDtcg })
94
+ : Boolean(outputReferences)
95
+ }
@@ -0,0 +1,36 @@
1
+ // src/utils/utopia.js
2
+
3
+ /**
4
+ * Generate a Utopia fluid type scale.
5
+ *
6
+ * Based on https://utopia.fyi/type/calculator/
7
+ * by James Gilyead & Trys Mudford
8
+ */
9
+
10
+ export function generateUtopiaScale({
11
+ minViewport = 360,
12
+ maxViewport = 1280,
13
+ minFont = 16,
14
+ maxFont = 20,
15
+ scaleMin = 1.2,
16
+ scaleMax = 1.25,
17
+ steps = [-2, -1, 0, 1, 2, 3, 4],
18
+ } = {}) {
19
+ const results: Record<number, string> = {};
20
+
21
+ steps.forEach((step) => {
22
+ const minSize = minFont * scaleMin ** step;
23
+ const maxSize = maxFont * scaleMax ** step;
24
+
25
+ const slope = ((maxSize - minSize) / (maxViewport - minViewport)) * 100;
26
+ const intercept = minSize - (slope / 100) * minViewport;
27
+
28
+ const clampValue = `clamp(${minSize / 16}rem, ${
29
+ intercept / 16
30
+ }rem + ${slope}vw, ${maxSize / 16}rem)`;
31
+
32
+ results[step] = clampValue;
33
+ });
34
+
35
+ return results;
36
+ }