atomsystem 0.1.26 → 0.1.27

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.
@@ -1,67 +1,218 @@
1
1
  import StyleDictionary from "style-dictionary";
2
2
 
3
- const valueWithUnit = (val) => {
4
- return typeof val === "number" ? `${val}px` : val;
5
- };
6
-
7
- // register it with register method
8
- StyleDictionary.registerFormat({
9
- name: "css/variables-multi-context",
10
- format: ({ dictionary }) => {
11
- const root = [];
12
- const light = [];
13
- const dark = [];
14
-
15
- dictionary.allTokens.map((token) => {
16
- const name = `--${token.name}`;
17
- const val = token.value;
18
- if (
19
- typeof token.value === "object" &&
20
- (token.value.light || token.value.dark)
21
- ) {
22
- if (token.value.light) {
23
- light.push(`${name}: ${valueWithUnit(val.light)};`);
24
- }
25
- if (token.value.dark) {
26
- dark.push(`${name}: ${valueWithUnit(val.dark)};`);
3
+
4
+ // 1. Style Dictionary 인스턴스 초기화
5
+ const myStyleDictionary = new StyleDictionary({
6
+ source: ["tokens/tokens.json"],
7
+ preprocessors: ['flatten-global'],
8
+ hooks: {
9
+ preprocessors: {
10
+ 'flatten-global': (dictionary) => {
11
+ // 1. global 객체를 찾습니다.
12
+ const { global, ...rest } = dictionary;
13
+
14
+ // 2. global 내부의 내용을 루트로 끌어올려 병합합니다.
15
+ // 이렇게 하면 {palette.white} 참조가 루트 레벨에서 해석 가능해집니다.
16
+ return {
17
+ ...global,
18
+ ...rest
19
+ };
27
20
  }
28
- } else {
29
- root.push(`${name}: ${valueWithUnit(val)};`);
30
21
  }
31
- });
32
- return `
33
- @import "tailwindcss";
34
-
35
- @theme {
36
- ${root.join("\n")}
37
- }
38
-
39
- :root{
40
- ${root.join("\n")}
41
- }
42
- .light {
43
- ${light.join("\n")}
44
- }
45
- .dark {
46
- ${dark.join("\n")}
47
- }`.trim();
48
- },
49
- });
50
-
51
- const sd = new StyleDictionary({
52
- source: ["token.json"],
22
+ },
53
23
  platforms: {
54
24
  css: {
55
- transformGroup: "css",
56
- buildPath: "app/",
25
+ transformGroup: "css", // CSS 기본 변환 적용 (예: size/rem, color/hex 등)
26
+ buildPath: "build/css/",
57
27
  files: [
58
28
  {
59
- destination: "globals.css",
60
- format: "css/variables-multi-context",
29
+ destination: "_variables.css",
30
+ format: "myCustomFormat", // 아래에서 정의한 커스텀 포맷 사용
31
+ options: {
32
+ outputReferences: true, // CSS 변수 참조 활성화 (var(--name))
33
+ },
61
34
  },
62
35
  ],
63
36
  },
37
+ typescript: {
38
+ transformGroup: "js",
39
+ buildPath: "build/typescript/",
40
+ files: [
41
+ {
42
+ destination: "theme.ts",
43
+ format: "theme",
44
+ },
45
+ ],
46
+ },
47
+ },
48
+ log: {
49
+ verbosity: 'verbose', // 어떤 reference를 못 찾는지 정확히 찍어줍니다.
50
+ },
51
+ });
52
+
53
+ // 2. 커스텀 포맷 등록
54
+ // 토큰 그룹을 CSS 변수 블록으로 변환합니다.
55
+ // - 'global' 카테고리 -> @theme 블록
56
+ // - 그 외 카테고리 -> .{category} 클래스 블록
57
+ myStyleDictionary.registerFormat({
58
+ name: "myCustomFormat",
59
+ format: function ({ dictionary, options }) {
60
+ // [헬퍼 함수]
61
+ // 1. getName: 토큰의 계층(type, item, subitem)을 하이픈(-)으로 연결하여 CSS 변수명 생성
62
+ // 예: palette.gray.500 -> palette-gray-500
63
+ const getName = (token) => token.path.slice(1).join("-");
64
+
65
+ // 2. getSemanticName: 시멘틱 토큰명을 생성 (예: color-card)
66
+ // 이름 규칙: $type(color)-...path -> color-bg-card
67
+ const getSemanticName = (token) => {
68
+ if (token.$type === "color") {
69
+ return [token.$type, ...token.path.slice(1)].join("-");
70
+ } else {
71
+ return null;
72
+ }
73
+ };
74
+
75
+ // [참조 맵 생성]
76
+ // {global.palette.white} 같은 참조 경로를 실제 토큰 객체로 찾기 위한 Map 생성
77
+ const tokenMap = new Map(
78
+ dictionary.allTokens.map((t) => [t.path.join("."), t])
79
+ );
80
+
81
+ // [토큰 그룹화]
82
+ // tokens.json의 최상위 카테고리(global, white, dark)별로 토큰을 묶음
83
+ const grouped = dictionary.allTokens.reduce((acc, token) => {
84
+ const category = token.attributes.category;
85
+ if (!acc[category]) acc[category] = [];
86
+ acc[category].push(token);
87
+ return acc;
88
+ }, {});
89
+
90
+ // [시멘틱 변수 수집 로직]
91
+ // global이 아닌 테마별 토큰(white, dark 등)을 순회하며 시멘틱 변수 정의를 수집함
92
+ // 목적: @theme 블록 안에 "--color-card: var(--card);" 형태의 매핑을 한 번만 선언하기 위함.
93
+ // 각 테마 파일(.white, .dark)에서는 --card 변수만 재정의하면 됨.
94
+ const themeTokens = dictionary.allTokens.filter(t => t.attributes.category !== 'palette');
95
+ const semanticMap = new Map();
96
+
97
+ themeTokens.forEach(token => {
98
+ const semanticName = getSemanticName(token); // 예: color-card
99
+ const varName = getName(token); // 예: card
100
+
101
+ // 중복 선언 방지: 동일한 시멘틱 이름이 여러 테마에 있어도 한 번만 수집
102
+ if (semanticName && !semanticMap.has(semanticName)) {
103
+ semanticMap.set(semanticName, varName);
104
+ }
105
+ });
106
+
107
+ // 수집된 시멘틱 변수들을 CSS 문자열로 변환
108
+ const semanticCss = Array.from(semanticMap.entries())
109
+ .map(([name, val]) => ` --${name}: var(--${val});`)
110
+ .join('\n');
111
+
112
+
113
+ // [최종 CSS 생성]
114
+ // 그룹별로 순회하며 CSS 블록 생성
115
+ return Object.entries(grouped)
116
+ .map(([category, tokens]) => {
117
+ // 선택자 결정:
118
+ // - 'global' 카테고리는 Tailwind CSS v4의 '@theme' 블록으로 변환
119
+ // - 나머지(white, dark)는 클래스 선택자(예: .white)로 변환
120
+ const selector = category === "palette" ? "@theme" : `.${category}`;
121
+
122
+
123
+ // 정렬
124
+ const typeOrder = ["color", "fontSizes", "fontWeights", "fontFamily", "borderRadius"];
125
+
126
+ const cssVariables = tokens
127
+ .sort((a, b) => {
128
+ const indexA = typeOrder.indexOf(a.$type);
129
+ const indexB = typeOrder.indexOf(b.$type);
130
+ if (indexA !== -1 && indexB !== -1) return indexA - indexB;
131
+ if (indexA !== -1) return -1;
132
+ if (indexB !== -1) return 1;
133
+ return 0;
134
+ })
135
+ .map((token) => {
136
+ const name = getName(token);
137
+ // 기본 값. 참조가 있다면 원본 값("{global.palette...}")을 가져옴
138
+ let value = token.$value || token.value;
139
+ const originalValue = token.original.$value || token.original.value;
140
+
141
+ // [수동 참조 해결]
142
+ // 값이 "{...}" 형태의 참조인 경우, 해당 참조를 var(--이름) 형태로 변환
143
+ if (
144
+ options?.outputReferences &&
145
+ typeof originalValue === "string" && // 값이 문자열이고
146
+ originalValue.includes("{") // '{'를 포함하면 참조로 간주
147
+ ) {
148
+ value = originalValue.replace(/{([^}]+)}/g, (match, pathStr) => {
149
+ const refToken = tokenMap.get(pathStr);
150
+ if (refToken) {
151
+ return `var(--${getName(refToken)})`;
152
+ }
153
+ return match;
154
+ });
155
+ }
156
+
157
+ if (category === "palette") {
158
+ // $type을 확인하여 borderRadius나 dimension이면 px 추가
159
+ if(token.$type === "borderRadius" | token.$type === "fontSizes" | token.$type === "spacing" | token.$type === "sizing" | token.$type === "dimension") {
160
+ if (!String(value).endsWith("px")) {
161
+ return `--${name}: ${value}px;`;
162
+ }
163
+ }
164
+ return `--${name}: ${value};`;
165
+ }
166
+ // 그 외 카테고리(테마)
167
+ if (token.$type === "color") {
168
+ return `--${name}: ${value};`;
169
+ }
170
+ if (token.$type === "spacing" || token.$type === "sizing") {
171
+ if (!String(value).endsWith("px") && !String(value).includes("var(")) {
172
+ return `--${name}: ${value}px;`;
173
+ }
174
+ return `--${name}: ${value};`;
175
+ }
176
+
177
+ return null;
178
+ })
179
+ .filter(Boolean)
180
+ .join("\n");
181
+
182
+ // global(@theme)인 경우에만 수집해둔 시멘틱 변수(semanticCss)를 추가
183
+ return `${selector} {\n${cssVariables}\n${selector === "@theme" ? `\n${semanticCss}\n` : ""}} `;
184
+ })
185
+ .join("\n\n");
186
+ },
187
+ });
188
+
189
+ // 3. theme와 관련된 커스텀 포맷 등록
190
+ // theme.ts 파일을 위한 포맷 등록
191
+ myStyleDictionary.registerFormat({
192
+ name: "theme",
193
+ format: function ({ dictionary }) {
194
+ const groupedThemes = dictionary.allTokens.reduce((acc, token) => {
195
+ const category = token.attributes.category;
196
+ if (category === "palette") return acc;
197
+
198
+ if (category.includes("-")) {
199
+ const prefix = category.split("-")[0];
200
+ if (!acc[prefix]) acc[prefix] = [];
201
+ if (!acc[prefix].includes(category)) {
202
+ acc[prefix].push(category);
203
+ }
204
+ } else {
205
+ if (!acc.global) acc.global = [];
206
+ if (!acc.global.includes(category)) {
207
+ acc.global.push(category);
208
+ }
209
+ }
210
+ return acc;
211
+ }, { global: [] });
212
+
213
+ return `export const themes: Record<string, string[]> = ${JSON.stringify(groupedThemes, null, 2)};`;
64
214
  },
65
215
  });
66
216
 
67
- await sd.buildAllPlatforms();
217
+ // 3. 모든 플랫폼 빌드
218
+ await myStyleDictionary.buildAllPlatforms();
@@ -0,0 +1,315 @@
1
+ @theme {
2
+ --color-gray-100: #f5f5f5;
3
+ --color-gray-200: #e6e6e6;
4
+ --color-gray-300: #dbdbdb;
5
+ --color-gray-400: #d4d4d4;
6
+ --color-gray-500: #cccccc;
7
+ --color-gray-600: #bfbfbf;
8
+ --color-gray-700: #b3b3b3;
9
+ --color-gray-800: #a6a6a6;
10
+ --color-gray-900: #999999;
11
+ --color-gray-950: #707070;
12
+ --color-dark-100: #111111;
13
+ --color-dark-200: #222222;
14
+ --color-dark-300: #333333;
15
+ --color-dark-400: #444444;
16
+ --color-dark-500: #555555;
17
+ --color-dark-600: #666666;
18
+ --color-dark-700: #777777;
19
+ --color-dark-800: #888888;
20
+ --color-dark-900: #999999;
21
+ --color-dark-950: #b2b2b2;
22
+ --color-blue-100: #e3f2fd;
23
+ --color-blue-200: #bbdefb;
24
+ --color-blue-300: #90caf9;
25
+ --color-blue-400: #64b5f6;
26
+ --color-blue-500: #2196f3;
27
+ --color-blue-600: #1e88e5;
28
+ --color-blue-700: #1976d2;
29
+ --color-blue-800: #1565c0;
30
+ --color-blue-900: #0d47a1;
31
+ --color-blue-950: #0a2f6b;
32
+ --color-indigo-100: #e8eaf6;
33
+ --color-indigo-200: #c5cae9;
34
+ --color-indigo-300: #7986cb;
35
+ --color-indigo-400: #3f51b5;
36
+ --color-indigo-500: #162c4e;
37
+ --color-indigo-600: #122542;
38
+ --color-indigo-700: #0e1d35;
39
+ --color-indigo-800: #0a1628;
40
+ --color-indigo-900: #070f1b;
41
+ --color-indigo-950: #04080e;
42
+ --color-red-100: #ffebee;
43
+ --color-red-200: #ffcdd2;
44
+ --color-red-300: #ef9a9a;
45
+ --color-red-400: #e57373;
46
+ --color-red-500: #f44336;
47
+ --color-red-600: #e53935;
48
+ --color-red-700: #d32f2f;
49
+ --color-red-800: #c62828;
50
+ --color-red-900: #b71c1c;
51
+ --color-red-950: #7f1212;
52
+ --color-yellow-100: #fff8e1;
53
+ --color-yellow-200: #ffecb3;
54
+ --color-yellow-300: #ffe082;
55
+ --color-yellow-400: #ffd54f;
56
+ --color-yellow-500: #ffb300;
57
+ --color-yellow-600: #ffa000;
58
+ --color-yellow-700: #ff8f00;
59
+ --color-yellow-800: #ff6f00;
60
+ --color-yellow-900: #e65100;
61
+ --color-yellow-950: #9e3600;
62
+ --color-green-100: #e8f5e9;
63
+ --color-green-200: #c8e6c9;
64
+ --color-green-300: #a5d6a7;
65
+ --color-green-400: #81c784;
66
+ --color-green-500: #4caf50;
67
+ --color-green-600: #43a047;
68
+ --color-green-700: #388e3c;
69
+ --color-green-800: #2e7d32;
70
+ --color-green-900: #1b5e20;
71
+ --color-green-950: #104015;
72
+ --color-black: #111111;
73
+ --color-white: #fafafa;
74
+ --color-primary: var(--color-blue-500);
75
+ --color-secondary: var(--color-indigo-500);
76
+ --color-danger: var(--color-red-500);
77
+ --color-warning: var(--color-yellow-500);
78
+ --color-info: var(--color-gray-900);
79
+ --color-success: var(--color-green-500);
80
+ --text-h1: 40px;
81
+ --text-h2: 36px;
82
+ --text-h3: 28px;
83
+ --text-h4: 24px;
84
+ --text-h5: 20px;
85
+ --text-h6: 16px;
86
+ --text-body: 16px;
87
+ --text-sm: 12px;
88
+ --font-weight-header: 700;
89
+ --radius-common: 4px;
90
+ --radius-circle: 10000000px;
91
+ --font-spoqaNeo: Spoqa Han Sans Neo;
92
+ --spacing-common-y: 12px;
93
+ --spacing-common-x: 16px;
94
+ --spacing-gap-small: 4px;
95
+ --spacing-sm-y: 8px;
96
+ --spacing-small: 20px;
97
+ }
98
+
99
+ @theme {
100
+ --bg-card: var(--color-white);
101
+ --color-bg-card: var(--bg-card);
102
+ --bg-wrapper: var(--color-gray-200);
103
+ --color-bg-wrapper: var(--bg-wrapper);
104
+ --bg-input-standard: var(--color-gray-100);
105
+ --color-bg-input-standard: var(--bg-input-standard);
106
+ --bg-input-hover: var(--color-gray-200);
107
+ --color-bg-input-hover: var(--bg-input-hover);
108
+ --bg-input-disabled: var(--color-gray-500);
109
+ --color-bg-input-disabled: var(--bg-input-disabled);
110
+ --text-standard: var(--color-dark-300);
111
+ --color-text-standard: var(--text-standard);
112
+ --text-active: var(--color-gray-100);
113
+ --color-text-active: var(--text-active);
114
+ --text-placeholder: var(--color-gray-950);
115
+ --color-text-placeholder: var(--text-placeholder);
116
+ --text-disabled: var(--color-gray-950);
117
+ --color-text-disabled: var(--text-disabled);
118
+ --border-standard: var(--color-gray-500);
119
+ --color-border-standard: var(--border-standard);
120
+ --border-active: var(--color-dark-400);
121
+ --color-border-active: var(--border-active);
122
+ --button-primary-bg-disabled: var(--color-blue-200);
123
+ --color-button-primary-bg-disabled: var(--button-primary-bg-disabled);
124
+ --button-primary-text-disabled: var(--color-blue-400);
125
+ --color-button-primary-text-disabled: var(--button-primary-text-disabled);
126
+ --button-primary-border-disabled: var(--color-blue-200);
127
+ --color-button-primary-border-disabled: var(--button-primary-border-disabled);
128
+ --button-secondary-bg-disabled: var(--color-indigo-200);
129
+ --color-button-secondary-bg-disabled: var(--button-secondary-bg-disabled);
130
+ --button-secondary-text-disabled: var(--color-indigo-400);
131
+ --color-button-secondary-text-disabled: var(--button-secondary-text-disabled);
132
+ --button-secondary-border-disabled: var(--color-indigo-200);
133
+ --color-button-secondary-border-disabled: var(--button-secondary-border-disabled);
134
+ --button-success-bg-disabled: var(--color-green-200);
135
+ --color-button-success-bg-disabled: var(--button-success-bg-disabled);
136
+ --button-success-text-disabled: var(--color-green-400);
137
+ --color-button-success-text-disabled: var(--button-success-text-disabled);
138
+ --button-success-border-disabled: var(--color-green-200);
139
+ --color-button-success-border-disabled: var(--button-success-border-disabled);
140
+ --button-danger-bg-disabled: var(--color-red-200);
141
+ --color-button-danger-bg-disabled: var(--button-danger-bg-disabled);
142
+ --button-danger-text-disabled: var(--color-red-400);
143
+ --color-button-danger-text-disabled: var(--button-danger-text-disabled);
144
+ --button-danger-border-disabled: var(--color-red-200);
145
+ --color-button-danger-border-disabled: var(--button-danger-border-disabled);
146
+ --button-warning-bg-disabled: var(--color-yellow-200);
147
+ --color-button-warning-bg-disabled: var(--button-warning-bg-disabled);
148
+ --button-warning-text-disabled: var(--color-yellow-400);
149
+ --color-button-warning-text-disabled: var(--button-warning-text-disabled);
150
+ --button-warning-border-disabled: var(--color-yellow-200);
151
+ --color-button-warning-border-disabled: var(--button-warning-border-disabled);
152
+ --button-gray-bg-disabled: var(--color-gray-200);
153
+ --color-button-gray-bg-disabled: var(--button-gray-bg-disabled);
154
+ --button-gray-text-disabled: var(--color-gray-500);
155
+ --color-button-gray-text-disabled: var(--button-gray-text-disabled);
156
+ --button-gray-border-disabled: var(--color-gray-300);
157
+ --color-button-gray-border-disabled: var(--button-gray-border-disabled);
158
+ --table-header-bg: var(--color-gray-400);
159
+ --color-table-header-bg: var(--table-header-bg);
160
+ --table-header-text: var(--color-dark-500);
161
+ --color-table-header-text: var(--table-header-text);
162
+ --table-header-border: var(--color-gray-500);
163
+ --color-table-header-border: var(--table-header-border);
164
+ --table-body-bg: var(--color-white);
165
+ --color-table-body-bg: var(--table-body-bg);
166
+ --table-body-bgEven: var(--color-gray-100);
167
+ --color-table-body-bgEven: var(--table-body-bgEven);
168
+ --table-body-text: var(--color-dark-300);
169
+ --color-table-body-text: var(--table-body-text);
170
+ --table-body-border: var(--color-gray-300);
171
+ --color-table-body-border: var(--table-body-border);
172
+ --pagination-bg-hover: var(--color-gray-100);
173
+ --color-pagination-bg-hover: var(--pagination-bg-hover);
174
+ }
175
+
176
+ @theme .dark {
177
+ --bg-card: var(--color-dark-200);
178
+ --color-bg-card: var(--bg-card);
179
+ --bg-wrapper: var(--color-black);
180
+ --color-bg-wrapper: var(--bg-wrapper);
181
+ --bg-input-standard: var(--color-dark-300);
182
+ --color-bg-input-standard: var(--bg-input-standard);
183
+ --bg-input-hover: var(--color-dark-400);
184
+ --color-bg-input-hover: var(--bg-input-hover);
185
+ --bg-input-disabled: var(--color-dark-200);
186
+ --color-bg-input-disabled: var(--bg-input-disabled);
187
+ --text-standard: var(--color-white);
188
+ --color-text-standard: var(--text-standard);
189
+ --text-active: var(--color-white);
190
+ --color-text-active: var(--text-active);
191
+ --text-placeholder: var(--color-gray-950);
192
+ --color-text-placeholder: var(--text-placeholder);
193
+ --text-disabled: var(--color-dark-400);
194
+ --color-text-disabled: var(--text-disabled);
195
+ --border-standard: var(--color-dark-400);
196
+ --color-border-standard: var(--border-standard);
197
+ --border-active: var(--color-gray-500);
198
+ --color-border-active: var(--border-active);
199
+ --button-primary-bg-disabled: var(--color-blue-900);
200
+ --color-button-primary-bg-disabled: var(--button-primary-bg-disabled);
201
+ --button-primary-text-disabled: var(--color-blue-700);
202
+ --color-button-primary-text-disabled: var(--button-primary-text-disabled);
203
+ --button-primary-border-disabled: var(--color-blue-800);
204
+ --color-button-primary-border-disabled: var(--button-primary-border-disabled);
205
+ --button-secondary-bg-disabled: var(--color-indigo-900);
206
+ --color-button-secondary-bg-disabled: var(--button-secondary-bg-disabled);
207
+ --button-secondary-text-disabled: var(--color-indigo-700);
208
+ --color-button-secondary-text-disabled: var(--button-secondary-text-disabled);
209
+ --button-secondary-border-disabled: var(--color-indigo-800);
210
+ --color-button-secondary-border-disabled: var(--button-secondary-border-disabled);
211
+ --button-success-bg-disabled: var(--color-green-900);
212
+ --color-button-success-bg-disabled: var(--button-success-bg-disabled);
213
+ --button-success-text-disabled: var(--color-green-700);
214
+ --color-button-success-text-disabled: var(--button-success-text-disabled);
215
+ --button-success-border-disabled: var(--color-green-800);
216
+ --color-button-success-border-disabled: var(--button-success-border-disabled);
217
+ --button-danger-bg-disabled: var(--color-red-900);
218
+ --color-button-danger-bg-disabled: var(--button-danger-bg-disabled);
219
+ --button-danger-text-disabled: var(--color-red-700);
220
+ --color-button-danger-text-disabled: var(--button-danger-text-disabled);
221
+ --button-danger-border-disabled: var(--color-red-800);
222
+ --color-button-danger-border-disabled: var(--button-danger-border-disabled);
223
+ --button-warning-bg-disabled: var(--color-yellow-900);
224
+ --color-button-warning-bg-disabled: var(--button-warning-bg-disabled);
225
+ --button-warning-text-disabled: var(--color-yellow-700);
226
+ --color-button-warning-text-disabled: var(--button-warning-text-disabled);
227
+ --button-warning-border-disabled: var(--color-yellow-800);
228
+ --color-button-warning-border-disabled: var(--button-warning-border-disabled);
229
+ --button-gray-bg-disabled: var(--color-gray-800);
230
+ --color-button-gray-bg-disabled: var(--button-gray-bg-disabled);
231
+ --button-gray-text-disabled: var(--color-gray-600);
232
+ --color-button-gray-text-disabled: var(--button-gray-text-disabled);
233
+ --button-gray-border-disabled: var(--color-gray-700);
234
+ --color-button-gray-border-disabled: var(--button-gray-border-disabled);
235
+ --table-header-bg: var(--color-dark-400);
236
+ --color-table-header-bg: var(--table-header-bg);
237
+ --table-header-text: var(--color-gray-200);
238
+ --color-table-header-text: var(--table-header-text);
239
+ --table-header-border: var(--color-dark-600);
240
+ --color-table-header-border: var(--table-header-border);
241
+ --table-body-bg: var(--color-dark-200);
242
+ --color-table-body-bg: var(--table-body-bg);
243
+ --table-body-bgEven: var(--color-dark-300);
244
+ --color-table-body-bgEven: var(--table-body-bgEven);
245
+ --table-body-text: var(--color-gray-200);
246
+ --color-table-body-text: var(--table-body-text);
247
+ --table-body-border: var(--color-dark-400);
248
+ --color-table-body-border: var(--table-body-border);
249
+ --pagination-bg-hover: var(--color-dark-300);
250
+ --color-pagination-bg-hover: var(--pagination-bg-hover);
251
+ }
252
+
253
+ @theme .topnav-standard {
254
+ --bg-topnav: var(--color-white);
255
+ --color-bg-topnav: var(--bg-topnav);
256
+ --text-topnav-standard: var(--color-dark-300);
257
+ --color-text-topnav-standard: var(--text-topnav-standard);
258
+ --text-topnav-active: var(--color-blue-500);
259
+ --color-text-topnav-active: var(--text-topnav-active);
260
+ --text-topnav-sub: var(--color-gray-600);
261
+ --color-text-topnav-sub: var(--text-topnav-sub);
262
+ --border-topnav: var(--color-gray-300);
263
+ --color-border-topnav: var(--border-topnav);
264
+ --spacing-topnav-x: var(--spacing-common-x);
265
+ --spacing-topnav-y: var(--spacing-common-y);
266
+ --spacing-topnav-gap: var(--spacing-common-x);
267
+ }
268
+
269
+ @theme .topnav-dark {
270
+ --bg-topnav: var(--color-dark-200);
271
+ --color-bg-topnav: var(--bg-topnav);
272
+ --text-topnav-standard: var(--color-white);
273
+ --color-text-topnav-standard: var(--text-topnav-standard);
274
+ --text-topnav-active: var(--color-blue-400);
275
+ --color-text-topnav-active: var(--text-topnav-active);
276
+ --text-topnav-sub: var(--color-dark-600);
277
+ --color-text-topnav-sub: var(--text-topnav-sub);
278
+ --border-topnav: var(--color-dark-400);
279
+ --color-border-topnav: var(--border-topnav);
280
+ --spacing-topnav-x: var(--spacing-common-x);
281
+ --spacing-topnav-y: var(--spacing-common-y);
282
+ --spacing-topnav-gap: var(--spacing-common-x);
283
+ }
284
+
285
+ @theme .sidenav-standard {
286
+ --bg-sidenav: var(--color-white);
287
+ --color-bg-sidenav: var(--bg-sidenav);
288
+ --text-sidenav-standard: var(--color-dark-300);
289
+ --color-text-sidenav-standard: var(--text-sidenav-standard);
290
+ --text-sidenav-active: var(--color-blue-500);
291
+ --color-text-sidenav-active: var(--text-sidenav-active);
292
+ --text-sidenav-sub: var(--color-gray-600);
293
+ --color-text-sidenav-sub: var(--text-sidenav-sub);
294
+ --border-sidenav: var(--color-gray-300);
295
+ --color-border-sidenav: var(--border-sidenav);
296
+ --spacing-sidenav-x: var(--spacing-common-x);
297
+ --spacing-sidenav-y: var(--spacing-common-y);
298
+ --spacing-sidenav-gap: var(--spacing-common-x);
299
+ }
300
+
301
+ @theme .sidenav-dark {
302
+ --bg-sidenav: var(--color-dark-200);
303
+ --color-bg-sidenav: var(--bg-sidenav);
304
+ --text-sidenav-standard: var(--color-white);
305
+ --color-text-sidenav-standard: var(--text-sidenav-standard);
306
+ --text-sidenav-active: var(--color-blue-400);
307
+ --color-text-sidenav-active: var(--text-sidenav-active);
308
+ --text-sidenav-sub: var(--color-dark-600);
309
+ --color-text-sidenav-sub: var(--text-sidenav-sub);
310
+ --border-sidenav: var(--color-dark-400);
311
+ --color-border-sidenav: var(--border-sidenav);
312
+ --spacing-sidenav-x: var(--spacing-common-x);
313
+ --spacing-sidenav-y: var(--spacing-common-y);
314
+ --spacing-sidenav-gap: var(--spacing-common-x);
315
+ }
@@ -0,0 +1,14 @@
1
+ export const themes: Record<string, string[]> = {
2
+ "global": [
3
+ "white",
4
+ "dark"
5
+ ],
6
+ "topnav": [
7
+ "topnav-standard",
8
+ "topnav-dark"
9
+ ],
10
+ "sidenav": [
11
+ "sidenav-standard",
12
+ "sidenav-dark"
13
+ ]
14
+ };
package/cli/add.js CHANGED
@@ -34,21 +34,53 @@ const components = [
34
34
  const args = process.argv.slice(2); // 'npx atomsystem-add Button -> [Button]'
35
35
  const component = args[0] || "all"; // '사용자가 입력하지 않으면 기본적으로 'all''
36
36
 
37
+ // 일부 컴포넌트의 의존성 구조 (Theme 설치시 내부적으로 Button, Form, Modal 등이 필요함)
38
+ const dependenciesMap = {
39
+ Theme: ["Button", "Form", "Modal"], // Form 안에는 Radio가 들어있음
40
+ };
41
+
42
+ // 재귀 방지를 위한 Set
43
+ const addedComponents = new Set();
44
+
37
45
  function addComponent(comp) {
46
+ if (addedComponents.has(comp)) return;
47
+ addedComponents.add(comp);
38
48
  const componentPath = path.join(templateDir, comp, `${comp}.tsx`);
39
49
  const targetDir = path.join(process.cwd(), "app", "components", comp);
40
50
  const targetPath = path.join(targetDir, `${comp}.tsx`);
41
51
 
52
+ // 컴포넌트의 CSS 파일 복제 지원
53
+ const cssFileName = `${comp}.css`.toLowerCase();
54
+ const sourceCssPath = path.join(__dirname, "..", "css", cssFileName);
55
+ const targetCssPath = path.join(targetDir, cssFileName);
56
+
42
57
  if (fs.existsSync(componentPath)) {
43
58
  // 대상 디렉토리가 없으면 생성
44
59
  if (!fs.existsSync(targetDir)) {
45
60
  fs.mkdirSync(targetDir, { recursive: true });
46
61
  console.log(`Created directory: ${targetDir}`);
47
62
  }
63
+
64
+ // 1) TSX 복사
48
65
  copyFileSync(componentPath, targetPath);
49
- console.log(`${comp} component has been added to ${targetPath}`);
66
+ console.log(`✅ ${comp} component typescript has been added to ${targetPath}`);
67
+
68
+ // 2) CSS 복사
69
+ if (fs.existsSync(sourceCssPath)) {
70
+ copyFileSync(sourceCssPath, targetCssPath);
71
+ console.log(`✅ ${comp} component stylesheet has been added to ${targetCssPath}`);
72
+ }
73
+ // 3) 의존성 컴포넌트 자동 복사 (Recursive Copy)
74
+ if (dependenciesMap[comp]) {
75
+ console.log(`\n⏳ Checking dependencies for ${comp}...`);
76
+ dependenciesMap[comp].forEach((dep) => {
77
+ addComponent(dep);
78
+ });
79
+ console.log(`✅ Finished adding dependencies for ${comp}.\n`);
80
+ }
81
+
50
82
  } else {
51
- console.log(`Component ${comp} does not exist in the atomsystem.`);
83
+ console.log(`❌ Component ${comp} does not exist in the atomsystem.`);
52
84
  }
53
85
  }
54
86