@xfilecom/front-core 0.2.25 → 0.2.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.
package/README.md CHANGED
@@ -10,6 +10,10 @@ Design tokens (`tokens.css`), atomic layout/CSS (`base.css`), and browser-only R
10
10
 
11
11
  **Dark mode:** `tokens.css` 끝에 `html.dark` / `data-theme="dark"` 프리셋이 있다. 루트에 클래스 또는 속성만 켜면 동일 변수명으로 전환된다.
12
12
 
13
+ **토큰:** `--xfc-shadow-xs|sm|md|lg`, `--xfc-accent-soft` 등 계층·글로우용 변수가 있다. `tokenVars`에서 TS로 참조 가능.
14
+
15
+ **배경:** `base.css`의 `body`는 아주 옅은 액센트 그라데이션을 쓴다. 단색만 원하면 앱 CSS에서 `body { background-image: none; }` 로 끈다.
16
+
13
17
  ## React
14
18
 
15
19
  - 루트 export 또는 `@xfilecom/front-core/atoms`, `@xfilecom/front-core/overlays`.
package/dist/base.css CHANGED
@@ -23,8 +23,11 @@ body {
23
23
  font-size: var(--xfc-text-body);
24
24
  line-height: var(--xfc-leading-normal);
25
25
  color: var(--xfc-fg);
26
- background: var(--xfc-bg);
26
+ background-color: var(--xfc-bg);
27
+ background-image: radial-gradient(ellipse 120% 80% at 50% -20%, var(--xfc-accent-soft), transparent 55%);
28
+ background-attachment: fixed;
27
29
  -webkit-font-smoothing: antialiased;
30
+ text-rendering: optimizeLegibility;
28
31
  }
29
32
 
30
33
  a {
@@ -46,9 +49,10 @@ a:hover {
46
49
 
47
50
  .xfc-text--title {
48
51
  font-size: var(--xfc-text-display);
49
- font-weight: 700;
52
+ font-weight: 800;
50
53
  line-height: var(--xfc-leading-tight);
51
- letter-spacing: -0.02em;
54
+ letter-spacing: -0.035em;
55
+ font-feature-settings: 'ss01' on, 'cv01' on;
52
56
  }
53
57
 
54
58
  .xfc-text--appbar {
@@ -75,6 +79,15 @@ a:hover {
75
79
  font-size: var(--xfc-text-body);
76
80
  }
77
81
 
82
+ /** 본문보다 한 단계 큰 리드 문단 (히어로·카피) */
83
+ .xfc-text--lead {
84
+ font-size: 1.125rem;
85
+ font-weight: 500;
86
+ line-height: 1.6;
87
+ color: var(--xfc-fg-label);
88
+ letter-spacing: -0.01em;
89
+ }
90
+
78
91
  .xfc-text--small {
79
92
  font-size: var(--xfc-text-small);
80
93
  }
@@ -175,51 +188,75 @@ a:hover {
175
188
  .xfc-card {
176
189
  background: var(--xfc-bg-elevated);
177
190
  color: var(--xfc-fg);
178
- border: 1px solid var(--xfc-border);
179
- border-radius: var(--xfc-radius-md);
191
+ border: 1px solid color-mix(in srgb, var(--xfc-border) 88%, var(--xfc-fg) 6%);
192
+ border-radius: var(--xfc-radius-lg);
180
193
  box-shadow: var(--xfc-card-shadow);
181
194
  }
182
195
 
196
+ /** 클릭·링크 카드만 — 정적 패널에는 붙이지 말 것 */
197
+ .xfc-card--interactive {
198
+ cursor: pointer;
199
+ transition:
200
+ box-shadow 0.2s ease,
201
+ border-color 0.2s ease,
202
+ transform 0.2s ease;
203
+ }
204
+
205
+ @media (hover: hover) {
206
+ .xfc-card--interactive:hover {
207
+ border-color: color-mix(in srgb, var(--xfc-border-strong) 75%, var(--xfc-accent) 10%);
208
+ box-shadow: var(--xfc-shadow-md);
209
+ transform: translateY(-1px);
210
+ }
211
+
212
+ .xfc-card--interactive:active {
213
+ transform: translateY(0);
214
+ }
215
+ }
216
+
183
217
  .xfc-card__header {
184
- padding: var(--xfc-space-lg) var(--xfc-space-lg) var(--xfc-space-md);
218
+ padding: var(--xfc-space-xl) var(--xfc-space-xl) var(--xfc-space-md);
185
219
  border-bottom: 1px solid var(--xfc-border);
186
220
  font-size: var(--xfc-text-section);
187
221
  font-weight: 700;
222
+ letter-spacing: -0.02em;
188
223
  color: var(--xfc-fg-strong);
189
224
  }
190
225
 
191
226
  .xfc-card__body {
192
227
  min-width: 0;
193
- padding: var(--xfc-space-lg);
228
+ padding: var(--xfc-space-xl);
194
229
  }
195
230
 
196
231
  .xfc-card__footer {
197
- padding: 0 var(--xfc-space-lg) var(--xfc-space-lg);
232
+ padding: 0 var(--xfc-space-xl) var(--xfc-space-xl);
198
233
  border-top: 1px solid var(--xfc-border);
199
234
  padding-top: var(--xfc-space-md);
200
235
  }
201
236
 
202
- /* —— Buttons (피그마: 80×34 primary, radius 5) —— */
237
+ /* —— Buttons —— */
203
238
 
204
239
  .xfc-btn {
205
240
  display: inline-flex;
206
241
  align-items: center;
207
242
  justify-content: center;
208
- gap: var(--xfc-space-xs);
209
- min-height: 34px;
210
- padding: 0 var(--xfc-space-lg);
243
+ gap: var(--xfc-space-sm);
244
+ min-height: 40px;
245
+ padding: 0 calc(var(--xfc-space-lg) + 2px);
211
246
  font: inherit;
212
247
  font-size: var(--xfc-text-body);
213
- font-weight: 700;
248
+ font-weight: 600;
249
+ letter-spacing: -0.01em;
214
250
  line-height: 1;
215
- border-radius: var(--xfc-radius-xs);
251
+ border-radius: var(--xfc-radius-sm);
216
252
  border: 1px solid transparent;
217
253
  cursor: pointer;
218
254
  transition:
219
- background 0.15s ease,
220
- color 0.15s ease,
221
- border-color 0.15s ease,
222
- box-shadow 0.15s ease;
255
+ background 0.18s ease,
256
+ color 0.18s ease,
257
+ border-color 0.18s ease,
258
+ box-shadow 0.18s ease,
259
+ transform 0.18s ease;
223
260
  }
224
261
 
225
262
  .xfc-btn:focus-visible {
@@ -228,8 +265,10 @@ a:hover {
228
265
  }
229
266
 
230
267
  .xfc-btn:disabled {
231
- opacity: 0.5;
268
+ opacity: 0.48;
232
269
  cursor: not-allowed;
270
+ transform: none;
271
+ box-shadow: none;
233
272
  }
234
273
 
235
274
  .xfc-btn--loading {
@@ -250,52 +289,75 @@ a:hover {
250
289
  .xfc-btn--primary {
251
290
  background: var(--xfc-accent);
252
291
  color: var(--xfc-accent-fg);
292
+ box-shadow:
293
+ var(--xfc-shadow-xs),
294
+ 0 2px 12px color-mix(in srgb, var(--xfc-accent) 32%, transparent);
253
295
  }
254
296
 
255
297
  .xfc-btn--primary:hover:not(:disabled) {
256
298
  background: var(--xfc-accent-hover);
299
+ box-shadow:
300
+ var(--xfc-shadow-sm),
301
+ 0 4px 20px color-mix(in srgb, var(--xfc-accent) 38%, transparent);
302
+ }
303
+
304
+ @media (hover: hover) {
305
+ .xfc-btn--primary:hover:not(:disabled) {
306
+ transform: translateY(-1px);
307
+ }
308
+
309
+ .xfc-btn--primary:active:not(:disabled) {
310
+ transform: translateY(0);
311
+ }
257
312
  }
258
313
 
259
314
  .xfc-btn--secondary {
260
315
  background: var(--xfc-bg-elevated);
261
316
  color: var(--xfc-fg);
262
317
  border-color: var(--xfc-border-strong);
318
+ box-shadow: var(--xfc-shadow-xs);
263
319
  }
264
320
 
265
321
  .xfc-btn--secondary:hover:not(:disabled) {
266
- border-color: var(--xfc-fg-muted);
322
+ border-color: color-mix(in srgb, var(--xfc-accent) 35%, var(--xfc-border-strong));
323
+ background: color-mix(in srgb, var(--xfc-bg-muted) 40%, var(--xfc-bg-elevated));
267
324
  }
268
325
 
269
326
  .xfc-btn--outline {
270
- background: var(--xfc-bg-elevated);
327
+ background: transparent;
271
328
  color: var(--xfc-accent);
272
- border-color: var(--xfc-accent);
329
+ border-color: color-mix(in srgb, var(--xfc-accent) 45%, var(--xfc-border-strong));
330
+ box-shadow: none;
273
331
  }
274
332
 
275
333
  .xfc-btn--outline:hover:not(:disabled) {
276
- background: color-mix(in srgb, var(--xfc-accent) 6%, transparent);
334
+ background: var(--xfc-accent-soft);
335
+ border-color: var(--xfc-accent);
277
336
  }
278
337
 
279
338
  .xfc-btn--muted {
280
- background: var(--xfc-bg-disabled);
339
+ background: color-mix(in srgb, var(--xfc-bg-muted) 65%, var(--xfc-bg-elevated));
281
340
  color: var(--xfc-fg-muted);
282
- border-color: transparent;
341
+ border-color: var(--xfc-border);
342
+ box-shadow: none;
283
343
  }
284
344
 
285
345
  .xfc-btn--muted:hover:not(:disabled) {
286
- filter: brightness(0.97);
346
+ background: var(--xfc-bg-muted);
347
+ color: var(--xfc-fg-label);
287
348
  }
288
349
 
289
350
  .xfc-btn--ghost {
290
351
  background: transparent;
291
352
  color: var(--xfc-accent);
353
+ box-shadow: none;
292
354
  }
293
355
 
294
356
  .xfc-btn--ghost:hover:not(:disabled) {
295
- background: color-mix(in srgb, var(--xfc-accent) 8%, transparent);
357
+ background: var(--xfc-accent-soft);
296
358
  }
297
359
 
298
- /* —— Inputs (로그인 필드 48px, border #D4D4D4) —— */
360
+ /* —— Inputs —— */
299
361
 
300
362
  .xfc-input {
301
363
  display: block;
@@ -307,26 +369,32 @@ a:hover {
307
369
  color: var(--xfc-fg);
308
370
  background: var(--xfc-bg-elevated);
309
371
  border: 1px solid var(--xfc-border-strong);
310
- border-radius: var(--xfc-radius-xs);
372
+ border-radius: var(--xfc-radius-sm);
311
373
  box-shadow: var(--xfc-input-shadow);
312
374
  transition:
313
- border-color 0.15s ease,
314
- box-shadow 0.15s ease;
375
+ border-color 0.18s ease,
376
+ box-shadow 0.18s ease,
377
+ background-color 0.18s ease;
315
378
  }
316
379
 
317
380
  .xfc-input::placeholder {
318
381
  color: var(--xfc-fg-placeholder);
319
382
  }
320
383
 
384
+ .xfc-input:hover:not(:disabled):not(:focus) {
385
+ border-color: color-mix(in srgb, var(--xfc-border-strong) 70%, var(--xfc-fg-muted) 30%);
386
+ }
387
+
321
388
  .xfc-input:focus {
322
389
  outline: none;
323
390
  border-color: var(--xfc-accent);
324
- box-shadow: var(--xfc-input-shadow), var(--xfc-focus-ring);
391
+ box-shadow: var(--xfc-shadow-sm), var(--xfc-focus-ring);
325
392
  }
326
393
 
327
394
  .xfc-input:disabled {
328
- opacity: 0.6;
395
+ opacity: 0.55;
329
396
  cursor: not-allowed;
397
+ background: var(--xfc-bg-muted);
330
398
  }
331
399
 
332
400
  .xfc-input--invalid {
@@ -335,7 +403,7 @@ a:hover {
335
403
 
336
404
  .xfc-input--invalid:focus {
337
405
  border-color: var(--xfc-danger);
338
- box-shadow: var(--xfc-input-shadow), 0 0 0 2px color-mix(in srgb, var(--xfc-danger) 25%, transparent);
406
+ box-shadow: var(--xfc-shadow-xs), 0 0 0 3px color-mix(in srgb, var(--xfc-danger) 22%, transparent);
339
407
  }
340
408
 
341
409
  .xfc-input-description {
@@ -410,31 +478,44 @@ a:hover {
410
478
  .xfc-badge {
411
479
  display: inline-flex;
412
480
  align-items: center;
413
- padding: 2px var(--xfc-space-sm);
481
+ padding: 4px 10px;
414
482
  font-size: var(--xfc-text-small);
415
483
  font-weight: 600;
416
- line-height: 1.3;
417
- border-radius: var(--xfc-radius-xs);
484
+ line-height: 1.25;
485
+ letter-spacing: 0.01em;
486
+ border-radius: var(--xfc-radius-full);
487
+ border: 1px solid transparent;
488
+ box-shadow: var(--xfc-shadow-xs);
418
489
  }
419
490
 
420
491
  .xfc-badge--neutral {
421
- background: var(--xfc-border);
422
- color: var(--xfc-fg);
492
+ background: color-mix(in srgb, var(--xfc-bg-muted) 55%, var(--xfc-bg-elevated));
493
+ color: var(--xfc-fg-label);
494
+ border-color: var(--xfc-border);
423
495
  }
424
496
 
425
497
  .xfc-badge--accent {
426
- background: color-mix(in srgb, var(--xfc-accent) 18%, transparent);
427
- color: var(--xfc-accent-hover);
498
+ background: color-mix(in srgb, var(--xfc-accent) 16%, var(--xfc-bg-elevated));
499
+ color: var(--xfc-accent);
500
+ border-color: color-mix(in srgb, var(--xfc-accent) 28%, transparent);
428
501
  }
429
502
 
430
503
  .xfc-badge--success {
431
- background: var(--xfc-success-bg);
504
+ background: color-mix(in srgb, var(--xfc-success) 12%, var(--xfc-success-bg));
432
505
  color: var(--xfc-success);
506
+ border-color: color-mix(in srgb, var(--xfc-success) 22%, transparent);
507
+ }
508
+
509
+ .xfc-badge--warn {
510
+ background: color-mix(in srgb, var(--xfc-warning) 14%, var(--xfc-warning-bg));
511
+ color: color-mix(in srgb, var(--xfc-warning) 85%, #000);
512
+ border-color: color-mix(in srgb, var(--xfc-warning) 25%, transparent);
433
513
  }
434
514
 
435
515
  .xfc-badge--danger {
436
- background: color-mix(in srgb, var(--xfc-danger) 15%, transparent);
516
+ background: color-mix(in srgb, var(--xfc-danger) 12%, var(--xfc-bg-elevated));
437
517
  color: var(--xfc-danger);
518
+ border-color: color-mix(in srgb, var(--xfc-danger) 22%, transparent);
438
519
  }
439
520
 
440
521
  span.xfc-badge[aria-disabled='true'] {
@@ -479,17 +560,18 @@ button.xfc-badge[aria-disabled='true'] {
479
560
  }
480
561
 
481
562
  .xfc-loading-overlay-spinner {
482
- width: 48px;
483
- height: 48px;
484
- border: 4px solid color-mix(in srgb, var(--xfc-accent) 22%, transparent);
563
+ width: 52px;
564
+ height: 52px;
565
+ border: 3px solid color-mix(in srgb, var(--xfc-accent) 18%, transparent);
485
566
  border-top-color: var(--xfc-accent);
567
+ border-right-color: color-mix(in srgb, var(--xfc-accent) 45%, transparent);
486
568
  border-radius: 50%;
487
- animation: xfc-loading-overlay-spin 0.68s linear infinite;
569
+ animation: xfc-loading-overlay-spin 0.72s linear infinite;
488
570
  flex-shrink: 0;
489
571
  box-shadow:
490
- 0 0 0 2px rgba(255, 255, 255, 0.95),
491
- 0 6px 24px rgba(0, 0, 0, 0.14);
492
- filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.08));
572
+ 0 0 0 1px rgba(255, 255, 255, 0.5),
573
+ 0 8px 32px color-mix(in srgb, var(--xfc-accent) 22%, transparent),
574
+ var(--xfc-shadow-md);
493
575
  }
494
576
 
495
577
  .xfc-loading-overlay-title {
@@ -564,32 +646,38 @@ button.xfc-badge[aria-disabled='true'] {
564
646
  box-sizing: border-box;
565
647
  align-items: flex-start;
566
648
  justify-content: flex-start;
567
- gap: var(--xfc-space-lg);
568
- padding: var(--xfc-space-lg);
649
+ gap: var(--xfc-space-md);
650
+ padding: var(--xfc-space-lg) var(--xfc-space-xl);
569
651
  border-radius: var(--xfc-toast-radius);
570
652
  color: #fff;
571
653
  font-family: var(--xfc-font-sans);
572
654
  font-size: var(--xfc-text-body);
573
- font-weight: 400;
574
- line-height: 1.25;
655
+ font-weight: 500;
656
+ line-height: 1.35;
657
+ letter-spacing: -0.01em;
658
+ border: var(--xfc-toast-border);
575
659
  box-shadow: var(--xfc-toast-shadow);
576
- transition: opacity 0.2s ease;
660
+ backdrop-filter: blur(8px);
661
+ -webkit-backdrop-filter: blur(8px);
662
+ transition:
663
+ opacity 0.2s ease,
664
+ transform 0.2s ease;
577
665
  }
578
666
 
579
667
  .xfc-toast--info {
580
- background: var(--xfc-toast-info-bg);
668
+ background: linear-gradient(155deg, rgba(255, 255, 255, 0.12) 0%, transparent 42%), var(--xfc-toast-info-bg);
581
669
  }
582
670
 
583
671
  .xfc-toast--success {
584
- background: var(--xfc-toast-success-bg);
672
+ background: linear-gradient(155deg, rgba(255, 255, 255, 0.14) 0%, transparent 40%), var(--xfc-toast-success-bg);
585
673
  }
586
674
 
587
675
  .xfc-toast--warn {
588
- background: var(--xfc-toast-warn-bg);
676
+ background: linear-gradient(155deg, rgba(255, 255, 255, 0.12) 0%, transparent 40%), var(--xfc-toast-warn-bg);
589
677
  }
590
678
 
591
679
  .xfc-toast--error {
592
- background: var(--xfc-toast-error-bg);
680
+ background: linear-gradient(155deg, rgba(255, 255, 255, 0.12) 0%, transparent 40%), var(--xfc-toast-error-bg);
593
681
  }
594
682
 
595
683
  .xfc-toast__icon {
@@ -662,11 +750,13 @@ button.xfc-badge[aria-disabled='true'] {
662
750
  align-items: center;
663
751
  justify-content: space-between;
664
752
  gap: var(--xfc-space-sm);
665
- padding: var(--xfc-space-md) var(--xfc-space-lg);
666
- background: var(--xfc-danger);
753
+ padding: var(--xfc-space-md) var(--xfc-space-xl);
754
+ background: linear-gradient(135deg, color-mix(in srgb, var(--xfc-danger) 92%, #fff) 0%, var(--xfc-danger) 100%);
667
755
  color: var(--xfc-accent-fg);
668
- border-radius: var(--xfc-radius-md);
756
+ border-radius: var(--xfc-radius-lg);
669
757
  font-size: var(--xfc-text-small);
758
+ font-weight: 600;
759
+ border: 1px solid color-mix(in srgb, var(--xfc-danger) 55%, #fff);
670
760
  box-shadow: var(--xfc-toast-shadow);
671
761
  }
672
762
 
@@ -744,8 +834,10 @@ button.xfc-badge[aria-disabled='true'] {
744
834
  justify-content: center;
745
835
  padding: var(--xfc-space-lg);
746
836
  box-sizing: border-box;
747
- background: color-mix(in srgb, var(--xfc-fg) 42%, transparent);
748
- animation: xfc-dialog-backdrop-in 0.18s ease-out;
837
+ background: color-mix(in srgb, var(--xfc-fg-strong) 48%, transparent);
838
+ backdrop-filter: blur(10px) saturate(1.05);
839
+ -webkit-backdrop-filter: blur(10px) saturate(1.05);
840
+ animation: xfc-dialog-backdrop-in 0.2s ease-out;
749
841
  }
750
842
 
751
843
  .xfc-dialog-backdrop--bottom {
@@ -762,15 +854,13 @@ button.xfc-badge[aria-disabled='true'] {
762
854
  overflow: auto;
763
855
  box-sizing: border-box;
764
856
  padding: var(--xfc-space-xl);
765
- border-radius: var(--xfc-radius-md);
857
+ border-radius: var(--xfc-radius-lg);
766
858
  background: var(--xfc-bg-elevated);
767
859
  color: var(--xfc-fg);
768
- border: 1px solid var(--xfc-border);
769
- box-shadow:
770
- 0 24px 48px color-mix(in srgb, var(--xfc-fg) 12%, transparent),
771
- var(--xfc-card-shadow);
860
+ border: 1px solid color-mix(in srgb, var(--xfc-border) 88%, var(--xfc-accent) 10%);
861
+ box-shadow: var(--xfc-shadow-lg);
772
862
  font-family: var(--xfc-font-sans);
773
- animation: xfc-dialog-panel-in 0.22s ease-out;
863
+ animation: xfc-dialog-panel-in 0.24s cubic-bezier(0.22, 1, 0.36, 1);
774
864
  }
775
865
 
776
866
  .xfc-dialog-panel__title-row {
@@ -916,4 +1006,10 @@ button.xfc-btn.xfc-btn--primary.xfc-confirm-dialog__confirm--danger:hover:not(:d
916
1006
  .xfc-bottom-sheet-panel {
917
1007
  animation: none;
918
1008
  }
1009
+
1010
+ .xfc-btn--primary:hover:not(:disabled),
1011
+ .xfc-card--interactive:hover,
1012
+ .xfc-card--interactive:active {
1013
+ transform: none;
1014
+ }
919
1015
  }
@@ -1,6 +1,6 @@
1
1
  import type { HTMLAttributes, MouseEventHandler, ReactNode } from 'react';
2
2
  export type BadgeProps = Omit<HTMLAttributes<HTMLSpanElement>, 'onClick'> & {
3
- tone?: 'neutral' | 'accent' | 'success' | 'danger';
3
+ tone?: 'neutral' | 'accent' | 'success' | 'warn' | 'danger';
4
4
  children?: ReactNode;
5
5
  /** 앞쪽 아이콘·이모지 등 */
6
6
  icon?: ReactNode;
@@ -5,5 +5,7 @@ export type CardProps = HTMLAttributes<HTMLDivElement> & {
5
5
  title?: ReactNode;
6
6
  /** 하단 푸터 슬롯 */
7
7
  footer?: ReactNode;
8
+ /** true면 호버 시 살짝 떠오르는 인터랙션 (클릭 카드·타일용) */
9
+ interactive?: boolean;
8
10
  };
9
- export declare function Card({ className, children, title, footer, ...rest }: CardProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function Card({ className, children, title, footer, interactive, ...rest }: CardProps): import("react/jsx-runtime").JSX.Element;
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Card = Card;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- function Card({ className = '', children, title, footer, ...rest }) {
6
- const cls = ['xfc-card', className].filter(Boolean).join(' ');
5
+ function Card({ className = '', children, title, footer, interactive, ...rest }) {
6
+ const cls = ['xfc-card', interactive ? 'xfc-card--interactive' : '', className].filter(Boolean).join(' ');
7
7
  const structured = title != null || footer != null;
8
8
  return ((0, jsx_runtime_1.jsxs)("div", { className: cls, ...rest, children: [title != null ? (0, jsx_runtime_1.jsx)("div", { className: "xfc-card__header", children: title }) : null, structured ? (0, jsx_runtime_1.jsx)("div", { className: "xfc-card__body", children: children }) : children, footer != null ? (0, jsx_runtime_1.jsx)("div", { className: "xfc-card__footer", children: footer }) : null] }));
9
9
  }
@@ -4,6 +4,7 @@ declare const variantClass: {
4
4
  readonly appbar: "xfc-text xfc-text--appbar";
5
5
  readonly section: "xfc-text xfc-text--section";
6
6
  readonly subtitle: "xfc-text xfc-text--subtitle";
7
+ readonly lead: "xfc-text xfc-text--lead";
7
8
  readonly body: "xfc-text xfc-text--body";
8
9
  readonly muted: "xfc-text xfc-text--body xfc-text--muted";
9
10
  readonly small: "xfc-text xfc-text--small";
@@ -7,6 +7,7 @@ const variantClass = {
7
7
  appbar: 'xfc-text xfc-text--appbar',
8
8
  section: 'xfc-text xfc-text--section',
9
9
  subtitle: 'xfc-text xfc-text--subtitle',
10
+ lead: 'xfc-text xfc-text--lead',
10
11
  body: 'xfc-text xfc-text--body',
11
12
  muted: 'xfc-text xfc-text--body xfc-text--muted',
12
13
  small: 'xfc-text xfc-text--small',
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated by scripts/write-version.js — do not edit by hand */
2
- export declare const XFRAME_FRONT_CORE_VERSION: "0.2.25";
2
+ export declare const XFRAME_FRONT_CORE_VERSION: "0.2.27";
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.XFRAME_FRONT_CORE_VERSION = void 0;
4
4
  /** Auto-generated by scripts/write-version.js — do not edit by hand */
5
- exports.XFRAME_FRONT_CORE_VERSION = "0.2.25";
5
+ exports.XFRAME_FRONT_CORE_VERSION = "0.2.27";
package/dist/index.d.ts CHANGED
@@ -24,7 +24,12 @@ export declare const tokenVars: {
24
24
  readonly colorBorderStrong: "--xfc-border-strong";
25
25
  readonly colorAccent: "--xfc-accent";
26
26
  readonly colorAccentHover: "--xfc-accent-hover";
27
+ readonly colorAccentSoft: "--xfc-accent-soft";
27
28
  readonly colorDanger: "--xfc-danger";
29
+ readonly shadowXs: "--xfc-shadow-xs";
30
+ readonly shadowSm: "--xfc-shadow-sm";
31
+ readonly shadowMd: "--xfc-shadow-md";
32
+ readonly shadowLg: "--xfc-shadow-lg";
28
33
  readonly radiusXs: "--xfc-radius-xs";
29
34
  readonly radiusSm: "--xfc-radius-sm";
30
35
  readonly radiusMd: "--xfc-radius-md";
package/dist/index.js CHANGED
@@ -28,7 +28,12 @@ exports.tokenVars = {
28
28
  colorBorderStrong: '--xfc-border-strong',
29
29
  colorAccent: '--xfc-accent',
30
30
  colorAccentHover: '--xfc-accent-hover',
31
+ colorAccentSoft: '--xfc-accent-soft',
31
32
  colorDanger: '--xfc-danger',
33
+ shadowXs: '--xfc-shadow-xs',
34
+ shadowSm: '--xfc-shadow-sm',
35
+ shadowMd: '--xfc-shadow-md',
36
+ shadowLg: '--xfc-shadow-lg',
32
37
  radiusXs: '--xfc-radius-xs',
33
38
  radiusSm: '--xfc-radius-sm',
34
39
  radiusMd: '--xfc-radius-md',
package/dist/tokens.css CHANGED
@@ -1,56 +1,64 @@
1
1
  /**
2
- * Design tokens — `xframe/html/html` (business-promotion Figma export) 기준.
3
- * 참고: main.html, navi_default.html, login_01.html 인라인 스타일에서 추출.
2
+ * Design tokens — 기본 테마 (라이트).
3
+ * 슬레이트 뉴트럴 + 인디고 액센트, 계층형 섀도·라디우스.
4
4
  */
5
5
  :root {
6
6
  /* Surfaces */
7
- --xfc-bg: #ffffff;
7
+ --xfc-bg: #f4f5f8;
8
8
  --xfc-bg-elevated: #ffffff;
9
- --xfc-bg-muted: #f8f8f8;
10
- --xfc-bg-disabled: #eaeaea;
9
+ --xfc-bg-muted: #e8ebf2;
10
+ --xfc-bg-disabled: #dce0e8;
11
11
 
12
12
  /* Text */
13
- --xfc-fg: #1c252f;
14
- --xfc-fg-strong: #000000;
15
- --xfc-fg-label: #1d2125;
16
- --xfc-fg-muted: #777777;
17
- --xfc-fg-placeholder: #c7c7c7;
18
- --xfc-fg-icon: #464c50;
13
+ --xfc-fg: #1e293b;
14
+ --xfc-fg-strong: #0f172a;
15
+ --xfc-fg-label: #334155;
16
+ --xfc-fg-muted: #64748b;
17
+ --xfc-fg-placeholder: #94a3b8;
18
+ --xfc-fg-icon: #475569;
19
19
 
20
20
  /* Lines */
21
- --xfc-border: #ededed;
22
- --xfc-border-strong: #d4d4d4;
23
- --xfc-border-header: #e9e8e6;
21
+ --xfc-border: #e2e8f0;
22
+ --xfc-border-strong: #cbd5e1;
23
+ --xfc-border-header: #e2e8f0;
24
24
 
25
25
  /* Brand */
26
- --xfc-accent: #2a5bff;
27
- --xfc-accent-hover: #2248e6;
26
+ --xfc-accent: #4f46e5;
27
+ --xfc-accent-hover: #4338ca;
28
28
  --xfc-accent-fg: #ffffff;
29
+ /** 포커스·글로우용 연한 틴트 */
30
+ --xfc-accent-soft: color-mix(in srgb, var(--xfc-accent) 14%, transparent);
29
31
 
30
32
  /* Status */
31
- --xfc-danger: #f43b3c;
33
+ --xfc-danger: #e11d48;
32
34
  --xfc-success: #059669;
33
35
  --xfc-success-bg: #d1fae5;
34
36
  --xfc-warning: #d97706;
35
37
  --xfc-warning-bg: #fef3c7;
36
38
 
37
- /* Toasts (business-promotion client index.css 동일 톤) */
38
- --xfc-toast-info-bg: #1c252f;
39
- --xfc-toast-success-bg: #19af66;
40
- --xfc-toast-warn-bg: #e8a317;
41
- --xfc-toast-error-bg: #f43b3c;
42
- --xfc-toast-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
43
- --xfc-toast-radius: 6px;
44
-
45
- /* Effects */
46
- --xfc-input-shadow: 0 7px 64px rgba(0, 0, 0, 0.07);
47
- --xfc-card-shadow: 0 1px 2px rgba(28, 37, 47, 0.06);
48
-
49
- /* Radius (px 그대로 디자인 5 / 10 / pill) */
50
- --xfc-radius-xs: 5px;
51
- --xfc-radius-sm: 5px;
52
- --xfc-radius-md: 10px;
53
- --xfc-radius-lg: 10px;
39
+ /* Toasts (단색 그라데이션·글로우는 base.css 에서 보강) */
40
+ --xfc-toast-info-bg: #1e293b;
41
+ --xfc-toast-success-bg: #059669;
42
+ --xfc-toast-warn-bg: #d97706;
43
+ --xfc-toast-error-bg: #e11d48;
44
+ --xfc-toast-shadow: 0 10px 40px rgba(15, 23, 42, 0.18), 0 2px 8px rgba(15, 23, 42, 0.08);
45
+ --xfc-toast-radius: 12px;
46
+ --xfc-toast-border: 1px solid rgba(255, 255, 255, 0.14);
47
+
48
+ /* Layered shadows */
49
+ --xfc-shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
50
+ --xfc-shadow-sm: 0 2px 8px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
51
+ --xfc-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.08), 0 2px 6px rgba(15, 23, 42, 0.04);
52
+ --xfc-shadow-lg: 0 24px 48px rgba(15, 23, 42, 0.12), 0 8px 16px rgba(15, 23, 42, 0.06);
53
+
54
+ --xfc-input-shadow: var(--xfc-shadow-xs);
55
+ --xfc-card-shadow: var(--xfc-shadow-sm);
56
+
57
+ /* Radius */
58
+ --xfc-radius-xs: 8px;
59
+ --xfc-radius-sm: 10px;
60
+ --xfc-radius-md: 14px;
61
+ --xfc-radius-lg: 18px;
54
62
  --xfc-radius-full: 9999px;
55
63
 
56
64
  /* Space */
@@ -61,7 +69,7 @@
61
69
  --xfc-space-xl: 24px;
62
70
  --xfc-space-2xl: 32px;
63
71
 
64
- /* Typography — Pretendard Variable (+ 로고용 Poppins 는 앱에서 별도) */
72
+ /* Typography */
65
73
  --xfc-font-sans: 'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, system-ui,
66
74
  Roboto, 'Noto Sans KR', 'Apple SD Gothic Neo', sans-serif;
67
75
  --xfc-font-mono: ui-monospace, 'Cascadia Code', 'SF Mono', Menlo, monospace;
@@ -74,44 +82,44 @@
74
82
  --xfc-text-input: 0.9375rem;
75
83
 
76
84
  --xfc-leading-tight: 1.25;
77
- --xfc-leading-normal: 1.5;
85
+ --xfc-leading-normal: 1.55;
78
86
  --xfc-leading-label: 22px;
79
87
  --xfc-leading-menu: 50px;
80
88
 
81
89
  /* Focus */
82
- --xfc-focus-ring: 0 0 0 3px color-mix(in srgb, var(--xfc-accent) 35%, transparent);
90
+ --xfc-focus-ring: 0 0 0 3px color-mix(in srgb, var(--xfc-accent) 32%, transparent);
83
91
  }
84
92
 
85
93
  /**
86
- * Dark theme (opt-in): `<html class="dark">` or `data-theme="dark"` on `:root` / `html`.
87
- * 동일한 변수 이름만 덮어쓴다 — 앱은 토큰 참조를 바꿀 필요 없음.
94
+ * Dark theme (opt-in): `<html class="dark">` or `data-theme="dark"`.
88
95
  */
89
96
  html.dark,
90
97
  :root[data-theme='dark'],
91
98
  html[data-theme='dark'] {
92
99
  color-scheme: dark;
93
100
 
94
- --xfc-bg: #0f1419;
95
- --xfc-bg-elevated: #1a222c;
96
- --xfc-bg-muted: #141b22;
97
- --xfc-bg-disabled: #2a3440;
101
+ --xfc-bg: #0c0f14;
102
+ --xfc-bg-elevated: #151b24;
103
+ --xfc-bg-muted: #1a222d;
104
+ --xfc-bg-disabled: #2d3a4a;
98
105
 
99
- --xfc-fg: #e8eef4;
100
- --xfc-fg-strong: #ffffff;
101
- --xfc-fg-label: #dce4ec;
102
- --xfc-fg-muted: #8b9aab;
103
- --xfc-fg-placeholder: #5c6b7a;
106
+ --xfc-fg: #e2e8f0;
107
+ --xfc-fg-strong: #f8fafc;
108
+ --xfc-fg-label: #cbd5e1;
109
+ --xfc-fg-muted: #94a3b8;
110
+ --xfc-fg-placeholder: #64748b;
104
111
  --xfc-fg-icon: #a8b8c8;
105
112
 
106
- --xfc-border: #2a3440;
107
- --xfc-border-strong: #3d4a5c;
108
- --xfc-border-header: #2f3d4d;
113
+ --xfc-border: #2d3a4a;
114
+ --xfc-border-strong: #3d4f66;
115
+ --xfc-border-header: #334155;
109
116
 
110
- --xfc-accent: #5b8aff;
111
- --xfc-accent-hover: #7aa3ff;
112
- --xfc-accent-fg: #0f1419;
117
+ --xfc-accent: #818cf8;
118
+ --xfc-accent-hover: #a5b4fc;
119
+ --xfc-accent-fg: #0f172a;
120
+ --xfc-accent-soft: color-mix(in srgb, var(--xfc-accent) 18%, transparent);
113
121
 
114
- --xfc-danger: #ff6b6b;
122
+ --xfc-danger: #fb7185;
115
123
  --xfc-success: #34d399;
116
124
  --xfc-success-bg: #064e3b;
117
125
  --xfc-warning: #fbbf24;
@@ -120,11 +128,17 @@ html[data-theme='dark'] {
120
128
  --xfc-toast-info-bg: #1e293b;
121
129
  --xfc-toast-success-bg: #047857;
122
130
  --xfc-toast-warn-bg: #b45309;
123
- --xfc-toast-error-bg: #dc2626;
124
- --xfc-toast-shadow: 0 2px 12px rgba(0, 0, 0, 0.45);
131
+ --xfc-toast-error-bg: #be123c;
132
+ --xfc-toast-shadow: 0 12px 40px rgba(0, 0, 0, 0.45), 0 4px 12px rgba(0, 0, 0, 0.3);
133
+ --xfc-toast-border: 1px solid rgba(255, 255, 255, 0.08);
134
+
135
+ --xfc-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.35);
136
+ --xfc-shadow-sm: 0 4px 16px rgba(0, 0, 0, 0.25), 0 1px 3px rgba(0, 0, 0, 0.2);
137
+ --xfc-shadow-md: 0 12px 32px rgba(0, 0, 0, 0.35), 0 4px 12px rgba(0, 0, 0, 0.2);
138
+ --xfc-shadow-lg: 0 28px 56px rgba(0, 0, 0, 0.45), 0 12px 24px rgba(0, 0, 0, 0.25);
125
139
 
126
- --xfc-input-shadow: 0 4px 24px rgba(0, 0, 0, 0.25);
127
- --xfc-card-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
140
+ --xfc-input-shadow: var(--xfc-shadow-xs);
141
+ --xfc-card-shadow: var(--xfc-shadow-sm);
128
142
 
129
- --xfc-focus-ring: 0 0 0 3px color-mix(in srgb, var(--xfc-accent) 45%, transparent);
143
+ --xfc-focus-ring: 0 0 0 3px color-mix(in srgb, var(--xfc-accent) 40%, transparent);
130
144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfilecom/front-core",
3
- "version": "0.2.25",
3
+ "version": "0.2.27",
4
4
  "description": "Shared design tokens, base CSS, and atomic React components (browser-only; no Nest dependency)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",