atomsystem 0.1.26 → 0.1.28

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,269 @@
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
+ --color-bg-card: var(--bg-card);
99
+ --color-bg-wrapper: var(--bg-wrapper);
100
+ --color-bg-input-standard: var(--bg-input-standard);
101
+ --color-bg-input-hover: var(--bg-input-hover);
102
+ --color-bg-input-disabled: var(--bg-input-disabled);
103
+ --color-text-standard: var(--text-standard);
104
+ --color-text-active: var(--text-active);
105
+ --color-text-placeholder: var(--text-placeholder);
106
+ --color-text-disabled: var(--text-disabled);
107
+ --color-border-standard: var(--border-standard);
108
+ --color-border-active: var(--border-active);
109
+ --color-button-primary-bg-disabled: var(--button-primary-bg-disabled);
110
+ --color-button-primary-text-disabled: var(--button-primary-text-disabled);
111
+ --color-button-primary-border-disabled: var(--button-primary-border-disabled);
112
+ --color-button-secondary-bg-disabled: var(--button-secondary-bg-disabled);
113
+ --color-button-secondary-text-disabled: var(--button-secondary-text-disabled);
114
+ --color-button-secondary-border-disabled: var(--button-secondary-border-disabled);
115
+ --color-button-success-bg-disabled: var(--button-success-bg-disabled);
116
+ --color-button-success-text-disabled: var(--button-success-text-disabled);
117
+ --color-button-success-border-disabled: var(--button-success-border-disabled);
118
+ --color-button-danger-bg-disabled: var(--button-danger-bg-disabled);
119
+ --color-button-danger-text-disabled: var(--button-danger-text-disabled);
120
+ --color-button-danger-border-disabled: var(--button-danger-border-disabled);
121
+ --color-button-warning-bg-disabled: var(--button-warning-bg-disabled);
122
+ --color-button-warning-text-disabled: var(--button-warning-text-disabled);
123
+ --color-button-warning-border-disabled: var(--button-warning-border-disabled);
124
+ --color-button-gray-bg-disabled: var(--button-gray-bg-disabled);
125
+ --color-button-gray-text-disabled: var(--button-gray-text-disabled);
126
+ --color-button-gray-border-disabled: var(--button-gray-border-disabled);
127
+ --color-table-header-bg: var(--table-header-bg);
128
+ --color-table-header-text: var(--table-header-text);
129
+ --color-table-header-border: var(--table-header-border);
130
+ --color-table-body-bg: var(--table-body-bg);
131
+ --color-table-body-bgEven: var(--table-body-bgEven);
132
+ --color-table-body-text: var(--table-body-text);
133
+ --color-table-body-border: var(--table-body-border);
134
+ --color-pagination-bg-hover: var(--pagination-bg-hover);
135
+ --color-bg-topnav: var(--bg-topnav);
136
+ --color-text-topnav-standard: var(--text-topnav-standard);
137
+ --color-text-topnav-active: var(--text-topnav-active);
138
+ --color-text-topnav-sub: var(--text-topnav-sub);
139
+ --color-border-topnav: var(--border-topnav);
140
+ --color-bg-sidenav: var(--bg-sidenav);
141
+ --color-text-sidenav-standard: var(--text-sidenav-standard);
142
+ --color-text-sidenav-active: var(--text-sidenav-active);
143
+ --color-text-sidenav-sub: var(--text-sidenav-sub);
144
+ --color-border-sidenav: var(--border-sidenav);
145
+ }
146
+
147
+ .white {
148
+ --bg-card: var(--color-white);
149
+ --bg-wrapper: var(--color-gray-200);
150
+ --bg-input-standard: var(--color-gray-100);
151
+ --bg-input-hover: var(--color-gray-200);
152
+ --bg-input-disabled: var(--color-gray-500);
153
+ --text-standard: var(--color-dark-300);
154
+ --text-active: var(--color-gray-100);
155
+ --text-placeholder: var(--color-gray-950);
156
+ --text-disabled: var(--color-gray-950);
157
+ --border-standard: var(--color-gray-500);
158
+ --border-active: var(--color-dark-400);
159
+ --button-primary-bg-disabled: var(--color-blue-200);
160
+ --button-primary-text-disabled: var(--color-blue-400);
161
+ --button-primary-border-disabled: var(--color-blue-200);
162
+ --button-secondary-bg-disabled: var(--color-indigo-200);
163
+ --button-secondary-text-disabled: var(--color-indigo-400);
164
+ --button-secondary-border-disabled: var(--color-indigo-200);
165
+ --button-success-bg-disabled: var(--color-green-200);
166
+ --button-success-text-disabled: var(--color-green-400);
167
+ --button-success-border-disabled: var(--color-green-200);
168
+ --button-danger-bg-disabled: var(--color-red-200);
169
+ --button-danger-text-disabled: var(--color-red-400);
170
+ --button-danger-border-disabled: var(--color-red-200);
171
+ --button-warning-bg-disabled: var(--color-yellow-200);
172
+ --button-warning-text-disabled: var(--color-yellow-400);
173
+ --button-warning-border-disabled: var(--color-yellow-200);
174
+ --button-gray-bg-disabled: var(--color-gray-200);
175
+ --button-gray-text-disabled: var(--color-gray-500);
176
+ --button-gray-border-disabled: var(--color-gray-300);
177
+ --table-header-bg: var(--color-gray-400);
178
+ --table-header-text: var(--color-dark-500);
179
+ --table-header-border: var(--color-gray-500);
180
+ --table-body-bg: var(--color-white);
181
+ --table-body-bgEven: var(--color-gray-100);
182
+ --table-body-text: var(--color-dark-300);
183
+ --table-body-border: var(--color-gray-300);
184
+ --pagination-bg-hover: var(--color-gray-100);
185
+ }
186
+
187
+ .dark {
188
+ --bg-card: var(--color-dark-200);
189
+ --bg-wrapper: var(--color-black);
190
+ --bg-input-standard: var(--color-dark-300);
191
+ --bg-input-hover: var(--color-dark-400);
192
+ --bg-input-disabled: var(--color-dark-200);
193
+ --text-standard: var(--color-white);
194
+ --text-active: var(--color-white);
195
+ --text-placeholder: var(--color-gray-950);
196
+ --text-disabled: var(--color-dark-400);
197
+ --border-standard: var(--color-dark-400);
198
+ --border-active: var(--color-gray-500);
199
+ --button-primary-bg-disabled: var(--color-blue-900);
200
+ --button-primary-text-disabled: var(--color-blue-700);
201
+ --button-primary-border-disabled: var(--color-blue-800);
202
+ --button-secondary-bg-disabled: var(--color-indigo-900);
203
+ --button-secondary-text-disabled: var(--color-indigo-700);
204
+ --button-secondary-border-disabled: var(--color-indigo-800);
205
+ --button-success-bg-disabled: var(--color-green-900);
206
+ --button-success-text-disabled: var(--color-green-700);
207
+ --button-success-border-disabled: var(--color-green-800);
208
+ --button-danger-bg-disabled: var(--color-red-900);
209
+ --button-danger-text-disabled: var(--color-red-700);
210
+ --button-danger-border-disabled: var(--color-red-800);
211
+ --button-warning-bg-disabled: var(--color-yellow-900);
212
+ --button-warning-text-disabled: var(--color-yellow-700);
213
+ --button-warning-border-disabled: var(--color-yellow-800);
214
+ --button-gray-bg-disabled: var(--color-gray-800);
215
+ --button-gray-text-disabled: var(--color-gray-600);
216
+ --button-gray-border-disabled: var(--color-gray-700);
217
+ --table-header-bg: var(--color-dark-400);
218
+ --table-header-text: var(--color-gray-200);
219
+ --table-header-border: var(--color-dark-600);
220
+ --table-body-bg: var(--color-dark-200);
221
+ --table-body-bgEven: var(--color-dark-300);
222
+ --table-body-text: var(--color-gray-200);
223
+ --table-body-border: var(--color-dark-400);
224
+ --pagination-bg-hover: var(--color-dark-300);
225
+ }
226
+
227
+ .topnav-standard {
228
+ --bg-topnav: var(--color-white);
229
+ --text-topnav-standard: var(--color-dark-300);
230
+ --text-topnav-active: var(--color-blue-500);
231
+ --text-topnav-sub: var(--color-gray-600);
232
+ --border-topnav: var(--color-gray-300);
233
+ --spacing-topnav-x: var(--spacing-common-x);
234
+ --spacing-topnav-y: var(--spacing-common-y);
235
+ --spacing-topnav-gap: var(--spacing-common-x);
236
+ }
237
+
238
+ .topnav-dark {
239
+ --bg-topnav: var(--color-dark-200);
240
+ --text-topnav-standard: var(--color-white);
241
+ --text-topnav-active: var(--color-blue-400);
242
+ --text-topnav-sub: var(--color-dark-600);
243
+ --border-topnav: var(--color-dark-400);
244
+ --spacing-topnav-x: var(--spacing-common-x);
245
+ --spacing-topnav-y: var(--spacing-common-y);
246
+ --spacing-topnav-gap: var(--spacing-common-x);
247
+ }
248
+
249
+ .sidenav-standard {
250
+ --bg-sidenav: var(--color-white);
251
+ --text-sidenav-standard: var(--color-dark-300);
252
+ --text-sidenav-active: var(--color-blue-500);
253
+ --text-sidenav-sub: var(--color-gray-600);
254
+ --border-sidenav: var(--color-gray-300);
255
+ --spacing-sidenav-x: var(--spacing-common-x);
256
+ --spacing-sidenav-y: var(--spacing-common-y);
257
+ --spacing-sidenav-gap: var(--spacing-common-x);
258
+ }
259
+
260
+ .sidenav-dark {
261
+ --bg-sidenav: var(--color-dark-200);
262
+ --text-sidenav-standard: var(--color-white);
263
+ --text-sidenav-active: var(--color-blue-400);
264
+ --text-sidenav-sub: var(--color-dark-600);
265
+ --border-sidenav: var(--color-dark-400);
266
+ --spacing-sidenav-x: var(--spacing-common-x);
267
+ --spacing-sidenav-y: var(--spacing-common-y);
268
+ --spacing-sidenav-gap: var(--spacing-common-x);
269
+ }
@@ -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