@foxeltech/angular-ui 0.7.44 → 0.7.83

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 (53) hide show
  1. package/components.css +345 -19
  2. package/familjen-grotesk/familjen-grotesk-400-latin-ext.woff2 +0 -0
  3. package/familjen-grotesk/familjen-grotesk-400-latin.woff2 +0 -0
  4. package/familjen-grotesk/familjen-grotesk-400-vietnamese.woff2 +0 -0
  5. package/familjen-grotesk/familjen-grotesk-500-latin-ext.woff2 +0 -0
  6. package/familjen-grotesk/familjen-grotesk-500-latin.woff2 +0 -0
  7. package/familjen-grotesk/familjen-grotesk-500-vietnamese.woff2 +0 -0
  8. package/familjen-grotesk/familjen-grotesk-600-latin-ext.woff2 +0 -0
  9. package/familjen-grotesk/familjen-grotesk-600-latin.woff2 +0 -0
  10. package/familjen-grotesk/familjen-grotesk-600-vietnamese.woff2 +0 -0
  11. package/familjen-grotesk/familjen-grotesk-700-latin-ext.woff2 +0 -0
  12. package/familjen-grotesk/familjen-grotesk-700-latin.woff2 +0 -0
  13. package/familjen-grotesk/familjen-grotesk-700-vietnamese.woff2 +0 -0
  14. package/fesm2022/foxeltech-angular-ui-chart.mjs +197 -24
  15. package/fesm2022/foxeltech-angular-ui-chart.mjs.map +1 -1
  16. package/fesm2022/foxeltech-angular-ui.mjs +585 -125
  17. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
  18. package/fonts.css +104 -3
  19. package/package.json +1 -1
  20. package/src/lib/styles/components.css +345 -19
  21. package/src/lib/styles/fonts.css +104 -3
  22. package/src/lib/styles/tailwind.css +12 -0
  23. package/src/lib/styles/theme.css +160 -114
  24. package/src/lib/styles/tokens.css +25 -0
  25. package/src/lib/styles/utilities.css +18 -1
  26. package/src/lib/ui/components/button/button.component.html +2 -0
  27. package/src/lib/ui/components/drawer/drawer.component.scss +3 -1
  28. package/src/lib/ui/components/facets-rail/facets-rail.component.html +1 -6
  29. package/src/lib/ui/components/master-detail/master-detail.component.css +65 -13
  30. package/src/lib/ui/components/master-detail/master-detail.component.html +26 -8
  31. package/src/lib/ui/components/pending-badge/pending-badge.component.html +2 -2
  32. package/src/lib/ui/components/project-card/project-card.component.html +7 -3
  33. package/src/lib/ui/components/route-skeleton/route-skeleton.component.css +38 -0
  34. package/src/lib/ui/components/route-skeleton/route-skeleton.component.html +184 -0
  35. package/src/lib/ui/components/sample-frame/sample-frame.component.html +6 -7
  36. package/src/lib/ui/components/section-card/section-card.component.html +4 -6
  37. package/src/lib/ui/components/segmented/segmented.component.html +3 -1
  38. package/src/lib/ui/components/skeleton-dashboard/skeleton-dashboard.component.html +24 -0
  39. package/src/lib/ui/components/skeleton-detail/skeleton-detail.component.html +8 -8
  40. package/src/lib/ui/components/skeleton-facets/skeleton-facets.component.html +15 -0
  41. package/src/lib/ui/components/skeleton-frame/skeleton-frame.component.html +59 -0
  42. package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +6 -6
  43. package/src/lib/ui/components/switch/switch.component.html +1 -1
  44. package/src/lib/ui/components/toast/toast.component.html +6 -6
  45. package/tailwind.css +12 -0
  46. package/theme.css +160 -114
  47. package/tokens.css +25 -0
  48. package/types/foxeltech-angular-ui-chart.d.ts +76 -1
  49. package/types/foxeltech-angular-ui.d.ts +318 -39
  50. package/utilities.css +18 -1
  51. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.html +0 -21
  52. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.scss +0 -0
  53. package/src/lib/ui/components/tab-component/tab.component.html +0 -1
package/fonts.css CHANGED
@@ -1,6 +1,11 @@
1
- /* Self-hosted V2 fonts (Archivo / JetBrains Mono / IBM Plex Mono / Newsreader /
2
- Source Serif 4) — woff2 extracted from the V2 design bundle by
3
- tools/extract-og-tokens.mjs; re-run it to regenerate this file.
1
+ /* @font-face cho bộ font của design V2 (Familjen Grotesk, JetBrains Mono,
2
+ Archivo, IBM Plex Mono, Newsreader, Source Serif 4) — woff2 bóc từ bundle
3
+ design bởi tools/extract-og-tokens.mjs; chạy lại script để sinh lại file.
4
+
5
+ Familjen Grotesk là font THÂN + TIÊU ĐỀ thực tế của demo (lớp override đè
6
+ lên mọi theme); JetBrains Mono dùng cho mono + figure. Bốn họ còn lại là
7
+ mặc định của lớp base OgeniumDashboards, hiện KHÔNG theme nào trỏ tới —
8
+ giữ lại để đổi theme không phải bóc lại font.
4
9
 
5
10
  NOT imported by tailwind.css: v4 inlines imports and rebases font url()s
6
11
  against the entry file, breaking resolution in consumers. Consumers add
@@ -545,6 +550,102 @@
545
550
  src: url('source-serif-4/source-serif-4-600-latin.woff2') format('woff2');
546
551
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
547
552
  }
553
+ @font-face {
554
+ font-family: 'Familjen Grotesk';
555
+ font-style: normal;
556
+ font-weight: 400;
557
+ font-display: swap;
558
+ src: url('familjen-grotesk/familjen-grotesk-400-vietnamese.woff2') format('woff2');
559
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
560
+ }
561
+ @font-face {
562
+ font-family: 'Familjen Grotesk';
563
+ font-style: normal;
564
+ font-weight: 400;
565
+ font-display: swap;
566
+ src: url('familjen-grotesk/familjen-grotesk-400-latin-ext.woff2') format('woff2');
567
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
568
+ }
569
+ @font-face {
570
+ font-family: 'Familjen Grotesk';
571
+ font-style: normal;
572
+ font-weight: 400;
573
+ font-display: swap;
574
+ src: url('familjen-grotesk/familjen-grotesk-400-latin.woff2') format('woff2');
575
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
576
+ }
577
+ @font-face {
578
+ font-family: 'Familjen Grotesk';
579
+ font-style: normal;
580
+ font-weight: 500;
581
+ font-display: swap;
582
+ src: url('familjen-grotesk/familjen-grotesk-500-vietnamese.woff2') format('woff2');
583
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
584
+ }
585
+ @font-face {
586
+ font-family: 'Familjen Grotesk';
587
+ font-style: normal;
588
+ font-weight: 500;
589
+ font-display: swap;
590
+ src: url('familjen-grotesk/familjen-grotesk-500-latin-ext.woff2') format('woff2');
591
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
592
+ }
593
+ @font-face {
594
+ font-family: 'Familjen Grotesk';
595
+ font-style: normal;
596
+ font-weight: 500;
597
+ font-display: swap;
598
+ src: url('familjen-grotesk/familjen-grotesk-500-latin.woff2') format('woff2');
599
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
600
+ }
601
+ @font-face {
602
+ font-family: 'Familjen Grotesk';
603
+ font-style: normal;
604
+ font-weight: 600;
605
+ font-display: swap;
606
+ src: url('familjen-grotesk/familjen-grotesk-600-vietnamese.woff2') format('woff2');
607
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
608
+ }
609
+ @font-face {
610
+ font-family: 'Familjen Grotesk';
611
+ font-style: normal;
612
+ font-weight: 600;
613
+ font-display: swap;
614
+ src: url('familjen-grotesk/familjen-grotesk-600-latin-ext.woff2') format('woff2');
615
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
616
+ }
617
+ @font-face {
618
+ font-family: 'Familjen Grotesk';
619
+ font-style: normal;
620
+ font-weight: 600;
621
+ font-display: swap;
622
+ src: url('familjen-grotesk/familjen-grotesk-600-latin.woff2') format('woff2');
623
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
624
+ }
625
+ @font-face {
626
+ font-family: 'Familjen Grotesk';
627
+ font-style: normal;
628
+ font-weight: 700;
629
+ font-display: swap;
630
+ src: url('familjen-grotesk/familjen-grotesk-700-vietnamese.woff2') format('woff2');
631
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
632
+ }
633
+ @font-face {
634
+ font-family: 'Familjen Grotesk';
635
+ font-style: normal;
636
+ font-weight: 700;
637
+ font-display: swap;
638
+ src: url('familjen-grotesk/familjen-grotesk-700-latin-ext.woff2') format('woff2');
639
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
640
+ }
641
+ @font-face {
642
+ font-family: 'Familjen Grotesk';
643
+ font-style: normal;
644
+ font-weight: 700;
645
+ font-display: swap;
646
+ src: url('familjen-grotesk/familjen-grotesk-700-latin.woff2') format('woff2');
647
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
648
+ }
548
649
  @font-face {
549
650
  font-family: 'JetBrains Mono';
550
651
  font-style: normal;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxeltech/angular-ui",
3
- "version": "0.7.44",
3
+ "version": "0.7.83",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -299,23 +299,35 @@ input[type='password'] {
299
299
  .txt-tag-default {
300
300
  @apply txt-tag-common text-text-secondary;
301
301
  }
302
+ /* ---- fx-ui-tag: tông theo hệ MỚI ------------------------------------
303
+ `fx-ui-tag` là tag DÙNG CHUNG (priority, status, category…), không phải
304
+ badge severity đứng một mình — nên nền mềm theo THANG ĐO (`--og-sev-*` pha
305
+ loãng), KHÔNG dùng pill đen đặc của `.tone-tint-critical`. Trước đây trỏ
306
+ vào bộ LEGACY vivid (`bg-critical/20` → hồng #fe0453) nên mọi badge trong
307
+ list ở mọi tab vẫn ra màu cũ (anh Tú báo 03/09). */
302
308
  .txt-tag-success {
303
- @apply txt-tag-common text-success;
309
+ @apply txt-tag-common;
310
+ color: rgb(var(--og-positive));
304
311
  }
305
312
  .txt-tag-warning {
306
- @apply txt-tag-common text-warning;
313
+ @apply txt-tag-common;
314
+ color: rgb(var(--og-warning));
307
315
  }
308
316
  .txt-tag-critical {
309
- @apply txt-tag-common text-critical;
317
+ @apply txt-tag-common;
318
+ color: rgb(var(--og-sev-critical));
310
319
  }
311
320
  .txt-tag-danger {
312
- @apply txt-tag-common text-danger;
321
+ @apply txt-tag-common;
322
+ color: rgb(var(--og-sev-danger));
313
323
  }
314
324
  .txt-tag-information {
315
- @apply txt-tag-common text-information;
325
+ @apply txt-tag-common;
326
+ color: rgb(var(--og-neutral-soft-fg));
316
327
  }
317
328
  .txt-tag-discovery {
318
- @apply txt-tag-common text-discovery;
329
+ @apply txt-tag-common;
330
+ color: rgb(var(--og-accent-soft-fg));
319
331
  }
320
332
  .txt-link {
321
333
  @apply font-semibold text-text-link text-sm tracking-normal;
@@ -327,17 +339,302 @@ input[type='password'] {
327
339
  @apply font-mono text-sm text-text-primary tracking-normal;
328
340
  }
329
341
 
342
+ /* ---- Nút: viền hairline + bóng mờ ở MỌI biến thể (anh Tú 04/09) --------
343
+ Demo `.og-button`: border `--og-rule`, hover `0 2px 7px rgba(0,0,0,.12)`;
344
+ `.bi-newbtn`: viền tối hơn nền 1 bậc + `0 1px 2px rgba(0,0,0,.15)`. Nút nền
345
+ đặc dùng viền inset tối 8% để không phải khai màu viền riêng cho gradient. */
330
346
  .btn-default {
331
347
  @apply btn-common bg-button-default hover:opacity-75 text-text-inverse;
348
+ box-shadow:
349
+ inset 0 0 0 1px rgb(0 0 0 / 0.08),
350
+ 0 1px 2px rgb(0 0 0 / 0.12);
332
351
  }
333
352
  .btn-primary {
334
353
  @apply btn-common bg-gradient-primary hover:opacity-75 text-text-focus;
354
+ box-shadow:
355
+ inset 0 0 0 1px rgb(0 0 0 / 0.08),
356
+ 0 1px 2px rgb(0 0 0 / 0.12);
335
357
  }
336
358
  .btn-success {
337
359
  @apply btn-common bg-gradient-success hover:opacity-75 text-surface;
360
+ box-shadow:
361
+ inset 0 0 0 1px rgb(0 0 0 / 0.08),
362
+ 0 1px 2px rgb(0 0 0 / 0.12);
363
+ }
364
+ /* Nút hành động chính theo demo (`.bi-newbtn`): màu cố định, không phải accent. */
365
+ /* ---- Tông severity theo ĐÚNG mô hình demo (đo 03/09) -----------------
366
+ Demo KHÔNG tô badge bằng màu rực. Nó dùng token semantic, và bậc nặng
367
+ nhất được nhấn bằng ĐỘ ĐẬM chứ không bằng màu:
368
+ Critical → nền --og-text (#26282c), chữ --og-surface (pill đen đặc)
369
+ High → nền --og-negative-soft-bg, chữ --og-negative
370
+ Medium → nền --og-warning-soft-bg, chữ --og-warning
371
+ Low → nền --og-positive-soft-bg, chữ --og-positive
372
+ Info → nền --og-neutral-soft-bg, chữ --og-neutral-soft-fg
373
+ Neutral → nền --og-surface-sunken, chữ --og-text-muted
374
+
375
+ Dùng class thay vì utility Tailwind vì tên `warning`/`critical` trong theme
376
+ Tailwind đang trỏ vào bộ LEGACY `--og-tone-*`, không phải token semantic —
377
+ gọi `text-warning` ở đây sẽ lấy sai màu.
378
+
379
+ MÀU (`tone-tint-*`) tách khỏi HÌNH DẠNG (`tone-badge`) có chủ đích: ô icon
380
+ vuông của stat-card và chip filter cần đúng màu nhưng KHÔNG được nhận
381
+ `border-radius:999px` + padding của pill. */
382
+ /* ---- Tông THANG ĐO cho filter chip -----------------------------------
383
+ Filter chip severity là một THANG ĐO (giống chấm legend), không phải badge
384
+ của một finding — nên KHÔNG dùng `.tone-tint-critical` (pill đen đặc, dành
385
+ riêng cho badge Critical theo `.og-badge--solid`). Ở đây Critical phải đọc
386
+ ra ĐỎ SẪM: nền mềm pha từ ramp `--og-sev-*`, chữ lấy bản dễ đọc nhất.
387
+ (Demo không có mẫu để copy: filter chip của demo theo TRẠNG THÁI —
388
+ "Confirmed / Reachable / Blocked" — chứ không theo severity.) */
389
+ .tone-scale-critical {
390
+ background: color-mix(in srgb, rgb(var(--og-sev-critical)) 12%, rgb(var(--og-surface)));
391
+ color: rgb(var(--og-sev-critical));
392
+ }
393
+ .tone-scale-danger {
394
+ background: color-mix(in srgb, rgb(var(--og-sev-danger)) 12%, rgb(var(--og-surface)));
395
+ color: rgb(var(--og-sev-danger));
396
+ }
397
+ .tone-scale-warning {
398
+ background: color-mix(in srgb, rgb(var(--og-sev-warning)) 16%, rgb(var(--og-surface)));
399
+ color: rgb(var(--og-warning));
400
+ }
401
+ .tone-scale-success {
402
+ background: color-mix(in srgb, rgb(var(--og-sev-success)) 12%, rgb(var(--og-surface)));
403
+ color: rgb(var(--og-positive));
404
+ }
405
+ .tone-scale-information {
406
+ background: rgb(var(--og-neutral-soft-bg));
407
+ color: rgb(var(--og-neutral-soft-fg));
408
+ }
409
+ .tone-scale-discovery {
410
+ background: rgb(var(--og-accent-soft-bg));
411
+ color: rgb(var(--og-accent-soft-fg));
412
+ }
413
+ .tone-scale-neutral {
414
+ background: rgb(var(--og-surface-sunken));
415
+ color: rgb(var(--og-text-muted));
416
+ }
417
+ /* ---- Nền ĐẶC theo tông ------------------------------------------------
418
+ Cho trạng thái ĐANG CHỌN (chip filter active) — nơi cần khối màu đậm kèm
419
+ chữ tương phản. Khác `.tone-tint-*`: cái đó là bộ badge severity theo demo
420
+ (Critical = đen), còn đây là màu của chính bậc severity. */
421
+ .tone-solid-critical {
422
+ background: rgb(var(--og-sev-critical));
423
+ color: rgb(var(--og-surface));
424
+ }
425
+ .tone-solid-danger {
426
+ background: rgb(var(--og-sev-danger));
427
+ color: rgb(var(--og-surface));
428
+ }
429
+ .tone-solid-warning {
430
+ background: rgb(var(--og-warning));
431
+ color: rgb(var(--og-surface));
432
+ }
433
+ .tone-solid-success {
434
+ background: rgb(var(--og-positive));
435
+ color: rgb(var(--og-surface));
436
+ }
437
+ .tone-solid-information {
438
+ background: rgb(var(--og-neutral-soft-fg));
439
+ color: rgb(var(--og-surface));
440
+ }
441
+ .tone-solid-discovery {
442
+ background: rgb(var(--og-accent));
443
+ color: rgb(var(--og-surface));
444
+ }
445
+ .tone-solid-neutral {
446
+ background: rgb(var(--og-text-faint));
447
+ color: rgb(var(--og-surface));
448
+ }
449
+
450
+ /* ---- Viền theo tông ---------------------------------------------------
451
+ Tách riêng vì `border-color` là thuộc tính khác: panel trạng thái (dropzone
452
+ upload) cần viền + nền + chữ cùng tông. `tone-hover-border-*` cho chỗ chỉ
453
+ đổi viền khi hover (KPI card) — modifier `hover:` của Tailwind không áp
454
+ được lên class CSS thật nên khai thẳng pseudo-class. */
455
+ .tone-border-critical {
456
+ border-color: rgb(var(--og-sev-critical));
457
+ }
458
+ .tone-border-danger {
459
+ border-color: rgb(var(--og-sev-danger));
338
460
  }
461
+ .tone-border-warning {
462
+ border-color: rgb(var(--og-warning));
463
+ }
464
+ .tone-border-success {
465
+ border-color: rgb(var(--og-positive));
466
+ }
467
+ .tone-border-information {
468
+ border-color: rgb(var(--og-neutral-soft-fg));
469
+ }
470
+ .tone-border-discovery {
471
+ border-color: rgb(var(--og-accent));
472
+ }
473
+ .tone-border-neutral {
474
+ border-color: rgb(var(--og-border));
475
+ }
476
+ .tone-hover-border-critical:hover {
477
+ border-color: rgb(var(--og-sev-critical));
478
+ }
479
+ .tone-hover-border-danger:hover {
480
+ border-color: rgb(var(--og-sev-danger));
481
+ }
482
+ .tone-hover-border-warning:hover {
483
+ border-color: rgb(var(--og-warning));
484
+ }
485
+ .tone-hover-border-success:hover {
486
+ border-color: rgb(var(--og-positive));
487
+ }
488
+ .tone-hover-border-information:hover {
489
+ border-color: rgb(var(--og-neutral-soft-fg));
490
+ }
491
+ .tone-hover-border-discovery:hover {
492
+ border-color: rgb(var(--og-accent));
493
+ }
494
+ .tone-hover-border-neutral:hover {
495
+ border-color: rgb(var(--og-border));
496
+ }
497
+
498
+ .tone-tint-critical {
499
+ background: rgb(var(--og-text));
500
+ color: rgb(var(--og-surface));
501
+ }
502
+ .tone-tint-danger {
503
+ background: rgb(var(--og-negative-soft-bg));
504
+ color: rgb(var(--og-negative));
505
+ }
506
+ .tone-tint-warning {
507
+ background: rgb(var(--og-warning-soft-bg));
508
+ color: rgb(var(--og-warning));
509
+ }
510
+ .tone-tint-success {
511
+ background: rgb(var(--og-positive-soft-bg));
512
+ color: rgb(var(--og-positive));
513
+ }
514
+ .tone-tint-information {
515
+ background: rgb(var(--og-neutral-soft-bg));
516
+ color: rgb(var(--og-neutral-soft-fg));
517
+ }
518
+ .tone-tint-discovery {
519
+ /* demo: `.og-badge--accent` */
520
+ background: rgb(var(--og-accent-soft-bg));
521
+ color: rgb(var(--og-accent-soft-fg));
522
+ }
523
+ .tone-tint-neutral {
524
+ background: rgb(var(--og-surface-sunken));
525
+ color: rgb(var(--og-text-muted));
526
+ }
527
+
528
+ /* ---- Chữ / chấm theo tông (tầng 3 của mô hình demo) -----------------
529
+ Demo tách RÕ ba tầng (nguồn: `sevMeta` + `.og-badge--*` + `.og-statusdot--*`):
530
+ ① chart → màu literal `--og-sev-*`
531
+ ② badge/pill → `.tone-tint-*` (token semantic, Critical = solid)
532
+ ③ chữ & chấm → màu SEMANTIC, không phải ramp chart
533
+
534
+ `.tone-fg-*` chọn thành viên DỄ ĐỌC NHẤT trong mỗi họ severity (contrast
535
+ trên surface trắng): critical #7f1d1d 10.0:1 · danger #dc2626 4.83:1 ·
536
+ warning --og-warning #a8741f 4.0:1 (ramp #f59e0b chỉ 2.15:1 → không dùng
537
+ cho chữ) · success --og-positive #3f7d55 4.4:1 (ramp #16a34a 3.30:1). */
538
+ .tone-fg-critical {
539
+ color: rgb(var(--og-sev-critical));
540
+ }
541
+ .tone-fg-danger {
542
+ color: rgb(var(--og-sev-danger));
543
+ }
544
+ .tone-fg-warning {
545
+ color: rgb(var(--og-warning));
546
+ }
547
+ .tone-fg-success {
548
+ color: rgb(var(--og-positive));
549
+ }
550
+ .tone-fg-information {
551
+ color: rgb(var(--og-neutral-soft-fg));
552
+ }
553
+ .tone-fg-discovery {
554
+ color: rgb(var(--og-accent-soft-fg));
555
+ }
556
+ .tone-fg-neutral {
557
+ color: rgb(var(--og-text-faint));
558
+ }
559
+
560
+ /* Chấm trạng thái trong HÀNG (`● Confirmed`) — demo dùng màu semantic. */
561
+ .tone-dot-critical {
562
+ background: rgb(var(--og-sev-critical));
563
+ }
564
+ .tone-dot-danger {
565
+ background: rgb(var(--og-negative));
566
+ }
567
+ .tone-dot-warning {
568
+ background: rgb(var(--og-warning));
569
+ }
570
+ .tone-dot-success {
571
+ background: rgb(var(--og-positive));
572
+ }
573
+ .tone-dot-information {
574
+ background: rgb(var(--og-neutral-soft-fg));
575
+ }
576
+ .tone-dot-discovery {
577
+ background: rgb(var(--og-accent));
578
+ }
579
+ .tone-dot-neutral {
580
+ background: rgb(var(--og-text-faint));
581
+ }
582
+
583
+ /* Chấm LEGEND của chart — phải TRÙNG màu cung nên dùng ramp `--og-sev-*`. */
584
+ .tone-dot-chart-critical {
585
+ background: rgb(var(--og-sev-critical));
586
+ }
587
+ .tone-dot-chart-danger {
588
+ background: rgb(var(--og-sev-danger));
589
+ }
590
+ .tone-dot-chart-warning {
591
+ background: rgb(var(--og-sev-warning));
592
+ }
593
+ .tone-dot-chart-success {
594
+ background: rgb(var(--og-sev-success));
595
+ }
596
+ .tone-dot-chart-information {
597
+ background: rgb(var(--og-neutral-soft-fg));
598
+ }
599
+ .tone-dot-chart-discovery {
600
+ background: rgb(var(--og-accent));
601
+ }
602
+ .tone-dot-chart-neutral {
603
+ background: rgb(var(--og-text-faint));
604
+ }
605
+
606
+ /* Hình dạng pill của badge severity — demo: 999px · 11.5px · weight 400. */
607
+ .tone-badge {
608
+ display: inline-flex;
609
+ align-items: center;
610
+ border-radius: var(--og-radius-pill);
611
+ gap: 5px;
612
+ padding: 3px 10px;
613
+ font-size: var(--og-fs-xs);
614
+ font-weight: 400;
615
+ line-height: 1.5;
616
+ white-space: nowrap;
617
+ }
618
+
619
+ .btn-cta {
620
+ @apply btn-common bg-cta hover:bg-cta-hover text-white border border-cta-hover;
621
+ box-shadow:
622
+ inset 0 1px 0 rgb(255 255 255 / 0.18),
623
+ 0 1px 2px rgb(0 0 0 / 0.15);
624
+ }
625
+ .btn-cta:hover {
626
+ box-shadow:
627
+ inset 0 1px 0 rgb(255 255 255 / 0.18),
628
+ 0 2px 6px rgb(0 0 0 / 0.2);
629
+ }
630
+ /* Outline = `.og-button--outline`: nền surface, viền --og-border, bóng 1px;
631
+ hover nổi lên 0 2px 7px như demo. */
339
632
  .btn-alternative {
340
- @apply btn-common hover:bg-bg-hover text-text-primary border border-border-default;
633
+ @apply btn-common bg-surface hover:bg-bg-hover text-text-primary border border-border-default;
634
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.05);
635
+ }
636
+ .btn-alternative:hover {
637
+ box-shadow: 0 2px 7px rgb(0 0 0 / 0.12);
341
638
  }
342
639
  .btn-menu-top {
343
640
  @apply btn-menu-common border-b border-border-default pt-small pb-semi;
@@ -376,43 +673,55 @@ input[type='password'] {
376
673
  @apply tag-common bg-bg-hover;
377
674
  }
378
675
  .tag-success {
379
- @apply tag-common bg-success/20;
676
+ @apply tag-common;
677
+ background: color-mix(in srgb, rgb(var(--og-sev-success)) 12%, rgb(var(--og-surface)));
380
678
  }
381
679
  .tag-warning {
382
- @apply tag-common bg-warning/20;
680
+ @apply tag-common;
681
+ background: color-mix(in srgb, rgb(var(--og-sev-warning)) 16%, rgb(var(--og-surface)));
383
682
  }
384
683
  .tag-danger {
385
- @apply tag-common bg-danger/20;
684
+ @apply tag-common;
685
+ background: color-mix(in srgb, rgb(var(--og-sev-danger)) 12%, rgb(var(--og-surface)));
386
686
  }
387
687
  .tag-critical {
388
- @apply tag-common bg-critical/20;
688
+ @apply tag-common;
689
+ background: color-mix(in srgb, rgb(var(--og-sev-critical)) 12%, rgb(var(--og-surface)));
389
690
  }
390
691
  .tag-discovery {
391
- @apply tag-common bg-discovery/20;
692
+ @apply tag-common;
693
+ background: rgb(var(--og-accent-soft-bg));
392
694
  }
393
695
  .tag-information {
394
- @apply tag-common bg-information/20;
696
+ @apply tag-common;
697
+ background: rgb(var(--og-neutral-soft-bg));
395
698
  }
396
699
  .tag-default-solid {
397
700
  @apply tag-common bg-bg-hover;
398
701
  }
399
702
  .tag-success-solid {
400
- @apply tag-common bg-success;
703
+ @apply tag-common;
704
+ background: rgb(var(--og-positive));
401
705
  }
402
706
  .tag-warning-solid {
403
- @apply tag-common bg-warning;
707
+ @apply tag-common;
708
+ background: rgb(var(--og-warning));
404
709
  }
405
710
  .tag-danger-solid {
406
- @apply tag-common bg-danger;
711
+ @apply tag-common;
712
+ background: rgb(var(--og-sev-danger));
407
713
  }
408
714
  .tag-critical-solid {
409
- @apply tag-common bg-critical;
715
+ @apply tag-common;
716
+ background: rgb(var(--og-sev-critical));
410
717
  }
411
718
  .tag-discovery-solid {
412
- @apply tag-common bg-discovery;
719
+ @apply tag-common;
720
+ background: rgb(var(--og-accent));
413
721
  }
414
722
  .tag-information-solid {
415
- @apply tag-common bg-information;
723
+ @apply tag-common;
724
+ background: rgb(var(--og-neutral-soft-fg));
416
725
  }
417
726
  .tag-square {
418
727
  @apply rounded-md px-semi py-small;
@@ -484,3 +793,20 @@ input[type='password'] {
484
793
  .skeleton-load {
485
794
  @apply rounded bg-track animate-pulse motion-reduce:animate-none;
486
795
  }
796
+
797
+ /* ============================================================================
798
+ Route skeleton — ẩn nội dung CŨ trong lúc skeleton của route mới đang hiện.
799
+
800
+ ⚠️ PHẢI ĐỂ Ở CSS TOÀN CỤC, không đặt trong .scss của component.
801
+ Component đang active do ROUTER tạo, nên nó KHÔNG mang attribute
802
+ `_ngcontent-*` của template chứa `<router-outlet>`. View encapsulation
803
+ (emulated) thêm attribute đó vào selector con, nên rule viết trong
804
+ component .scss chỉ ẩn được `<router-outlet>` — chính component thật thì
805
+ không. Đã bị đúng bẫy này khi test standalone (`app-projects` vẫn hiện).
806
+
807
+ `<router-outlet>` và component active là hai node NGANG CẤP nên không bọc
808
+ được, phải loại trừ theo selector.
809
+ ============================================================================ */
810
+ .fx-rs-busy > *:not(fx-ui-route-skeleton) {
811
+ display: none !important;
812
+ }
@@ -1,6 +1,11 @@
1
- /* Self-hosted V2 fonts (Archivo / JetBrains Mono / IBM Plex Mono / Newsreader /
2
- Source Serif 4) — woff2 extracted from the V2 design bundle by
3
- tools/extract-og-tokens.mjs; re-run it to regenerate this file.
1
+ /* @font-face cho bộ font của design V2 (Familjen Grotesk, JetBrains Mono,
2
+ Archivo, IBM Plex Mono, Newsreader, Source Serif 4) — woff2 bóc từ bundle
3
+ design bởi tools/extract-og-tokens.mjs; chạy lại script để sinh lại file.
4
+
5
+ Familjen Grotesk là font THÂN + TIÊU ĐỀ thực tế của demo (lớp override đè
6
+ lên mọi theme); JetBrains Mono dùng cho mono + figure. Bốn họ còn lại là
7
+ mặc định của lớp base OgeniumDashboards, hiện KHÔNG theme nào trỏ tới —
8
+ giữ lại để đổi theme không phải bóc lại font.
4
9
 
5
10
  NOT imported by tailwind.css: v4 inlines imports and rebases font url()s
6
11
  against the entry file, breaking resolution in consumers. Consumers add
@@ -545,6 +550,102 @@
545
550
  src: url('source-serif-4/source-serif-4-600-latin.woff2') format('woff2');
546
551
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
547
552
  }
553
+ @font-face {
554
+ font-family: 'Familjen Grotesk';
555
+ font-style: normal;
556
+ font-weight: 400;
557
+ font-display: swap;
558
+ src: url('familjen-grotesk/familjen-grotesk-400-vietnamese.woff2') format('woff2');
559
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
560
+ }
561
+ @font-face {
562
+ font-family: 'Familjen Grotesk';
563
+ font-style: normal;
564
+ font-weight: 400;
565
+ font-display: swap;
566
+ src: url('familjen-grotesk/familjen-grotesk-400-latin-ext.woff2') format('woff2');
567
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
568
+ }
569
+ @font-face {
570
+ font-family: 'Familjen Grotesk';
571
+ font-style: normal;
572
+ font-weight: 400;
573
+ font-display: swap;
574
+ src: url('familjen-grotesk/familjen-grotesk-400-latin.woff2') format('woff2');
575
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
576
+ }
577
+ @font-face {
578
+ font-family: 'Familjen Grotesk';
579
+ font-style: normal;
580
+ font-weight: 500;
581
+ font-display: swap;
582
+ src: url('familjen-grotesk/familjen-grotesk-500-vietnamese.woff2') format('woff2');
583
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
584
+ }
585
+ @font-face {
586
+ font-family: 'Familjen Grotesk';
587
+ font-style: normal;
588
+ font-weight: 500;
589
+ font-display: swap;
590
+ src: url('familjen-grotesk/familjen-grotesk-500-latin-ext.woff2') format('woff2');
591
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
592
+ }
593
+ @font-face {
594
+ font-family: 'Familjen Grotesk';
595
+ font-style: normal;
596
+ font-weight: 500;
597
+ font-display: swap;
598
+ src: url('familjen-grotesk/familjen-grotesk-500-latin.woff2') format('woff2');
599
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
600
+ }
601
+ @font-face {
602
+ font-family: 'Familjen Grotesk';
603
+ font-style: normal;
604
+ font-weight: 600;
605
+ font-display: swap;
606
+ src: url('familjen-grotesk/familjen-grotesk-600-vietnamese.woff2') format('woff2');
607
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
608
+ }
609
+ @font-face {
610
+ font-family: 'Familjen Grotesk';
611
+ font-style: normal;
612
+ font-weight: 600;
613
+ font-display: swap;
614
+ src: url('familjen-grotesk/familjen-grotesk-600-latin-ext.woff2') format('woff2');
615
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
616
+ }
617
+ @font-face {
618
+ font-family: 'Familjen Grotesk';
619
+ font-style: normal;
620
+ font-weight: 600;
621
+ font-display: swap;
622
+ src: url('familjen-grotesk/familjen-grotesk-600-latin.woff2') format('woff2');
623
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
624
+ }
625
+ @font-face {
626
+ font-family: 'Familjen Grotesk';
627
+ font-style: normal;
628
+ font-weight: 700;
629
+ font-display: swap;
630
+ src: url('familjen-grotesk/familjen-grotesk-700-vietnamese.woff2') format('woff2');
631
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
632
+ }
633
+ @font-face {
634
+ font-family: 'Familjen Grotesk';
635
+ font-style: normal;
636
+ font-weight: 700;
637
+ font-display: swap;
638
+ src: url('familjen-grotesk/familjen-grotesk-700-latin-ext.woff2') format('woff2');
639
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
640
+ }
641
+ @font-face {
642
+ font-family: 'Familjen Grotesk';
643
+ font-style: normal;
644
+ font-weight: 700;
645
+ font-display: swap;
646
+ src: url('familjen-grotesk/familjen-grotesk-700-latin.woff2') format('woff2');
647
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
648
+ }
548
649
  @font-face {
549
650
  font-family: 'JetBrains Mono';
550
651
  font-style: normal;