@ojiepermana/angular 22.2.1 → 22.2.2

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 (125) hide show
  1. package/README.md +18 -7
  2. package/component/icon/README.md +40 -13
  3. package/fesm2022/ojiepermana-angular-chart-area.mjs +3 -3
  4. package/fesm2022/ojiepermana-angular-chart-bar.mjs +5 -5
  5. package/fesm2022/ojiepermana-angular-chart-core.mjs +33 -33
  6. package/fesm2022/ojiepermana-angular-chart-line.mjs +5 -5
  7. package/fesm2022/ojiepermana-angular-chart-pie.mjs +5 -5
  8. package/fesm2022/ojiepermana-angular-chart-primitives.mjs +33 -33
  9. package/fesm2022/ojiepermana-angular-chart-radar.mjs +5 -5
  10. package/fesm2022/ojiepermana-angular-chart-radial.mjs +5 -5
  11. package/fesm2022/ojiepermana-angular-chart-scatter.mjs +3 -3
  12. package/fesm2022/ojiepermana-angular-component-accordion.mjs +17 -17
  13. package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +27 -27
  14. package/fesm2022/ojiepermana-angular-component-alert.mjs +12 -12
  15. package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +3 -3
  16. package/fesm2022/ojiepermana-angular-component-avatar.mjs +18 -18
  17. package/fesm2022/ojiepermana-angular-component-badge.mjs +9 -9
  18. package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +21 -21
  19. package/fesm2022/ojiepermana-angular-component-button-group.mjs +10 -10
  20. package/fesm2022/ojiepermana-angular-component-button.mjs +4 -4
  21. package/fesm2022/ojiepermana-angular-component-calendar.mjs +5 -5
  22. package/fesm2022/ojiepermana-angular-component-card.mjs +27 -24
  23. package/fesm2022/ojiepermana-angular-component-carousel.mjs +18 -18
  24. package/fesm2022/ojiepermana-angular-component-checkbox.mjs +4 -4
  25. package/fesm2022/ojiepermana-angular-component-collapsible.mjs +12 -12
  26. package/fesm2022/ojiepermana-angular-component-combobox.mjs +3 -3
  27. package/fesm2022/ojiepermana-angular-component-command.mjs +28 -28
  28. package/fesm2022/ojiepermana-angular-component-composer.mjs +22 -22
  29. package/fesm2022/ojiepermana-angular-component-context-menu.mjs +3 -3
  30. package/fesm2022/ojiepermana-angular-component-date-picker.mjs +5 -5
  31. package/fesm2022/ojiepermana-angular-component-dialog.mjs +22 -22
  32. package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +39 -39
  33. package/fesm2022/ojiepermana-angular-component-editor.mjs +21 -21
  34. package/fesm2022/ojiepermana-angular-component-empty.mjs +19 -19
  35. package/fesm2022/ojiepermana-angular-component-form.mjs +36 -36
  36. package/fesm2022/ojiepermana-angular-component-hover-card.mjs +9 -9
  37. package/fesm2022/ojiepermana-angular-component-icon.mjs +114 -105
  38. package/fesm2022/ojiepermana-angular-component-input-group.mjs +19 -19
  39. package/fesm2022/ojiepermana-angular-component-input-otp.mjs +16 -16
  40. package/fesm2022/ojiepermana-angular-component-input.mjs +6 -6
  41. package/fesm2022/ojiepermana-angular-component-item.mjs +31 -31
  42. package/fesm2022/ojiepermana-angular-component-kanban.mjs +29 -29
  43. package/fesm2022/ojiepermana-angular-component-kbd.mjs +7 -7
  44. package/fesm2022/ojiepermana-angular-component-label.mjs +3 -3
  45. package/fesm2022/ojiepermana-angular-component-menubar.mjs +13 -13
  46. package/fesm2022/ojiepermana-angular-component-native-select.mjs +14 -14
  47. package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs +21 -21
  48. package/fesm2022/ojiepermana-angular-component-pagination.mjs +3 -3
  49. package/fesm2022/ojiepermana-angular-component-pillbox.mjs +16 -16
  50. package/fesm2022/ojiepermana-angular-component-popover.mjs +6 -6
  51. package/fesm2022/ojiepermana-angular-component-progress.mjs +3 -3
  52. package/fesm2022/ojiepermana-angular-component-radio.mjs +7 -7
  53. package/fesm2022/ojiepermana-angular-component-resizable.mjs +12 -12
  54. package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +3 -3
  55. package/fesm2022/ojiepermana-angular-component-segmented-control.mjs +6 -8
  56. package/fesm2022/ojiepermana-angular-component-select.mjs +8 -8
  57. package/fesm2022/ojiepermana-angular-component-separator.mjs +3 -3
  58. package/fesm2022/ojiepermana-angular-component-sheet.mjs +41 -28
  59. package/fesm2022/ojiepermana-angular-component-skeleton.mjs +3 -3
  60. package/fesm2022/ojiepermana-angular-component-slider.mjs +10 -10
  61. package/fesm2022/ojiepermana-angular-component-spinner.mjs +3 -3
  62. package/fesm2022/ojiepermana-angular-component-switch.mjs +4 -4
  63. package/fesm2022/ojiepermana-angular-component-table.mjs +24 -24
  64. package/fesm2022/ojiepermana-angular-component-tabs.mjs +18 -18
  65. package/fesm2022/ojiepermana-angular-component-textarea.mjs +6 -6
  66. package/fesm2022/ojiepermana-angular-component-timeline.mjs +19 -19
  67. package/fesm2022/ojiepermana-angular-component-toast.mjs +5 -5
  68. package/fesm2022/ojiepermana-angular-component-toggle-group.mjs +9 -9
  69. package/fesm2022/ojiepermana-angular-component-toggle.mjs +5 -5
  70. package/fesm2022/ojiepermana-angular-component-tooltip.mjs +15 -15
  71. package/fesm2022/ojiepermana-angular-component-utils-scale.mjs +3 -3
  72. package/fesm2022/ojiepermana-angular-navigation-icon.mjs +3 -3
  73. package/fesm2022/{ojiepermana-angular-navigation-navigation-badge-count.component-DYuysKVf.mjs → ojiepermana-angular-navigation-navigation-badge-count.component-4RIokl1C.mjs} +3 -3
  74. package/fesm2022/{ojiepermana-angular-navigation-navigation-dockbar-menu.component-COiTaw34.mjs → ojiepermana-angular-navigation-navigation-dockbar-menu.component-CGfu2V6f.mjs} +22 -15
  75. package/fesm2022/{ojiepermana-angular-navigation-navigation-entry-grid.component-YIz0IsIb.mjs → ojiepermana-angular-navigation-navigation-entry-grid.component-zBCXDa4c.mjs} +15 -24
  76. package/fesm2022/{ojiepermana-angular-navigation-navigation-flyout-menu.component-D2vwEwBY.mjs → ojiepermana-angular-navigation-navigation-flyout-menu.component-DYkYIIHd.mjs} +14 -13
  77. package/fesm2022/{ojiepermana-angular-navigation-navigation-horizontal.component-B5D7yXfb.mjs → ojiepermana-angular-navigation-navigation-horizontal.component-LNTertHr.mjs} +5 -5
  78. package/fesm2022/{ojiepermana-angular-navigation-navigation-item.component-CUb68MZH.mjs → ojiepermana-angular-navigation-navigation-item.component-DiGOcizS.mjs} +21 -22
  79. package/fesm2022/{ojiepermana-angular-navigation-navigation-list.component-CrK16trj.mjs → ojiepermana-angular-navigation-navigation-list.component-DB22Gl-5.mjs} +4 -4
  80. package/fesm2022/ojiepermana-angular-navigation-service.mjs +13 -5
  81. package/fesm2022/ojiepermana-angular-navigation.mjs +94 -87
  82. package/fesm2022/ojiepermana-angular-theme-component-settings.mjs +127 -72
  83. package/fesm2022/ojiepermana-angular-theme-layout-services.mjs +6 -6
  84. package/fesm2022/ojiepermana-angular-theme-layout-wrapper.mjs +56 -48
  85. package/fesm2022/ojiepermana-angular-theme-layout.mjs +30 -30
  86. package/fesm2022/ojiepermana-angular-theme-page-core.mjs +3 -3
  87. package/fesm2022/ojiepermana-angular-theme-page-filter.mjs +7 -7
  88. package/fesm2022/ojiepermana-angular-theme-page-overview.mjs +3 -3
  89. package/fesm2022/ojiepermana-angular-theme-page-root.mjs +8 -6
  90. package/fesm2022/ojiepermana-angular-theme-page-side.mjs +7 -7
  91. package/fesm2022/ojiepermana-angular-theme-page-slots.mjs +12 -12
  92. package/fesm2022/ojiepermana-angular-theme-shell-services.mjs +3 -3
  93. package/fesm2022/ojiepermana-angular-theme-shell.mjs +16 -12
  94. package/fesm2022/ojiepermana-angular-theme-styles.mjs +17 -33
  95. package/navigation/README.md +2 -2
  96. package/package.json +3 -2
  97. package/theme/README.md +4 -10
  98. package/theme/page/README.md +2 -0
  99. package/theme/shell/README.md +5 -1
  100. package/theme/styles/README.md +32 -8
  101. package/theme/styles/css/base/components.css +13 -9
  102. package/theme/styles/css/base/tailwind.css +43 -0
  103. package/theme/styles/css/base/theme.css +4 -25
  104. package/theme/styles/css/base/tokens.css +21 -0
  105. package/theme/styles/css/fonts.css +85 -0
  106. package/types/ojiepermana-angular-chart-radar.d.ts +2 -1
  107. package/types/ojiepermana-angular-component-avatar.d.ts +2 -1
  108. package/types/ojiepermana-angular-component-calendar.d.ts +2 -1
  109. package/types/ojiepermana-angular-component-card.d.ts +6 -1
  110. package/types/ojiepermana-angular-component-editor.d.ts +2 -1
  111. package/types/ojiepermana-angular-component-icon.d.ts +64 -79
  112. package/types/ojiepermana-angular-component-input-otp.d.ts +2 -1
  113. package/types/ojiepermana-angular-component-pillbox.d.ts +2 -1
  114. package/types/ojiepermana-angular-component-radio.d.ts +2 -1
  115. package/types/ojiepermana-angular-component-resizable.d.ts +2 -1
  116. package/types/ojiepermana-angular-component-segmented-control.d.ts +1 -1
  117. package/types/ojiepermana-angular-component-select.d.ts +2 -1
  118. package/types/ojiepermana-angular-navigation-service.d.ts +8 -0
  119. package/types/ojiepermana-angular-navigation-types.d.ts +1 -1
  120. package/types/ojiepermana-angular-navigation.d.ts +12 -5
  121. package/types/ojiepermana-angular-theme-layout-services.d.ts +2 -1
  122. package/types/ojiepermana-angular-theme-layout-wrapper.d.ts +4 -3
  123. package/types/ojiepermana-angular-theme-page-root.d.ts +1 -1
  124. package/types/ojiepermana-angular-theme-shell-services.d.ts +2 -1
  125. package/types/ojiepermana-angular-theme-styles.d.ts +0 -12
@@ -64,7 +64,7 @@ Type dipilih lewat komponen anak deklaratif (`NavigationSidebar`, `NavigationDoc
64
64
  `NavigationItem.badge.source` or route `data.navigationBadge`.
65
65
  - `ariaLabel` overrides the host navigation label and defaults to `Primary navigation`.
66
66
  - `label` on `NavigationFlyout` sets the single trigger label and defaults to `Menu`.
67
- - `icon` on `NavigationFlyout` renders a Material Symbols icon on the trigger; `icon-position` (`start`/`end`) sets its placement and `icon-only` hides the label visually while keeping it as the accessible name (compact mode is always icon-only and falls back to the `menu` icon).
67
+ - `icon` on `NavigationFlyout` renders a Lucide icon on the trigger (the name must be registered with `provideIcons()`); `icon-position` (`start`/`end`) sets its placement and `icon-only` hides the label visually while keeping it as the accessible name (compact mode is always icon-only and falls back to the `menu` icon).
68
68
  - `nav-position` on `NavigationNavbar`/`NavigationFlyout` accepts `top` (default) or `bottom`. With `bottom`, the navbar grid panel opens upward with squared bottom corners, and the flyout panel anchors to the container's bottom edge with the tab row docked at the bottom — seamless for bars placed below the content.
69
69
  - `nav-appearance` on `NavigationNavbar`/`NavigationFlyout` accepts `flat` or `border-rail` (same vocabulary as the layout's `layout-appearance`). `border-rail` squares the panel (no rounding), frames entry icons with dimmed blueprint-style rail lines plus dashed center cross lines (instead of rounded boxes), renders the nested-collapse rail dashed, and replaces hover/active backgrounds with primary-colored text. `flat` keeps the current visuals.
70
70
  - The `navbar` type renders root items inline immediately; clicking a group (hover does not open it) opens a full-width grid panel below the bar with the same flyout-style entries, responsive columns, and nested collapse. Root group triggers intentionally render without a chevron; open state remains available through the trigger styling and `aria-expanded`.
@@ -377,7 +377,7 @@ Any other navigation id, such as `erp-navbar`, only keeps its state in memory fo
377
377
 
378
378
  ## Icons
379
379
 
380
- Saat `item.icon` tersedia, `nav` akan merender Material Symbols secara default melalui `@ojiepermana/angular/component/icon`. Jalankan `provideUiTheme()` untuk preload global, atau biarkan `IconComponent` melakukan lazy-load saat icon pertama dipakai.
380
+ Saat `item.icon` tersedia, `nav` merender ikon Lucide secara default melalui `@ojiepermana/angular/component/icon`. `item.icon` adalah nama kebab Lucide (misalnya `layout-dashboard`) dan harus didaftarkan lewat `provideIcons()` di aplikasi; ikon tetap milik komponen navigasi (toggle header, chevron, tombol tutup dockbar, ikon rute fallback `route`) didaftarkan oleh komponennya sendiri. Nama yang tidak terdaftar tidak merender ikon dan menulis satu warning di dev mode.
381
381
 
382
382
  ```html
383
383
  <Navigation [data]="items">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ojiepermana/angular",
3
- "version": "22.2.1",
3
+ "version": "22.2.2",
4
4
  "description": "Accessible, tree-shakable Angular 22 design system from ojiepermana: UI components, charts, navigation shells, theme tokens, and an OpenAPI SDK generator.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -43,6 +43,7 @@
43
43
  "@angular/material": ">=22.2.0 <23.0.0",
44
44
  "@angular/platform-browser": ">=22.2.0 <23.0.0",
45
45
  "@angular/router": ">=22.2.0 <23.0.0",
46
+ "@lucide/angular": ">=1.61.0 <2.0.0",
46
47
  "rxjs": ">=7.8.0 <8.0.0"
47
48
  },
48
49
  "peerDependenciesMeta": {
@@ -502,5 +503,5 @@
502
503
  "module": "fesm2022/ojiepermana-angular.mjs",
503
504
  "typings": "types/ojiepermana-angular.d.ts",
504
505
  "type": "module",
505
- "gitHead": "1d9e13ff98804f60ee81765a22a6b5a814d0f374"
506
+ "gitHead": "915ddb2641dfdf3c3f256276f3c2eafd5ab31e64"
506
507
  }
package/theme/README.md CHANGED
@@ -107,17 +107,11 @@ every accent and neutral palette, so switching needs no runtime CSS loading. The
107
107
  lightweight `theme.css` ships base only — import the axis files you need on top of
108
108
  it (or use `theme-full.css`) to enable runtime switching.
109
109
 
110
- ### Material Symbols icons (opt-in)
110
+ ### Icons
111
111
 
112
- `provideUiTheme()` makes **no external network request by default**. To preload the
113
- Material Symbols font from Google Fonts:
114
-
115
- ```ts
116
- provideUiTheme({ icons: { materialSymbols: true } });
117
- ```
118
-
119
- Leave it off (the default) for privacy / offline / strict CSP, and self-host the
120
- font in your own stylesheet if you need it.
112
+ `provideUiTheme()` makes **no external network request** and loads no icon font. Icons are
113
+ Lucide SVGs registered with `provideIcons()` from `{{packageName}}/component/icon`; the theme
114
+ only supplies the `--icon-size` and `--icon-stroke-width` tokens that `<Icon>` reads.
121
115
 
122
116
  ## Support and security
123
117
 
@@ -76,6 +76,8 @@ Saat `Page` dirender di dalam layout `empty`, sebuah tombol icon-only tanpa bord
76
76
 
77
77
  Tombol hanya muncul ketika layout aktif `empty` dan nav target punya data. Di layout lain (`vertical`/`horizontal`/`fluid`) tombol tidak dirender karena navigasi utama sudah terlihat.
78
78
 
79
+ Saat `LayoutWrapperDefault` memakai `nav-type="desktop"` dan layout `empty`, data navigasi diterbitkan ke `main` untuk launcher `Page`, bukan ke titlebar `Shell`. Hanya launcher di kanan `PageHeader` (atau di atas konten bila tanpa header) yang tampil. Item mini dock dan navigasinya tetap tersedia.
80
+
79
81
  | Input | Type | Default | Keterangan |
80
82
  | -------------- | --------- | ------------------- | ------------------------------------------------------------ |
81
83
  | `appsLauncher` | `boolean` | `true` | Aktif/nonaktifkan apps-launcher pada layout `empty`. |
@@ -245,7 +245,11 @@ window-width popover:
245
245
  - Windows and Linux — before consumer `[shellBarStart]` content, at the far left
246
246
  opposite the window controls.
247
247
 
248
- The trigger is rendered only when desktop navigation data exists. It keeps a real
248
+ The trigger is rendered only when desktop navigation data exists. On an `empty`
249
+ layout, `LayoutWrapperDefault` publishes that data under `main` for the `Page`
250
+ launcher instead, so the titlebar does not add a duplicate button. Other layouts
251
+ keep their titlebar navigation, and minidock items remain independent.
252
+ The trigger keeps a real
249
253
  button, `aria-haspopup="menu"`, an explicit accessible name, visible keyboard
250
254
  focus, and the established semantic shell/navigation tokens. Its surface stays
251
255
  transparent, including hover and open states, while the icon inherits the exact
@@ -33,19 +33,43 @@ export const appConfig = {
33
33
  - `brand` mem-bootstrap `ThemeBrandService`; set `--brand` / utility `bg-brand` dan preset
34
34
  accent `theme-color='brand'`. Menerima string HSL triplet atau `{ color, foreground }`.
35
35
  Bisa diubah runtime via `setBrand()`, persist di storage.
36
- - `icons.materialSymbols` mengontrol preload stylesheet Material Symbols pada bootstrap aplikasi.
37
36
  - Pilihan yang dipersist (`theme-color` / `theme-neutral` / `theme-radius` / `theme-space` /
38
37
  `theme-brand`) menang atas default.
39
38
 
40
39
  ## Default Behavior
41
40
 
42
- - Material Symbols **tidak** di-preload secara default (opt-in). Tanpa konfigurasi,
43
- `provideUiTheme()` tidak melakukan request jaringan eksternal apa pun — ramah privasi,
44
- offline, dan CSP.
45
- - Untuk mengaktifkan preload font dari Google Fonts, set `icons.materialSymbols` ke `true`,
46
- atau self-host font Material Symbols dan `@import` sendiri di stylesheet Anda.
47
- - Jika memakai `@ojiepermana/angular/component/icon` tanpa provider tema, aktifkan
48
- `provideMaterialSymbols()` dari entrypoint icon atau self-host stylesheet font sendiri.
41
+ - `provideUiTheme()` tidak melakukan request jaringan eksternal apa pun dan tidak memuat font ikon:
42
+ ikon adalah SVG Lucide dari peer `@lucide/angular`, didaftarkan lewat `provideIcons()` dari
43
+ `@ojiepermana/angular/component/icon`. Tema hanya menyediakan token `--icon-size` (default `1.5rem`)
44
+ dan `--icon-stroke-width` (default `2`) yang dibaca komponen `<Icon>`.
45
+ - Opsi `icons` di `provideUiTheme()` (`icons.materialSymbols`) sudah dihapus bersama Material Symbols;
46
+ hapus opsi itu dari konfigurasi Anda.
47
+
48
+ ## Font
49
+
50
+ Token `--theme-font-sans` (`'Inter', …`) dan `--theme-font-mono` (`'JetBrains Mono', …`) hanya
51
+ menamai font; tema tidak memuat file font apa pun dan paket tidak membawa biner font. Tanpa langkah
52
+ di bawah, teks memakai font yang kebetulan terpasang di mesin pengunjung.
53
+
54
+ Untuk memuatnya, impor lembar opsional setelah tema dan sajikan file woff2 dari origin Anda sendiri:
55
+
56
+ ```css
57
+ @import '@ojiepermana/angular/theme/theme.css';
58
+ @import '@ojiepermana/angular/theme/styles/css/fonts.css';
59
+ ```
60
+
61
+ - `fonts.css` mendeklarasikan `@font-face` untuk `/fonts/inter/InterVariable.woff2` (dan varian
62
+ italic) serta `/fonts/jetbrains-mono/JetBrainsMono-{Regular,Medium,Bold}.woff2`, semuanya
63
+ `font-display: swap`. Ambil file dari rilis resmi [Inter](https://github.com/rsms/inter) dan
64
+ [JetBrains Mono](https://github.com/JetBrains/JetBrainsMono) (keduanya SIL OFL 1.1). Path lain?
65
+ Salin `fonts.css` dan ubah `url()`-nya.
66
+ - Lembar itu juga menambahkan face `Inter Fallback` (Arial lokal dengan metrik Inter) ke
67
+ `--theme-font-sans`, sehingga pergantian ke Inter tidak menggeser layout.
68
+ - Preload hanya file Inter tegak:
69
+ `<link rel="preload" href="/fonts/inter/InterVariable.woff2" as="font" type="font/woff2" crossorigin>`.
70
+ Browser hanya mengunduh face lain (italic, mono 500/700) saat halaman benar-benar memakainya.
71
+ - Showcase memakai persis susunan ini (`apps/showcase/public/fonts/`), dan CSP `font-src 'self'`
72
+ sudah mengizinkannya.
49
73
 
50
74
  ## Token Semantik Status
51
75
 
@@ -91,6 +91,19 @@
91
91
  }
92
92
  }
93
93
 
94
+ /* Vestibular safety for every component, including the ones without their
95
+ * own motion-reduce variant: motion collapses to an instant state change. */
96
+ @media (prefers-reduced-motion: reduce) {
97
+ *,
98
+ *::before,
99
+ *::after {
100
+ animation-duration: 0.01ms !important;
101
+ animation-iteration-count: 1 !important;
102
+ transition-duration: 0.01ms !important;
103
+ scroll-behavior: auto !important;
104
+ }
105
+ }
106
+
94
107
  @media (pointer: coarse) {
95
108
  :where(
96
109
  button,
@@ -107,15 +120,6 @@
107
120
  }
108
121
 
109
122
  @layer components {
110
- /* Icon family per appearance: Material Symbols Rounded is the default
111
- * (IconComponent's own fallback); any border-rail scope — the layout axis
112
- * (data-layout-appearance) or a navigation type-style (data-type-style) —
113
- * swaps the inherited token to Sharp so every <Icon> inside follows. */
114
- [data-layout-appearance='border-rail'],
115
- [data-type-style='border-rail'] {
116
- --icon-font-family: 'Material Symbols Sharp';
117
- }
118
-
119
123
  .nav-text {
120
124
  font-family: var(--nav-font-family, var(--font-sans));
121
125
  font-size: var(--nav-text-size, var(--text-base));
@@ -90,6 +90,30 @@
90
90
  --shadow-lg: var(--theme-shadow-lg);
91
91
  --shadow-xl: var(--theme-shadow-xl);
92
92
 
93
+ /* Motion. Every transition utility reads the motion tokens, so one
94
+ * --duration-base / --ease-base change retimes the whole kit. The Tailwind
95
+ * names differ from the token names on purpose: re-emitting a token under
96
+ * its own name would be a var() cycle (see the shadow note in tokens.css). */
97
+ --default-transition-duration: var(--duration-normal);
98
+ --default-transition-timing-function: var(--ease-standard);
99
+ --transition-duration-fast: var(--duration-fast);
100
+ --transition-duration-normal: var(--duration-normal);
101
+ --transition-duration-slow: var(--duration-slow);
102
+ --ease-in: var(--ease-exit);
103
+ --ease-out: var(--ease-enter);
104
+ --ease-in-out: var(--ease-standard);
105
+
106
+ /* Stacking layers: z-dropdown, z-sticky, z-overlay, z-modal, z-popover,
107
+ * z-tooltip, z-toast. Local z-10/z-20 inside a component's own stacking
108
+ * context stay allowed. */
109
+ --z-index-dropdown: var(--z-dropdown);
110
+ --z-index-sticky: var(--z-sticky);
111
+ --z-index-overlay: var(--z-overlay);
112
+ --z-index-modal: var(--z-modal);
113
+ --z-index-popover: var(--z-popover);
114
+ --z-index-tooltip: var(--z-tooltip);
115
+ --z-index-toast: var(--z-toast);
116
+
93
117
  --text-2xs: var(--font-size-2xs);
94
118
  --text-2xs--line-height: var(--line-height-2xs);
95
119
  --text-2xs-plus: var(--font-size-2xs-plus);
@@ -163,3 +187,22 @@
163
187
  @utility rounded-base {
164
188
  border-radius: var(--radius-base);
165
189
  }
190
+
191
+ /* One keyboard focus indicator for every interactive primitive: an outline,
192
+ * so it never moves layout and survives forced-colors mode. Mouse focus
193
+ * draws nothing (:focus-visible only). */
194
+ @utility focus-ring {
195
+ &:focus-visible {
196
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
197
+ outline-offset: var(--focus-ring-offset);
198
+ }
199
+ }
200
+
201
+ /* The same indicator on a composite's container while its inner input has
202
+ * keyboard focus (Command, Combobox). */
203
+ @utility focus-ring-within {
204
+ &:has(:focus-visible) {
205
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
206
+ outline-offset: var(--focus-ring-offset);
207
+ }
208
+ }
@@ -20,8 +20,6 @@
20
20
 
21
21
  --border-width-base: 1px;
22
22
 
23
- --shadow-base: 0 1px 2px hsl(0 0% 3.9% / 0.08);
24
-
25
23
  --duration-base: 200ms;
26
24
  --ease-base: cubic-bezier(0.4, 0, 0.2, 1);
27
25
 
@@ -85,19 +83,7 @@
85
83
  --border-width-medium: calc(var(--border-width-base) * 2);
86
84
  --border-width-thick: calc(var(--border-width-base) * 3);
87
85
 
88
- /* Concrete values live in the *-base primitives. The --shadow-* aliases are
89
- * re-emitted by the Tailwind @theme mapping as var(--theme-shadow-*) in the
90
- * later `theme` layer, so pointing either alias back at --shadow-* would
91
- * create a var() cycle that invalidates the whole chain. */
92
- --shadow-xs-base: 0 1px 1px hsl(0 0% 3.9% / 0.05);
93
- --shadow-md-base: 0 4px 6px hsl(0 0% 3.9% / 0.08);
94
- --shadow-lg-base: 0 10px 15px hsl(0 0% 3.9% / 0.1);
95
- --shadow-xl-base: 0 20px 25px hsl(0 0% 3.9% / 0.12);
96
- --shadow-xs: var(--shadow-xs-base);
97
- --shadow-sm: var(--shadow-base);
98
- --shadow-md: var(--shadow-md-base);
99
- --shadow-lg: var(--shadow-lg-base);
100
- --shadow-xl: var(--shadow-xl-base);
86
+ /* Shadow and elevation tokens are defined once, in tokens.css. */
101
87
 
102
88
  --duration-fast: calc(var(--duration-base) * 0.5);
103
89
  --duration-normal: var(--duration-base);
@@ -304,17 +290,10 @@
304
290
  --font-size-2xs-plus: 0.6875rem;
305
291
  --line-height-2xs-plus: 1rem;
306
292
 
307
- /* Deprecated: use the new primitive token names instead.
308
- * Must reference the *-base primitives, never --shadow-* — the Tailwind
309
- * @theme mapping re-emits --shadow-*: var(--theme-shadow-*), so an alias
310
- * back to --shadow-* forms a var() cycle. */
311
- --theme-shadow-xs: var(--shadow-xs-base);
293
+ /* Deprecated: use --border-width-base instead. The deprecated
294
+ * --theme-shadow-* aliases now live beside the shadow primitives in
295
+ * tokens.css. */
312
296
  --border-width: var(--border-width-base);
313
- --theme-shadow-sm: var(--shadow-base);
314
- --theme-shadow: var(--shadow-base);
315
- --theme-shadow-md: var(--shadow-md-base);
316
- --theme-shadow-lg: var(--shadow-lg-base);
317
- --theme-shadow-xl: var(--shadow-xl-base);
318
297
  }
319
298
 
320
299
  :root,
@@ -77,15 +77,18 @@
77
77
  * --shadow-* — the Tailwind @theme mapping re-emits
78
78
  * --shadow-*: var(--theme-shadow-*) in the later `theme` layer, which
79
79
  * turns that into a var() cycle and invalidates both tokens. */
80
+ --shadow-xs-base: 0 1px 1px hsl(0 0% 12% / 0.05);
80
81
  --shadow-base: 0 1px 0 0 hsl(0 0% 12% / 0.08);
81
82
  --shadow-md-base: 0 8px 18px -14px hsl(0 0% 12% / 0.24);
82
83
  --shadow-lg-base: 0 18px 36px -28px hsl(0 0% 12% / 0.34);
83
84
  --shadow-xl-base: 0 24px 48px -36px hsl(0 0% 12% / 0.4);
85
+ --shadow-xs: var(--shadow-xs-base);
84
86
  --shadow-sm: var(--shadow-base);
85
87
  --shadow-md: var(--shadow-md-base);
86
88
  --shadow-lg: var(--shadow-lg-base);
87
89
  --shadow-xl: var(--shadow-xl-base);
88
90
 
91
+ --theme-shadow-xs: var(--shadow-xs-base);
89
92
  --theme-shadow-sm: var(--shadow-base);
90
93
  --theme-shadow: 0 2px 4px -2px hsl(0 0% 12% / 0.14);
91
94
  --theme-shadow-md: var(--shadow-md-base);
@@ -93,6 +96,19 @@
93
96
  --theme-shadow-xl: var(--shadow-xl-base);
94
97
  --shadow: var(--theme-shadow);
95
98
 
99
+ /* Keyboard focus indicator, drawn by the focus-ring utility. */
100
+ --focus-ring-width: 2px;
101
+ --focus-ring-offset: 2px;
102
+ --focus-ring-color: hsl(var(--ring));
103
+
104
+ /* Elevation tiers. Resting surfaces (cards, fields, checkboxes, switches)
105
+ * sit at tier 0 and separate by a border; only floating layers carry a
106
+ * shadow: tier 1 for menus, popovers, select panels and tooltips, tier 2
107
+ * for dialogs, sheets, drawers and toasts. */
108
+ --elevation-0: none;
109
+ --elevation-1: var(--shadow-md-base);
110
+ --elevation-2: var(--shadow-lg-base);
111
+
96
112
  --theme-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
97
113
  --theme-font-mono: 'JetBrains Mono', 'Fira Code', monospace;
98
114
 
@@ -138,6 +154,11 @@
138
154
  * flyout/panel frames, entry borders, and tree rails. */
139
155
  --section-border-opacity: 0.666;
140
156
 
157
+ /* Icon geometry — read by the <Icon> primitive (Lucide SVG). The component's
158
+ * `size` and `strokeWidth` inputs only override these two tokens on the host. */
159
+ --icon-size: 1.5rem;
160
+ --icon-stroke-width: 2;
161
+
141
162
  /* Navigation geometry — ONE proportional system for every nav surface
142
163
  * (sidebar, dockbar, navbar, flyout, entry grid). Icons are naked glyphs;
143
164
  * derived tokens keep alignment exact: --nav-indent places a child's icon
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Optional font faces for the theme's type roles.
3
+ *
4
+ * The theme declares `--theme-font-sans: 'Inter', …` and
5
+ * `--theme-font-mono: 'JetBrains Mono', …` but never loads a font: the package
6
+ * ships no binaries and makes no network request. Import this file to load
7
+ * them from your own origin:
8
+ *
9
+ * @import '@ojiepermana/angular/theme/theme.css';
10
+ * @import '@ojiepermana/angular/theme/styles/css/fonts.css';
11
+ *
12
+ * and serve the files at these paths (both families are SIL OFL 1.1):
13
+ *
14
+ * /fonts/inter/InterVariable.woff2 Inter 4.x, github.com/rsms/inter
15
+ * /fonts/inter/InterVariable-Italic.woff2
16
+ * /fonts/jetbrains-mono/JetBrainsMono-Regular.woff2 JetBrains Mono 2.x,
17
+ * /fonts/jetbrains-mono/JetBrainsMono-Medium.woff2 github.com/JetBrains/JetBrainsMono
18
+ * /fonts/jetbrains-mono/JetBrainsMono-Bold.woff2
19
+ *
20
+ * Hosting them elsewhere? Copy this file and change the urls. Preload only the
21
+ * upright Inter file; the browser fetches the other faces only when a page
22
+ * renders that style or weight.
23
+ *
24
+ * `Inter Fallback` is local Arial with Inter's metrics, so swapping in Inter
25
+ * once it loads (`font-display: swap`) does not shift the layout.
26
+ */
27
+
28
+ @font-face {
29
+ font-family: 'Inter';
30
+ font-style: normal;
31
+ font-weight: 100 900;
32
+ font-display: swap;
33
+ src: url('/fonts/inter/InterVariable.woff2') format('woff2');
34
+ }
35
+
36
+ @font-face {
37
+ font-family: 'Inter';
38
+ font-style: italic;
39
+ font-weight: 100 900;
40
+ font-display: swap;
41
+ src: url('/fonts/inter/InterVariable-Italic.woff2') format('woff2');
42
+ }
43
+
44
+ @font-face {
45
+ font-family: 'Inter Fallback';
46
+ src: local('Arial'), local('ArialMT');
47
+ ascent-override: 90.49%;
48
+ descent-override: 22.56%;
49
+ line-gap-override: 0%;
50
+ size-adjust: 107.06%;
51
+ }
52
+
53
+ @font-face {
54
+ font-family: 'JetBrains Mono';
55
+ font-style: normal;
56
+ font-weight: 400;
57
+ font-display: swap;
58
+ src: url('/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2') format('woff2');
59
+ }
60
+
61
+ @font-face {
62
+ font-family: 'JetBrains Mono';
63
+ font-style: normal;
64
+ font-weight: 500;
65
+ font-display: swap;
66
+ src: url('/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2') format('woff2');
67
+ }
68
+
69
+ @font-face {
70
+ font-family: 'JetBrains Mono';
71
+ font-style: normal;
72
+ font-weight: 700;
73
+ font-display: swap;
74
+ src: url('/fonts/jetbrains-mono/JetBrainsMono-Bold.woff2') format('woff2');
75
+ }
76
+
77
+ /* The metric-matched fallback only makes sense once the faces are loaded, so
78
+ * it joins the stack here rather than in base/tokens.css. Same layer as the
79
+ * tokens, imported after them, so it wins without !important. */
80
+ @layer tokens {
81
+ :root {
82
+ --theme-font-sans:
83
+ 'Inter', 'Inter Fallback', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
84
+ }
85
+ }
@@ -194,4 +194,5 @@ export declare class RadarChart {
194
194
  }, {
195
195
  "pointClick": "pointClick";
196
196
  }, never, ["*", "ChartTooltip", "ChartLegend"], true, never>;
197
- }
197
+ }
198
+ export {};
@@ -107,4 +107,5 @@ export declare class AvatarGroupCountComponent {
107
107
  "isSignal": true;
108
108
  };
109
109
  }, {}, never, ["*"], true, never>;
110
- }
110
+ }
111
+ export {};
@@ -146,4 +146,5 @@ export declare class CalendarComponent implements ControlValueAccessor {
146
146
  }, {
147
147
  "value": "valueChange";
148
148
  }, never, never, true, never>;
149
- }
149
+ }
150
+ export {};
@@ -46,8 +46,13 @@ export declare class CardHeaderComponent {
46
46
  export declare class CardTitleComponent {
47
47
  private readonly card;
48
48
  private readonly element;
49
+ /** A static `aria-level` the author wrote; the host binding would otherwise overwrite it. */
50
+ private readonly staticAriaLevel;
49
51
  readonly class: import("@angular/core").InputSignal<string>;
50
- /** Heading level for custom `<CardTitle>` hosts; native h1-h6 infer their own level. */
52
+ /**
53
+ * Heading level for custom `<CardTitle>` hosts; native h1-h6 infer their own level.
54
+ * Unset, a static `aria-level` attribute is honoured, then the default of 3.
55
+ */
51
56
  readonly level: import("@angular/core").InputSignalWithTransform<number | null, string | number | null | undefined>;
52
57
  protected readonly resolvedLevel: import("@angular/core").Signal<number>;
53
58
  protected readonly classes: import("@angular/core").Signal<string>;
@@ -230,4 +230,5 @@ export type EditorHtmlSanitizer = (html: string) => string;
230
230
  * Override this token when an application needs a stricter allow-list than
231
231
  * Angular's platform HTML sanitizer.
232
232
  */
233
- export declare const EDITOR_HTML_SANITIZER: InjectionToken<EditorHtmlSanitizer>;
233
+ export declare const EDITOR_HTML_SANITIZER: InjectionToken<EditorHtmlSanitizer>;
234
+ export {};
@@ -1,20 +1,22 @@
1
+ import { LucideIcon, LucideIconData } from "@lucide/angular";
1
2
  import * as i0 from "@angular/core";
2
- import { EnvironmentProviders, InjectionToken } from "@angular/core";
3
+ import { InjectionToken, Provider } from "@angular/core";
3
4
  export declare class IconComponent {
4
- private readonly materialSymbols;
5
+ private readonly nearest;
6
+ private readonly registry;
7
+ /** Lucide kebab name, e.g. `layout-dashboard`. Must be registered with `provideIcons()`. */
5
8
  readonly name: import("@angular/core").InputSignal<string>;
6
9
  readonly class: import("@angular/core").InputSignal<string>;
7
- /** Glyph size in px, or 'inherit' to defer to the surrounding font-size
8
- * (lets a CSS token size the glyph — no inline style is emitted). */
10
+ /** Glyph size in px, or 'inherit' to follow the surrounding font-size (1em).
11
+ * Writes `--icon-size` on the host; leave unset to use the theme token. */
9
12
  readonly size: import("@angular/core").InputSignal<number | "inherit" | null>;
10
- readonly fill: import("@angular/core").InputSignal<number>;
11
- /** Light strokes + high-emphasis grade at the 48 optical grid — the
12
- * system's icon voice. Override per icon only with intent. */
13
- readonly weight: import("@angular/core").InputSignal<number>;
14
- readonly grade: import("@angular/core").InputSignal<number>;
15
- readonly opticalSize: import("@angular/core").InputSignal<number>;
16
- protected readonly resolvedSize: import("@angular/core").Signal<number | null>;
17
- protected readonly fontVariationSettings: import("@angular/core").Signal<string>;
13
+ /** Stroke width. Writes `--icon-stroke-width` on the host; unset uses the theme token. */
14
+ readonly strokeWidth: import("@angular/core").InputSignal<number | null>;
15
+ /** Accessible name for a functional icon. Without it the icon is decorative and hidden from assistive tech. */
16
+ readonly label: import("@angular/core").InputSignal<string | null>;
17
+ protected readonly icon: import("@angular/core").Signal<LucideIconData | undefined>;
18
+ protected readonly sizeToken: import("@angular/core").Signal<string | null>;
19
+ protected readonly strokeWidthToken: import("@angular/core").Signal<string | null>;
18
20
  protected readonly classes: import("@angular/core").Signal<string>;
19
21
  constructor();
20
22
  static ɵfac: i0.ɵɵFactoryDeclaration<IconComponent, never>;
@@ -34,86 +36,69 @@ export declare class IconComponent {
34
36
  "required": false;
35
37
  "isSignal": true;
36
38
  };
37
- "fill": {
38
- "alias": "fill";
39
+ "strokeWidth": {
40
+ "alias": "strokeWidth";
39
41
  "required": false;
40
42
  "isSignal": true;
41
43
  };
42
- "weight": {
43
- "alias": "weight";
44
- "required": false;
45
- "isSignal": true;
46
- };
47
- "grade": {
48
- "alias": "grade";
49
- "required": false;
50
- "isSignal": true;
51
- };
52
- "opticalSize": {
53
- "alias": "opticalSize";
44
+ "label": {
45
+ "alias": "label";
54
46
  "required": false;
55
47
  "isSignal": true;
56
48
  };
57
49
  }, {}, never, never, true, never>;
58
50
  }
59
- export declare const MATERIAL_SYMBOLS_FONT_ATTR = "data-icon-font";
60
- export declare const MATERIAL_SYMBOLS_FONT_ID = "material-symbols";
51
+ /** An icon as `provideIcons()` takes it: a Lucide component class or its raw data. */
52
+ type IconInput = LucideIcon | LucideIconData;
61
53
  /**
62
- * Loads BOTH Material Symbols variable families the design system uses:
63
- * Rounded (the default everywhere) and Sharp (swapped in under
64
- * `appearance='border-rail'` scopes via the `--icon-font-family` token).
65
- * Full variable axes are requested so per-icon `fill`/`weight`/`grade`/
66
- * `opticalSize` inputs keep working.
54
+ * Register the Lucide icons `<Icon name="...">` may render. Pass the Lucide
55
+ * components (`LucideHouse`) or their `LucideIconData`; only what you register
56
+ * ends up in your bundle.
57
+ *
58
+ * Works at the application root, in a route's `providers`, and in a
59
+ * component's `providers`. Every level adds to the levels above it and the
60
+ * nearest registration wins on a name clash. Keys are the kebab Lucide name
61
+ * plus its aliases.
62
+ *
63
+ * @example
64
+ * bootstrapApplication(App, { providers: [provideIcons(LucideHouse, LucideSettings)] });
67
65
  */
68
- export declare const MATERIAL_SYMBOLS_FONT_HREF = "https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&family=Material+Symbols+Sharp:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=block";
69
- /** Configuration for {@link provideMaterialSymbols}. */
70
- export interface MaterialSymbolsConfig {
71
- /**
72
- * Stylesheet URL to load the Material Symbols font from. Defaults to the
73
- * Google Fonts URL ({@link MATERIAL_SYMBOLS_FONT_HREF}). Point this at a
74
- * self-hosted stylesheet for offline / CSP / privacy-safe setups.
75
- */
76
- readonly href?: string;
77
- }
66
+ export declare function provideIcons(...icons: IconInput[]): Provider;
67
+ /** The icons visible from one injector level: own icons merged over every parent level. */
68
+ type IconSet = ReadonlyMap<string, LucideIconData>;
78
69
  /**
79
- * Presence of this token is what enables {@link IconComponent} to auto-load the
80
- * font. It is only provided when the app opts in via `provideMaterialSymbols()`;
81
- * without it, `<Icon>` makes no network request.
70
+ * What one `provideIcons()` call contributes at its own injector level. Several
71
+ * calls on the same level add up (multi), so `providers: [provideIcons(a), provideIcons(b)]`
72
+ * keeps both. Internal: not exported from the entry point.
82
73
  */
83
- export declare const MATERIAL_SYMBOLS_CONFIG: InjectionToken<MaterialSymbolsConfig>;
84
- export declare class MaterialSymbolsService {
85
- private readonly documentRef;
86
- private readonly platformId;
87
- private readonly config;
88
- /**
89
- * Token-gated load. No-op unless the app opted in via `provideMaterialSymbols()`
90
- * (which provides {@link MATERIAL_SYMBOLS_CONFIG}). Called by `IconComponent`
91
- * so rendering an icon never triggers an implicit network request on its own.
92
- */
93
- ensureLoaded(): void;
74
+ declare const UI_ICON_CONTRIBUTIONS: InjectionToken<readonly IconSet[]>;
75
+ /**
76
+ * The merged set visible at one injector level. Every `provideIcons()` call also
77
+ * provides this token with `own + parent`, so a child level never hides an icon
78
+ * registered above it (route, app root). Nearest wins on a name clash. Internal.
79
+ */
80
+ declare const UI_ICONS: InjectionToken<IconSet>;
81
+ /**
82
+ * Root view of the registered icons. The `<Icon>` component resolves from the set
83
+ * of its nearest injector first, then falls back here, so icons registered with
84
+ * `provideIcons()` at the application root are always found.
85
+ *
86
+ * Also feeds the catalog gallery (`names()`) and de-duplicates the dev mode
87
+ * warning for unregistered names.
88
+ */
89
+ export declare class IconRegistry {
90
+ private readonly rootSet;
91
+ private readonly warned;
92
+ /** Look up an icon by its Lucide kebab name or one of its aliases. */
93
+ resolve(name: string): LucideIconData | undefined;
94
+ /** Canonical Lucide names of every icon registered at the root, sorted. */
95
+ names(): string[];
94
96
  /**
95
- * Explicitly inject the font stylesheet, regardless of whether the config
96
- * token is present. Used by integrations that decide to load the font from
97
- * their own option surface (e.g. `provideUiTheme({ icons: { materialSymbols: true } })`).
97
+ * Dev mode only: one `console.warn` per unregistered name. Production logs nothing.
98
+ * @internal
98
99
  */
99
- load(config?: MaterialSymbolsConfig): void;
100
- static ɵfac: i0.ɵɵFactoryDeclaration<MaterialSymbolsService, never>;
100
+ ɵwarnUnregistered(name: string): void;
101
+ static ɵfac: i0.ɵɵFactoryDeclaration<IconRegistry, never>;
101
102
  static ɵprov: i0.ɵɵInjectableDeclaration<any>;
102
103
  }
103
- /**
104
- * Opt in to loading the Material Symbols icon font that `<Icon>` renders with.
105
- *
106
- * By default `IconComponent` makes **no** network request — better for privacy,
107
- * offline, and strict CSP. Add this provider to your bootstrap to fetch the font
108
- * stylesheet once at startup. Pass `{ href }` to load from a self-hosted source
109
- * instead of Google Fonts.
110
- *
111
- * @example
112
- * // Google Fonts (default source):
113
- * bootstrapApplication(App, { providers: [provideMaterialSymbols()] });
114
- *
115
- * @example
116
- * // Self-hosted / offline:
117
- * provideMaterialSymbols({ href: '/assets/material-symbols.css' });
118
- */
119
- export declare function provideMaterialSymbols(config?: MaterialSymbolsConfig): EnvironmentProviders;
104
+ export type { IconInput };