@ojiepermana/angular 22.1.11 → 22.1.12
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 +32 -26
- package/chart/README.md +272 -0
- package/component/README.md +89 -0
- package/component/accordion/README.md +195 -0
- package/component/alert/README.md +184 -0
- package/component/alert-dialog/README.md +255 -0
- package/component/aspect-ratio/README.md +116 -0
- package/component/avatar/README.md +182 -0
- package/component/badge/README.md +143 -0
- package/component/breadcrumb/README.md +223 -0
- package/component/button/README.md +141 -0
- package/component/button-group/README.md +214 -0
- package/component/calendar/README.md +137 -0
- package/component/card/README.md +232 -0
- package/component/carousel/README.md +276 -0
- package/component/checkbox/README.md +153 -0
- package/component/collapsible/README.md +216 -0
- package/component/combobox/README.md +202 -0
- package/component/command/README.md +287 -0
- package/component/composer/README.md +241 -0
- package/component/context-menu/README.md +268 -0
- package/component/date-picker/README.md +202 -0
- package/component/dialog/README.md +276 -0
- package/component/drawer/README.md +147 -0
- package/component/dropdown-menu/README.md +336 -0
- package/component/editor/README.md +152 -0
- package/component/empty/README.md +193 -0
- package/component/form/README.md +216 -0
- package/component/hover-card/README.md +150 -0
- package/component/icon/README.md +25 -0
- package/component/input/README.md +160 -0
- package/component/input-group/README.md +245 -0
- package/component/input-otp/README.md +331 -0
- package/component/item/README.md +254 -0
- package/component/kanban/README.md +81 -0
- package/component/kbd/README.md +141 -0
- package/component/label/README.md +137 -0
- package/component/menubar/README.md +270 -0
- package/component/native-select/README.md +176 -0
- package/component/navigation-menu/README.md +165 -0
- package/component/pagination/README.md +151 -0
- package/component/pillbox/README.md +68 -0
- package/component/popover/README.md +54 -0
- package/component/progress/README.md +160 -0
- package/component/radio/README.md +212 -0
- package/component/resizable/README.md +178 -0
- package/component/scroll-area/README.md +150 -0
- package/component/select/README.md +228 -0
- package/component/separator/README.md +175 -0
- package/component/sheet/README.md +190 -0
- package/component/skeleton/README.md +158 -0
- package/component/slider/README.md +207 -0
- package/component/spinner/README.md +160 -0
- package/component/switch/README.md +170 -0
- package/component/table/README.md +292 -0
- package/component/tabs/README.md +220 -0
- package/component/textarea/README.md +169 -0
- package/component/timeline/README.md +94 -0
- package/component/toast/README.md +173 -0
- package/component/toggle/README.md +133 -0
- package/component/toggle-group/README.md +234 -0
- package/component/tooltip/README.md +230 -0
- package/fesm2022/ojiepermana-angular-chart-area.mjs +289 -3
- package/fesm2022/ojiepermana-angular-chart-bar.mjs +722 -3
- package/fesm2022/ojiepermana-angular-chart-core.mjs +1482 -3
- package/fesm2022/ojiepermana-angular-chart-line.mjs +294 -3
- package/fesm2022/ojiepermana-angular-chart-pie.mjs +291 -3
- package/fesm2022/ojiepermana-angular-chart-primitives.mjs +1301 -3
- package/fesm2022/ojiepermana-angular-chart-radar.mjs +421 -3
- package/fesm2022/ojiepermana-angular-chart-radial.mjs +295 -3
- package/fesm2022/ojiepermana-angular-chart-scatter.mjs +306 -3
- package/fesm2022/ojiepermana-angular-chart.mjs +12 -3
- package/fesm2022/ojiepermana-angular-component-accordion.mjs +267 -3
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +359 -3
- package/fesm2022/ojiepermana-angular-component-alert.mjs +101 -3
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +31 -3
- package/fesm2022/ojiepermana-angular-component-avatar.mjs +139 -3
- package/fesm2022/ojiepermana-angular-component-badge.mjs +57 -3
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +192 -3
- package/fesm2022/ojiepermana-angular-component-button-group.mjs +95 -3
- package/fesm2022/ojiepermana-angular-component-button.mjs +73 -3
- package/fesm2022/ojiepermana-angular-component-calendar.mjs +718 -3
- package/fesm2022/ojiepermana-angular-component-card.mjs +167 -3
- package/fesm2022/ojiepermana-angular-component-carousel.mjs +326 -3
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs +190 -3
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs +117 -3
- package/fesm2022/ojiepermana-angular-component-combobox.mjs +327 -3
- package/fesm2022/ojiepermana-angular-component-command.mjs +351 -3
- package/fesm2022/ojiepermana-angular-component-composer.mjs +359 -3
- package/fesm2022/ojiepermana-angular-component-context-menu.mjs +158 -3
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs +482 -3
- package/fesm2022/ojiepermana-angular-component-dialog.mjs +400 -3
- package/fesm2022/ojiepermana-angular-component-drawer.mjs +1 -3
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +689 -3
- package/fesm2022/ojiepermana-angular-component-editor.mjs +511 -3
- package/fesm2022/ojiepermana-angular-component-empty.mjs +134 -3
- package/fesm2022/ojiepermana-angular-component-form.mjs +438 -3
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs +273 -3
- package/fesm2022/ojiepermana-angular-component-icon.mjs +139 -3
- package/fesm2022/ojiepermana-angular-component-input-group.mjs +176 -3
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs +550 -3
- package/fesm2022/ojiepermana-angular-component-input.mjs +52 -3
- package/fesm2022/ojiepermana-angular-component-item.mjs +264 -3
- package/fesm2022/ojiepermana-angular-component-kanban.mjs +310 -3
- package/fesm2022/ojiepermana-angular-component-kbd.mjs +48 -3
- package/fesm2022/ojiepermana-angular-component-label.mjs +27 -3
- package/fesm2022/ojiepermana-angular-component-menubar.mjs +327 -3
- package/fesm2022/ojiepermana-angular-component-native-select.mjs +62 -3
- package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs +378 -3
- package/fesm2022/ojiepermana-angular-component-pagination.mjs +238 -3
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs +999 -3
- package/fesm2022/ojiepermana-angular-component-popover.mjs +201 -3
- package/fesm2022/ojiepermana-angular-component-progress.mjs +57 -3
- package/fesm2022/ojiepermana-angular-component-radio.mjs +180 -3
- package/fesm2022/ojiepermana-angular-component-resizable.mjs +493 -3
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +51 -3
- package/fesm2022/ojiepermana-angular-component-segmented-control.mjs +227 -3
- package/fesm2022/ojiepermana-angular-component-select.mjs +609 -3
- package/fesm2022/ojiepermana-angular-component-separator.mjs +31 -3
- package/fesm2022/ojiepermana-angular-component-sheet.mjs +407 -3
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs +25 -3
- package/fesm2022/ojiepermana-angular-component-slider.mjs +456 -3
- package/fesm2022/ojiepermana-angular-component-spinner.mjs +56 -3
- package/fesm2022/ojiepermana-angular-component-switch.mjs +145 -3
- package/fesm2022/ojiepermana-angular-component-table.mjs +155 -3
- package/fesm2022/ojiepermana-angular-component-tabs.mjs +315 -3
- package/fesm2022/ojiepermana-angular-component-textarea.mjs +46 -3
- package/fesm2022/ojiepermana-angular-component-timeline.mjs +237 -3
- package/fesm2022/ojiepermana-angular-component-toast.mjs +241 -3
- package/fesm2022/ojiepermana-angular-component-toggle-group.mjs +290 -3
- package/fesm2022/ojiepermana-angular-component-toggle.mjs +76 -3
- package/fesm2022/ojiepermana-angular-component-tooltip.mjs +519 -3
- package/fesm2022/ojiepermana-angular-component-utils-core.mjs +310 -3
- package/fesm2022/ojiepermana-angular-component-utils-overlay.mjs +62 -3
- package/fesm2022/ojiepermana-angular-component-utils-scale.mjs +34 -3
- package/fesm2022/ojiepermana-angular-component-utils.mjs +3 -2
- package/fesm2022/ojiepermana-angular-component.mjs +4 -4
- package/fesm2022/ojiepermana-angular-navigation-core.mjs +21 -3
- package/fesm2022/ojiepermana-angular-navigation-icon.mjs +20 -3
- package/fesm2022/ojiepermana-angular-navigation-navigation-badge-count.component-CDBxQ8rk.mjs +91 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-DBQHdvGa.mjs +512 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-entry-grid.component-Cd-jh6tg.mjs +623 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-flyout-menu.component-BCGvhoYj.mjs +404 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-horizontal.component-Dk_03e-6.mjs +466 -0
- package/fesm2022/ojiepermana-angular-navigation-ojiepermana-angular-navigation-DNKU6viC.mjs +2642 -0
- package/fesm2022/ojiepermana-angular-navigation-service.mjs +725 -3
- package/fesm2022/ojiepermana-angular-navigation-types.mjs +65 -4
- package/fesm2022/ojiepermana-angular-navigation.mjs +4 -10
- package/fesm2022/ojiepermana-angular-sdk.mjs +204 -4
- package/fesm2022/ojiepermana-angular-theme-component-settings.mjs +1387 -3
- package/fesm2022/ojiepermana-angular-theme-layout-services.mjs +331 -3
- package/fesm2022/ojiepermana-angular-theme-layout-types.mjs +45 -4
- package/fesm2022/ojiepermana-angular-theme-layout-wrapper.mjs +1044 -3
- package/fesm2022/ojiepermana-angular-theme-layout.mjs +729 -3
- package/fesm2022/ojiepermana-angular-theme-page-core.mjs +381 -3
- package/fesm2022/ojiepermana-angular-theme-page-filter.mjs +267 -3
- package/fesm2022/ojiepermana-angular-theme-page-root.mjs +361 -3
- package/fesm2022/ojiepermana-angular-theme-page-side.mjs +198 -3
- package/fesm2022/ojiepermana-angular-theme-page-slots.mjs +110 -3
- package/fesm2022/ojiepermana-angular-theme-page.mjs +7 -2
- package/fesm2022/ojiepermana-angular-theme-shell-services.mjs +338 -3
- package/fesm2022/ojiepermana-angular-theme-shell-types.mjs +160 -4
- package/fesm2022/ojiepermana-angular-theme-shell.mjs +811 -3
- package/fesm2022/ojiepermana-angular-theme-styles.mjs +783 -3
- package/fesm2022/ojiepermana-angular-theme.mjs +4 -4
- package/fesm2022/ojiepermana-angular.mjs +6 -11
- package/navigation/README.md +374 -0
- package/package.json +81 -20
- package/sdk/README.md +212 -0
- package/sdk/schematics/collection.json +20 -0
- package/sdk/schematics/init/index.js +83 -0
- package/sdk/schematics/init/schema.json +19 -0
- package/sdk/schematics/ng-add/index.js +147 -0
- package/sdk/schematics/ng-add/schema.json +14 -0
- package/sdk/schematics/package.json +3 -0
- package/sdk/schematics/sdk/index.js +175 -0
- package/sdk/schematics/sdk/schema.json +19 -0
- package/sdk/sdk.config.example.json +24 -0
- package/sdk/src/config/loader.js +43 -0
- package/sdk/src/config/schema.js +59 -0
- package/sdk/src/emit/client.js +251 -0
- package/sdk/src/emit/metadata.js +310 -0
- package/sdk/src/emit/models.js +104 -0
- package/sdk/src/emit/navigation.js +56 -0
- package/sdk/src/emit/operations.js +118 -0
- package/sdk/src/emit/public-api.js +54 -0
- package/sdk/src/emit/services.js +110 -0
- package/sdk/src/engine.js +67 -0
- package/sdk/src/layout/per-domain.js +360 -0
- package/sdk/src/package.json +3 -0
- package/sdk/src/parser/bundle.js +25 -0
- package/sdk/src/parser/ir.js +429 -0
- package/sdk/src/parser/types.js +7 -0
- package/sdk/src/render/template.js +152 -0
- package/sdk/src/writer/index.js +286 -0
- package/theme/README.md +126 -0
- package/theme/component/settings/README.md +135 -0
- package/theme/layout/README.md +539 -0
- package/theme/page/README.md +453 -0
- package/theme/shell/README.md +340 -0
- package/theme/styles/README.md +178 -0
- package/theme/styles/css/base/components.css +553 -0
- package/theme/styles/css/base/index.css +14 -0
- package/theme/styles/css/base/package.css +9 -0
- package/theme/styles/css/base/prefers-dark.css +86 -0
- package/theme/styles/css/base/print.css +62 -0
- package/theme/styles/css/base/status.css +38 -0
- package/theme/styles/css/base/tailwind.css +165 -0
- package/theme/styles/css/base/theme.css +373 -0
- package/theme/styles/css/base/tokens.css +361 -0
- package/theme/styles/css/color/amber.css +72 -0
- package/theme/styles/css/color/blue.css +72 -0
- package/theme/styles/css/color/brand.css +38 -0
- package/theme/styles/css/color/cyan.css +72 -0
- package/theme/styles/css/color/emerald.css +72 -0
- package/theme/styles/css/color/fuchsia.css +72 -0
- package/theme/styles/css/color/green.css +72 -0
- package/theme/styles/css/color/index.css +19 -0
- package/theme/styles/css/color/indigo.css +72 -0
- package/theme/styles/css/color/lime.css +72 -0
- package/theme/styles/css/color/orange.css +72 -0
- package/theme/styles/css/color/pink.css +72 -0
- package/theme/styles/css/color/purple.css +72 -0
- package/theme/styles/css/color/red.css +72 -0
- package/theme/styles/css/color/rose.css +72 -0
- package/theme/styles/css/color/sky.css +72 -0
- package/theme/styles/css/color/teal.css +72 -0
- package/theme/styles/css/color/violet.css +72 -0
- package/theme/styles/css/color/yellow.css +72 -0
- package/theme/styles/css/index.css +18 -0
- package/theme/styles/css/neutral/gray.css +63 -0
- package/theme/styles/css/neutral/index.css +11 -0
- package/theme/styles/css/neutral/mauve.css +63 -0
- package/theme/styles/css/neutral/mist.css +63 -0
- package/theme/styles/css/neutral/neutral.css +63 -0
- package/theme/styles/css/neutral/olive.css +63 -0
- package/theme/styles/css/neutral/slate.css +63 -0
- package/theme/styles/css/neutral/stone.css +63 -0
- package/theme/styles/css/neutral/taupe.css +63 -0
- package/theme/styles/css/neutral/zinc.css +63 -0
- package/theme/styles/css/radius/index.css +30 -0
- package/theme/styles/css/space/index.css +20 -0
- package/types/ojiepermana-angular-chart-area.d.ts +76 -1
- package/types/ojiepermana-angular-chart-bar.d.ts +201 -1
- package/types/ojiepermana-angular-chart-core.d.ts +811 -1
- package/types/ojiepermana-angular-chart-line.d.ts +75 -1
- package/types/ojiepermana-angular-chart-pie.d.ts +131 -1
- package/types/ojiepermana-angular-chart-primitives.d.ts +291 -1
- package/types/ojiepermana-angular-chart-radar.d.ts +116 -1
- package/types/ojiepermana-angular-chart-radial.d.ts +116 -1
- package/types/ojiepermana-angular-chart-scatter.d.ts +125 -1
- package/types/ojiepermana-angular-chart.d.ts +2 -1
- package/types/ojiepermana-angular-component-accordion.d.ts +85 -1
- package/types/ojiepermana-angular-component-alert-dialog.d.ts +140 -1
- package/types/ojiepermana-angular-component-alert.d.ts +42 -1
- package/types/ojiepermana-angular-component-aspect-ratio.d.ts +12 -1
- package/types/ojiepermana-angular-component-avatar.d.ts +56 -1
- package/types/ojiepermana-angular-component-badge.d.ts +24 -1
- package/types/ojiepermana-angular-component-breadcrumb.d.ts +46 -1
- package/types/ojiepermana-angular-component-button-group.d.ts +26 -1
- package/types/ojiepermana-angular-component-button.d.ts +27 -1
- package/types/ojiepermana-angular-component-calendar.d.ts +111 -1
- package/types/ojiepermana-angular-component-card.d.ts +69 -1
- package/types/ojiepermana-angular-component-carousel.d.ts +86 -1
- package/types/ojiepermana-angular-component-checkbox.d.ts +42 -1
- package/types/ojiepermana-angular-component-collapsible.d.ts +42 -1
- package/types/ojiepermana-angular-component-combobox.d.ts +60 -1
- package/types/ojiepermana-angular-component-command.d.ts +130 -1
- package/types/ojiepermana-angular-component-composer.d.ts +90 -1
- package/types/ojiepermana-angular-component-context-menu.d.ts +54 -1
- package/types/ojiepermana-angular-component-date-picker.d.ts +109 -1
- package/types/ojiepermana-angular-component-dialog.d.ts +136 -1
- package/types/ojiepermana-angular-component-drawer.d.ts +1 -1
- package/types/ojiepermana-angular-component-dropdown-menu.d.ts +248 -1
- package/types/ojiepermana-angular-component-editor.d.ts +135 -1
- package/types/ojiepermana-angular-component-empty.d.ts +50 -1
- package/types/ojiepermana-angular-component-form.d.ts +159 -1
- package/types/ojiepermana-angular-component-hover-card.d.ts +75 -1
- package/types/ojiepermana-angular-component-icon.d.ts +89 -1
- package/types/ojiepermana-angular-component-input-group.d.ts +51 -1
- package/types/ojiepermana-angular-component-input-otp.d.ts +155 -1
- package/types/ojiepermana-angular-component-input.d.ts +21 -1
- package/types/ojiepermana-angular-component-item.d.ts +93 -1
- package/types/ojiepermana-angular-component-kanban.d.ts +70 -1
- package/types/ojiepermana-angular-component-kbd.d.ts +16 -1
- package/types/ojiepermana-angular-component-label.d.ts +11 -1
- package/types/ojiepermana-angular-component-menubar.d.ts +93 -1
- package/types/ojiepermana-angular-component-native-select.d.ts +26 -1
- package/types/ojiepermana-angular-component-navigation-menu.d.ts +104 -1
- package/types/ojiepermana-angular-component-pagination.d.ts +33 -1
- package/types/ojiepermana-angular-component-pillbox.d.ts +222 -1
- package/types/ojiepermana-angular-component-popover.d.ts +66 -1
- package/types/ojiepermana-angular-component-progress.d.ts +17 -1
- package/types/ojiepermana-angular-component-radio.d.ts +60 -1
- package/types/ojiepermana-angular-component-resizable.d.ts +117 -1
- package/types/ojiepermana-angular-component-scroll-area.d.ts +19 -1
- package/types/ojiepermana-angular-component-segmented-control.d.ts +58 -1
- package/types/ojiepermana-angular-component-select.d.ts +122 -1
- package/types/ojiepermana-angular-component-separator.d.ts +14 -1
- package/types/ojiepermana-angular-component-sheet.d.ts +130 -1
- package/types/ojiepermana-angular-component-skeleton.d.ts +10 -1
- package/types/ojiepermana-angular-component-slider.d.ts +83 -1
- package/types/ojiepermana-angular-component-spinner.d.ts +13 -1
- package/types/ojiepermana-angular-component-switch.d.ts +44 -1
- package/types/ojiepermana-angular-component-table.d.ts +57 -1
- package/types/ojiepermana-angular-component-tabs.d.ts +124 -1
- package/types/ojiepermana-angular-component-textarea.d.ts +17 -1
- package/types/ojiepermana-angular-component-timeline.d.ts +64 -1
- package/types/ojiepermana-angular-component-toast.d.ts +64 -1
- package/types/ojiepermana-angular-component-toggle-group.d.ts +90 -1
- package/types/ojiepermana-angular-component-toggle.d.ts +25 -1
- package/types/ojiepermana-angular-component-tooltip.d.ts +143 -1
- package/types/ojiepermana-angular-component-utils-core.d.ts +184 -1
- package/types/ojiepermana-angular-component-utils-overlay.d.ts +21 -1
- package/types/ojiepermana-angular-component-utils-scale.d.ts +19 -1
- package/types/ojiepermana-angular-component-utils.d.ts +2 -1
- package/types/ojiepermana-angular-component.d.ts +2 -1
- package/types/ojiepermana-angular-navigation-core.d.ts +59 -1
- package/types/ojiepermana-angular-navigation-icon.d.ts +12 -1
- package/types/ojiepermana-angular-navigation-service.d.ts +116 -1
- package/types/ojiepermana-angular-navigation-types.d.ts +144 -1
- package/types/ojiepermana-angular-navigation.d.ts +507 -1
- package/types/ojiepermana-angular-sdk.d.ts +89 -1
- package/types/ojiepermana-angular-theme-component-settings.d.ts +154 -1
- package/types/ojiepermana-angular-theme-layout-services.d.ts +147 -1
- package/types/ojiepermana-angular-theme-layout-types.d.ts +40 -1
- package/types/ojiepermana-angular-theme-layout-wrapper.d.ts +299 -1
- package/types/ojiepermana-angular-theme-layout.d.ts +129 -1
- package/types/ojiepermana-angular-theme-page-core.d.ts +157 -1
- package/types/ojiepermana-angular-theme-page-filter.d.ts +77 -1
- package/types/ojiepermana-angular-theme-page-root.d.ts +88 -1
- package/types/ojiepermana-angular-theme-page-side.d.ts +50 -1
- package/types/ojiepermana-angular-theme-page-slots.d.ts +40 -1
- package/types/ojiepermana-angular-theme-page.d.ts +5 -1
- package/types/ojiepermana-angular-theme-shell-services.d.ts +193 -1
- package/types/ojiepermana-angular-theme-shell-types.d.ts +105 -1
- package/types/ojiepermana-angular-theme-shell.d.ts +100 -1
- package/types/ojiepermana-angular-theme-styles.d.ts +319 -1
- package/types/ojiepermana-angular-theme.d.ts +2 -1
- package/types/ojiepermana-angular.d.ts +5 -5
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# @ojiepermana/angular/theme/shell
|
|
2
|
+
|
|
3
|
+
The application shell root. Where `@ojiepermana/angular/theme/layout` arranges an
|
|
4
|
+
app _inside_ a page, `shell` owns the whole surface — a browser tab, an **OS
|
|
5
|
+
window**, a tablet, a phone — and bridges it to Tauri (or Electron, or any other
|
|
6
|
+
desktop runtime).
|
|
7
|
+
|
|
8
|
+
A `<Shell>` holds the app directly, plus a titlebar it renders itself when there
|
|
9
|
+
is window chrome to draw:
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<Shell mode="desktop" device="macos" color="sync">
|
|
13
|
+
<button shellBarStart type="button">Menu</button>
|
|
14
|
+
<span shellBarTitle>Acme Studio</span>
|
|
15
|
+
<app-sync-badge shellBarEnd />
|
|
16
|
+
|
|
17
|
+
<Layout>
|
|
18
|
+
<LayoutVertical>
|
|
19
|
+
<LayoutNavigation><Navigation … /></LayoutNavigation>
|
|
20
|
+
<LayoutContent><router-outlet /></LayoutContent>
|
|
21
|
+
</LayoutVertical>
|
|
22
|
+
</Layout>
|
|
23
|
+
</Shell>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`<Layout>` is a direct child — there is no container element. Inside a shell it
|
|
27
|
+
grows into whatever the titlebar left over (`--layout-viewport-height` becomes
|
|
28
|
+
`auto` plus a zero flex-basis) instead of measuring the viewport and overflowing
|
|
29
|
+
by the height of the bar.
|
|
30
|
+
|
|
31
|
+
## Entry points
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import {
|
|
35
|
+
ShellComponent,
|
|
36
|
+
ShellService,
|
|
37
|
+
provideShellWindowBridge,
|
|
38
|
+
provideTauriShellWindowBridge,
|
|
39
|
+
isTauriRuntime,
|
|
40
|
+
type ShellMode,
|
|
41
|
+
type ShellDevice,
|
|
42
|
+
type ShellWindowBridge,
|
|
43
|
+
} from '@ojiepermana/angular/theme/shell';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The secondary entry points stay available for explicit imports:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { ShellService } from '@ojiepermana/angular/theme/shell/services';
|
|
50
|
+
import { SHELL_MODES, SHELL_MODE_DEVICES } from '@ojiepermana/angular/theme/shell/types';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`ShellBarComponent` is deliberately **not** exported. When a titlebar exists is
|
|
54
|
+
`<Shell>`'s decision, not the consumer's — configure it through `<Shell>` and its
|
|
55
|
+
slots.
|
|
56
|
+
|
|
57
|
+
## The four axes
|
|
58
|
+
|
|
59
|
+
| Input | Values | Storage key |
|
|
60
|
+
| -------- | ---------------------------------------------------------------- | -------------- |
|
|
61
|
+
| `mode` | `web`, `desktop`, `tablet`, `mobile` | `shell-mode` |
|
|
62
|
+
| `device` | `browser`, `macos`, `windows`, `linux`, `ipad`, `android`, `ios` | `shell-device` |
|
|
63
|
+
| `color` | `sync`, `default` | `shell-color` |
|
|
64
|
+
| `frame` | `classic`, `modern` | `shell-frame` |
|
|
65
|
+
|
|
66
|
+
Every axis is optional and falls back rather than demanding a value:
|
|
67
|
+
|
|
68
|
+
- **`mode`** — the input, else the stored `shell-mode`, else `desktop` when a
|
|
69
|
+
desktop runtime is registered (a Tauri window _is_ a desktop shell), else `web`.
|
|
70
|
+
- **`device`** — the input, else the stored `shell-device`, else whatever the
|
|
71
|
+
window bridge reports, else the mode's default device.
|
|
72
|
+
- **`color`** — the input, else the stored `shell-color`, else `sync`.
|
|
73
|
+
- **`frame`** — the input, else the stored `shell-frame`, else `classic`; only
|
|
74
|
+
`desktop` can be `modern`.
|
|
75
|
+
|
|
76
|
+
### Driving the axes from localStorage
|
|
77
|
+
|
|
78
|
+
Because all four are storage-backed, a delivery target can be flipped **without a
|
|
79
|
+
rebuild** — useful for trying a native look, or for reproducing a tablet report on
|
|
80
|
+
a desktop machine. Set the keys and reload:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
localStorage.setItem('shell-mode', 'desktop');
|
|
84
|
+
localStorage.setItem('shell-device', 'macos');
|
|
85
|
+
localStorage.setItem('shell-color', 'default');
|
|
86
|
+
localStorage.setItem('shell-frame', 'modern');
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
An unrecognized value is ignored rather than throwing, so a stale or hand-typo'd
|
|
90
|
+
key degrades to the axis default.
|
|
91
|
+
|
|
92
|
+
At runtime, go through `ShellService` — each setter writes its own key:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const shell = inject(ShellService);
|
|
96
|
+
|
|
97
|
+
shell.setMode('desktop'); // a user choice — persists to shell-mode
|
|
98
|
+
shell.setDevice('macos'); // …to shell-device
|
|
99
|
+
shell.setColor('default'); // …to shell-color
|
|
100
|
+
shell.setFrame('modern'); // …to shell-frame
|
|
101
|
+
|
|
102
|
+
shell.registerMode('desktop'); // a default — yields to a stored choice
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Pass `{ persist: false }` to any setter to change the axis for this session only.
|
|
106
|
+
|
|
107
|
+
Inputs, by contrast, are **host declarations, not user preferences**: they outrank
|
|
108
|
+
storage and never write to it, so `<Shell mode="desktop">` in a Tauri build cannot
|
|
109
|
+
be knocked over by a stale key — and pinning an input does not destroy the choice
|
|
110
|
+
underneath it.
|
|
111
|
+
|
|
112
|
+
### Modes and their devices
|
|
113
|
+
|
|
114
|
+
| Mode | Devices | Chrome |
|
|
115
|
+
| --------- | --------------------------- | -------------------------- |
|
|
116
|
+
| `web` | `browser` | none — the browser drew it |
|
|
117
|
+
| `desktop` | `macos`, `windows`, `linux` | its own titlebar |
|
|
118
|
+
| `tablet` | `ipad`, `android` | none |
|
|
119
|
+
| `mobile` | `ios`, `android` | none |
|
|
120
|
+
|
|
121
|
+
`android` deliberately belongs to both `tablet` and `mobile`: the same OS ships on
|
|
122
|
+
both form factors, and only `mode` can tell them apart.
|
|
123
|
+
|
|
124
|
+
`mode` and `device` are always resolved as a **coherent pair**. A device belonging
|
|
125
|
+
to another form factor (`mode="desktop" device="ipad"`) is corrected to the mode's
|
|
126
|
+
default, so `data-shell-device` never describes something the mode cannot be. The
|
|
127
|
+
stored preference itself is left alone, so a temporary `mode="web"` does not erase
|
|
128
|
+
a stored `macos`.
|
|
129
|
+
|
|
130
|
+
### Mode locks the layout width
|
|
131
|
+
|
|
132
|
+
`mode` is the one axis that constrains another package. On `desktop`, `tablet`, and
|
|
133
|
+
`mobile` the app _is_ the surface, so there is no page for the layout frame to be
|
|
134
|
+
inset within — `LayoutService.widthLocked()` becomes `true` and `width()` reports
|
|
135
|
+
`full`, outranking even `<Layout>`'s own `width` input.
|
|
136
|
+
|
|
137
|
+
The user's choice is not destroyed: it stays in `LayoutService.preferredWidth()` (and
|
|
138
|
+
in `localStorage`), so returning to `web` restores it. `ThemeSettings` reflects this
|
|
139
|
+
by disabling every width but `full` and explaining why.
|
|
140
|
+
|
|
141
|
+
Switch the axes at runtime through `ThemeSettings`
|
|
142
|
+
(`@ojiepermana/angular/theme/component/settings`), which exposes all four in a
|
|
143
|
+
**Shell** section directly below Theme.
|
|
144
|
+
|
|
145
|
+
## Chrome color
|
|
146
|
+
|
|
147
|
+
`color="sync"` (the default) promotes the selected **Theme color** to the shell
|
|
148
|
+
background through `--primary`. Its title and controls use `--primary-foreground`,
|
|
149
|
+
so named accents and the consumer's `brand` color keep the same readable semantic
|
|
150
|
+
pair in light and dark modes.
|
|
151
|
+
|
|
152
|
+
`color="default"` paints the native chrome of the active `device` instead — for an
|
|
153
|
+
app that wants to disappear into its OS. Each desktop device has a light and dark
|
|
154
|
+
palette, applied through `--shell-bar-background` / `-foreground` / `-border`
|
|
155
|
+
under `[data-shell-color='default'][data-shell-device='…']`.
|
|
156
|
+
|
|
157
|
+
## Frame
|
|
158
|
+
|
|
159
|
+
`frame` decides how the app sits inside its window. Only a desktop window has a
|
|
160
|
+
frame to shape, so this collapses to `classic` on every other mode.
|
|
161
|
+
|
|
162
|
+
**`classic`** (the default) is the traditional desktop arrangement: the app is flush
|
|
163
|
+
to the window edge, with a bordered titlebar stacked on top of it.
|
|
164
|
+
|
|
165
|
+
**`modern`** insets the app instead. It gains `mx-3 mb-3 mt-0`
|
|
166
|
+
(`--shell-frame-inset`, which rides `--spacing-base`), so it floats as a panel free
|
|
167
|
+
of the window on three sides and meets the titlebar on the fourth. The titlebar then
|
|
168
|
+
drops its bottom border and takes the **same background token as the surround**, so
|
|
169
|
+
the bar and the margin around the panel read as one continuous frame rather than two
|
|
170
|
+
stacked surfaces.
|
|
171
|
+
|
|
172
|
+
The inset is margin on the layout, never padding on `<Shell>`: the titlebar is the
|
|
173
|
+
shell's other flex child, and padding would indent it too, breaking the full-bleed
|
|
174
|
+
drag region and pulling the macOS traffic lights off the window corner. The margin
|
|
175
|
+
also releases the layout's width — while it kept `width: 100%` the margins only
|
|
176
|
+
_shifted_ a full-width panel, overflowing the trailing edge into the shell's
|
|
177
|
+
`overflow-hidden` and leaving a gap on the leading side alone.
|
|
178
|
+
|
|
179
|
+
### Panel edge
|
|
180
|
+
|
|
181
|
+
Inside a desktop window the app gets a panel edge: `modern` is rounded to
|
|
182
|
+
`--shell-frame-radius` (**12px**) and outlined 1px in `--shell-frame-border`, the
|
|
183
|
+
full-strength structural border tier.
|
|
184
|
+
|
|
185
|
+
`modern` rounds and closes all four sides, since it is a free-floating panel — its
|
|
186
|
+
titlebar drops the divider, so the panel's own top edge is what closes it. `classic`
|
|
187
|
+
keeps the entire layout square and flush to the window edge. Its top edge meets the
|
|
188
|
+
titlebar, which already draws that seam with its own `border-b`; outlining it again
|
|
189
|
+
would double the line, and rounding any edge would cut notches of chrome into the
|
|
190
|
+
full-bleed window join. No other mode has a window to shape.
|
|
191
|
+
|
|
192
|
+
The radius is a fixed 12px rather than riding `--radius-base`: this is window
|
|
193
|
+
geometry, matching what the OS rounds its own windows to, not a component-styling
|
|
194
|
+
choice the `theme-radius` axis should retune.
|
|
195
|
+
|
|
196
|
+
On `color="sync"`, the bar and surround both use the active `--primary`, while the
|
|
197
|
+
inset app panel remains on `--background`; the selected Theme color therefore makes
|
|
198
|
+
the frame visible without introducing another palette. `color="default"` keeps the
|
|
199
|
+
same composition but uses the active device's native chrome instead.
|
|
200
|
+
|
|
201
|
+
## Window buttons
|
|
202
|
+
|
|
203
|
+
Buttons follow the **device**, not the runtime:
|
|
204
|
+
|
|
205
|
+
| Device | Controls |
|
|
206
|
+
| ----------------- | ------------------------------------------------------------------------- |
|
|
207
|
+
| `macos` | traffic lights on the **leading** edge — close, minimize, zoom |
|
|
208
|
+
| `windows`/`linux` | square glyph buttons on the **trailing** edge — minimize, maximize, close |
|
|
209
|
+
|
|
210
|
+
The macOS lights are drawn by the shell, not the OS. That is deliberate: a
|
|
211
|
+
`decorations: false` window has no OS buttons at all, so without them the window
|
|
212
|
+
could not be closed. Their fills are macOS's own constants rather than theme tokens
|
|
213
|
+
— a traffic light that recoloured with the accent would stop reading as one — and
|
|
214
|
+
each is a real button with an `aria-label`, so colour is never the only way to tell
|
|
215
|
+
them apart.
|
|
216
|
+
|
|
217
|
+
They sit at `--shell-traffic-light-size` (~13.1px), a little over the 12px the OS
|
|
218
|
+
draws: the app's own lights are in a titlebar the user can mis-click, so they get a
|
|
219
|
+
slightly larger target. Each takes a `cursor: pointer`, and on hover it darkens to
|
|
220
|
+
its **own** pressed shade — the per-light shift macOS uses, so the feedback reads as
|
|
221
|
+
"this specific control" rather than a generic highlight. The glyph inside appears on
|
|
222
|
+
hover and on keyboard focus, so the hover state never rests on colour alone.
|
|
223
|
+
|
|
224
|
+
Set `nativeWindowControls` when the runtime still draws its own decorations — Tauri's
|
|
225
|
+
macOS `titleBarStyle: 'Overlay'`. macOS then reserves `--shell-traffic-light-inset`
|
|
226
|
+
on the leading edge for the OS lights and draws none of its own; Windows and Linux
|
|
227
|
+
draw nothing either.
|
|
228
|
+
|
|
229
|
+
Whether those buttons _do_ anything is the bridge's business; the shape of the
|
|
230
|
+
chrome is the device's. Every button carries an `aria-label` (`minimizeLabel`,
|
|
231
|
+
`maximizeLabel`, `restoreLabel`, `closeLabel` are `<Shell>` inputs, for
|
|
232
|
+
localization), and the maximize/zoom button reports `aria-pressed` so the maximized
|
|
233
|
+
state is announced.
|
|
234
|
+
|
|
235
|
+
## Desktop navigation flyout
|
|
236
|
+
|
|
237
|
+
When `LayoutWrapperDefault` uses `nav-type="desktop"`, it publishes its primary
|
|
238
|
+
navigation data to the titlebar instead of rendering a second navigation region
|
|
239
|
+
inside the layout. `<Shell>` then adds the same icon-only `NavigationFlyout`
|
|
240
|
+
pattern used by an empty-layout `<Page>`, adapted to desktop chrome as a
|
|
241
|
+
window-width popover:
|
|
242
|
+
|
|
243
|
+
- macOS — after consumer `[shellBarEnd]` content, at the far right opposite the
|
|
244
|
+
traffic lights;
|
|
245
|
+
- Windows and Linux — before consumer `[shellBarStart]` content, at the far left
|
|
246
|
+
opposite the window controls.
|
|
247
|
+
|
|
248
|
+
The trigger is rendered only when desktop navigation data exists. It keeps a real
|
|
249
|
+
button, `aria-haspopup="menu"`, an explicit accessible name, visible keyboard
|
|
250
|
+
focus, and the established semantic shell/navigation tokens. Its surface stays
|
|
251
|
+
transparent, including hover and open states, while the icon inherits the exact
|
|
252
|
+
`--shell-bar-foreground` color used by the titlebar text. The open panel is fixed
|
|
253
|
+
at the viewport origin (`top: 0; left: 0`) and spans the full window width instead
|
|
254
|
+
of using a fixed content cap. Its lower corners use the desktop-only
|
|
255
|
+
`--shell-frame-radius` while the upper edge stays square like the classic window
|
|
256
|
+
frame. Set
|
|
257
|
+
`[navigationFlyout]="false"` to suppress it, or localize/customize it through
|
|
258
|
+
`navigationFlyoutLabel` and `navigationFlyoutIcon`.
|
|
259
|
+
|
|
260
|
+
## Window dragging
|
|
261
|
+
|
|
262
|
+
Declarative, so nothing runs on pointer-down. In desktop mode `<Shell>` becomes
|
|
263
|
+
Electron's ancestor `shell-drag-region` (`-webkit-app-region: drag`) and paints a
|
|
264
|
+
transparent Tauri frame layer at `z-index: 0` with `data-tauri-drag-region`. The
|
|
265
|
+
titlebar also carries the direct Tauri marker; it deliberately does not create a
|
|
266
|
+
second Electron drag rectangle above the flyout.
|
|
267
|
+
|
|
268
|
+
Application foregrounds such as `<Layout>` sit at `z-index: 10` and opt out through
|
|
269
|
+
`app-region: no-drag`; the titlebar sits at `z-index: 20`, and its open flyout
|
|
270
|
+
panel stays at `z-index: 50` with an explicit no-drag region. Consequently the
|
|
271
|
+
exposed window surround—including the modern-frame gaps—moves the window, while
|
|
272
|
+
buttons, fields, routed content, and every flyout menu item keep normal pointer
|
|
273
|
+
interaction. Electron's ancestor drag region is suspended while the flyout is
|
|
274
|
+
mounted because native app-region hit testing is independent of ordinary DOM
|
|
275
|
+
stacking; it resumes automatically as soon as the panel closes.
|
|
276
|
+
|
|
277
|
+
Turn the bar into pure UI with `[drag]="false"`. Runtimes that need an explicit
|
|
278
|
+
call can use `ShellService.startDragging()`. Disabling drag removes both the
|
|
279
|
+
frame-wide layer and the titlebar markers.
|
|
280
|
+
|
|
281
|
+
## Bridging to Tauri
|
|
282
|
+
|
|
283
|
+
The theme package never imports a desktop SDK. Everything the shell needs from a
|
|
284
|
+
window sits behind `ShellWindowBridge`, so a browser build pays nothing.
|
|
285
|
+
|
|
286
|
+
For a Tauri app with `app.withGlobalTauri = true`:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
import { provideTauriShellWindowBridge } from '@ojiepermana/angular/theme/shell';
|
|
290
|
+
|
|
291
|
+
export const appConfig: ApplicationConfig = {
|
|
292
|
+
// The bridge reports the OS, so mode and device both resolve on their own.
|
|
293
|
+
providers: [provideTauriShellWindowBridge()],
|
|
294
|
+
};
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Without the global, hand it the modules instead — they stay dynamic imports, so a
|
|
298
|
+
web build never pulls them in:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
provideTauriShellWindowBridge({
|
|
302
|
+
currentWindow: () => import('@tauri-apps/api/window').then((m) => m.getCurrentWindow()),
|
|
303
|
+
platform: () => import('@tauri-apps/plugin-os').then((m) => m.platform()),
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
This is safe to register unconditionally: outside a Tauri webview the bridge
|
|
308
|
+
resolves no window and every command becomes a no-op. Use `isTauriRuntime()` if you
|
|
309
|
+
need to branch yourself.
|
|
310
|
+
|
|
311
|
+
For any other runtime, implement `ShellWindowBridge` and register it with
|
|
312
|
+
`provideShellWindowBridge()`. With no bridge at all, `ShellService` falls back to an
|
|
313
|
+
inert web bridge — which is what keeps `<Shell>` renderable in a browser tab, in a
|
|
314
|
+
unit test, and under SSR.
|
|
315
|
+
|
|
316
|
+
## Tokens
|
|
317
|
+
|
|
318
|
+
Geometry rides `--spacing-base`, so window chrome tracks the `theme-space` density
|
|
319
|
+
axis:
|
|
320
|
+
|
|
321
|
+
| Token | Purpose |
|
|
322
|
+
| ----------------------------- | ---------------------------------------------- |
|
|
323
|
+
| `--shell-bar-height` | Titlebar thickness — the single geometry knob. |
|
|
324
|
+
| `--shell-bar-inline` | Titlebar inline padding. |
|
|
325
|
+
| `--shell-bar-gap` | Gap between items in the titlebar. |
|
|
326
|
+
| `--shell-control-size` | Window button hit area. |
|
|
327
|
+
| `--shell-traffic-light-inset` | Leading run reserved for macOS traffic lights. |
|
|
328
|
+
| `--shell-traffic-light-size` | Diameter of one traffic light (~13.1px). |
|
|
329
|
+
| `--shell-bar-background` | Shell chrome background; see **Chrome color**. |
|
|
330
|
+
| `--shell-bar-foreground` | Titlebar text and glyphs. |
|
|
331
|
+
| `--shell-bar-border` | Titlebar bottom divider (`classic` only). |
|
|
332
|
+
| `--shell-frame-inset` | `modern`: gap between the app and the window. |
|
|
333
|
+
| `--shell-frame-radius` | Corner radius of the app in a desktop window. |
|
|
334
|
+
|
|
335
|
+
## Host attributes
|
|
336
|
+
|
|
337
|
+
`<Shell>` reflects its resolved state so consumer CSS and tests can key off it:
|
|
338
|
+
`data-shell-root`, `data-shell-mode`, `data-shell-device`, `data-shell-color`,
|
|
339
|
+
`data-shell-platform`, `data-shell-window`. The titlebar adds
|
|
340
|
+
`data-shell-focused` — an unfocused window recedes, per OS convention.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Theme
|
|
2
|
+
|
|
3
|
+
`@ojiepermana/angular/theme/styles` menyediakan provider untuk mode, warna (accent +
|
|
4
|
+
neutral, gaya FluxUI), brand color, dan preload aset tema global.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { provideUiTheme } from '@ojiepermana/angular/theme/styles';
|
|
8
|
+
|
|
9
|
+
export const appConfig = {
|
|
10
|
+
providers: [
|
|
11
|
+
provideUiTheme({
|
|
12
|
+
mode: 'light',
|
|
13
|
+
color: 'base', // accent: base, red…rose, brand
|
|
14
|
+
neutral: 'base', // gray family: base, slate, gray, zinc, …
|
|
15
|
+
radius: 'md', // corner radius: none, xs, sm, md, xl, full
|
|
16
|
+
space: 'normal', // spacing density: compact, normal
|
|
17
|
+
brand: '221 83% 53%', // warna brand consumer (HSL triplet, atau { color, foreground })
|
|
18
|
+
}),
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## API Notes
|
|
24
|
+
|
|
25
|
+
- `mode` mem-bootstrap `ThemeModeService` dan default mode yang disimpan di storage.
|
|
26
|
+
- `color` mem-bootstrap `ThemeColorService`; accent palette awal (`<html theme-color>`).
|
|
27
|
+
- `neutral` keluarga neutral awal (`<html theme-neutral>`); berkomposisi dengan accent.
|
|
28
|
+
- `radius` mem-bootstrap `ThemeRadiusService`; preset corner radius awal (`<html theme-radius>`).
|
|
29
|
+
Menggerakkan knob `--radius-base` sehingga seluruh skala `--radius-*` dan utility `rounded-*` ikut.
|
|
30
|
+
Nilainya mengikuti Tailwind: `none`, `xs`, `sm`, `md` (default), `xl`, dan `full`.
|
|
31
|
+
- `space` mem-bootstrap `ThemeSpaceService`; preset spacing density awal (`<html theme-space>`).
|
|
32
|
+
Menggerakkan knob `--spacing-base` sehingga setiap utility `p-*`/`m-*`/`gap-*`/`w-*`/`h-*` ikut.
|
|
33
|
+
- `brand` mem-bootstrap `ThemeBrandService`; set `--brand` / utility `bg-brand` dan preset
|
|
34
|
+
accent `theme-color='brand'`. Menerima string HSL triplet atau `{ color, foreground }`.
|
|
35
|
+
Bisa diubah runtime via `setBrand()`, persist di storage.
|
|
36
|
+
- `icons.materialSymbols` mengontrol preload stylesheet Material Symbols pada bootstrap aplikasi.
|
|
37
|
+
- Pilihan yang dipersist (`theme-color` / `theme-neutral` / `theme-radius` / `theme-space` /
|
|
38
|
+
`theme-brand`) menang atas default.
|
|
39
|
+
|
|
40
|
+
## Default Behavior
|
|
41
|
+
|
|
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.
|
|
49
|
+
|
|
50
|
+
## Token Semantik Status
|
|
51
|
+
|
|
52
|
+
Selain palet accent dan neutral, tema mengemit tiga token status semantik beserta
|
|
53
|
+
pasangan foregroundnya. Semuanya digenerate oleh `scripts/gen-theme-colors.mjs` ke
|
|
54
|
+
`css/base/status.css`, jadi jangan diedit tangan.
|
|
55
|
+
|
|
56
|
+
| Token | Pasangan foreground | Seed | Dipakai oleh |
|
|
57
|
+
| ----------- | ---------------------- | ----------- | ------------------------------------ |
|
|
58
|
+
| `--success` | `--success-foreground` | green (142) | `bg-success text-success-foreground` |
|
|
59
|
+
| `--warning` | `--warning-foreground` | amber (38) | `bg-warning text-warning-foreground` |
|
|
60
|
+
| `--info` | `--info-foreground` | sky (199) | `bg-info text-info-foreground` |
|
|
61
|
+
|
|
62
|
+
`--destructive` tetap ditulis tangan di `css/base/theme.css` karena tidak berasal
|
|
63
|
+
dari seed accent, tapi generator ikut memvalidasi kontrasnya.
|
|
64
|
+
|
|
65
|
+
## Jaminan Kontras
|
|
66
|
+
|
|
67
|
+
Generator mengukur kontras setiap pasangan token teks yang diemitnya dengan rumus
|
|
68
|
+
WCAG, bukan menebak dari rentang hue. Aturannya:
|
|
69
|
+
|
|
70
|
+
- pasangan teks (`primary`, `accent`, `secondary`, `muted`, `surface`, `card`,
|
|
71
|
+
`popover`, dan `foreground` terhadap `background`) wajib minimal 4.5 banding 1;
|
|
72
|
+
- `--ring` diemit terpisah dari `--primary` dan wajib minimal 3 banding 1 terhadap
|
|
73
|
+
`--background`, jadi indikator fokus selalu terlihat;
|
|
74
|
+
- foreground yang meleset digeser lightnessnya sekecil mungkin sampai lolos. Hue dan
|
|
75
|
+
saturation tidak pernah bergeser, sehingga identitas warna palet tetap;
|
|
76
|
+
- bila tidak ada nilai yang bisa lolos, generator gagal dan tidak menulis apa pun.
|
|
77
|
+
|
|
78
|
+
A brand the consumer picks at runtime is held to the same rules, and split the
|
|
79
|
+
same way. `ThemeBrandService.setBrand()` measures it with the same WCAG formula
|
|
80
|
+
(`theme-contrast.ts`, a port of `scripts/theme-contrast.mjs`) before storing it:
|
|
81
|
+
|
|
82
|
+
- `--brand` itself is never moved. Like a generated `--primary`, the fill keeps
|
|
83
|
+
the lightness it was chosen with, so `theme-color='brand'` ships the colour the
|
|
84
|
+
consumer asked for;
|
|
85
|
+
- `--brand-foreground` is moved until text on the brand clears 4.5 to 1;
|
|
86
|
+
- the focus ring is solved separately, once per mode, into `--brand-ring-light`
|
|
87
|
+
and `--brand-ring-dark`. `css/color/brand.css` maps `--ring` to
|
|
88
|
+
`var(--brand-ring, var(--brand))` and picks the right one per `data-mode`, so
|
|
89
|
+
the ring clears 3 to 1 against that mode's `--background` without dragging the
|
|
90
|
+
fill down with it.
|
|
91
|
+
|
|
92
|
+
Every HSL form CSS accepts is normalised first — space or comma separated, a hue
|
|
93
|
+
written with `deg`/`grad`/`rad`/`turn`, a hue outside 0..360 — so none of them can
|
|
94
|
+
paint while skipping the measurement. Hex is accepted and converted too, since
|
|
95
|
+
that is what a native colour picker returns. A value with nothing to measure in
|
|
96
|
+
it (`var(--consumer-brand)`, say) is passed through as given, and forfeits the
|
|
97
|
+
guarantee.
|
|
98
|
+
|
|
99
|
+
Jalankan `node scripts/gen-theme-colors.mjs --check` untuk memastikan CSS yang
|
|
100
|
+
ter commit masih cocok dengan seed. Cek ini juga berjalan di `verify:libs` dan CI.
|
|
101
|
+
|
|
102
|
+
## Standar Token Geometri
|
|
103
|
+
|
|
104
|
+
Setiap nilai geometri di `library/` wajib turun dari token tema, bukan angka mati.
|
|
105
|
+
Aturan ini yang membuat sumbu `theme-space` dan `theme-radius` benar benar
|
|
106
|
+
menjangkau seluruh library, bukan hanya komponen yang kebetulan memakai utility
|
|
107
|
+
Tailwind. Keputusan lengkapnya di
|
|
108
|
+
[spec 0003](../../../docs/specs/0003-jangkauan-sumbu-space-radius/index.md).
|
|
109
|
+
|
|
110
|
+
**Pola kanonik**:
|
|
111
|
+
|
|
112
|
+
```css
|
|
113
|
+
/* Jarak di CSS komponen: selalu turun dari unit spacing */
|
|
114
|
+
.panel {
|
|
115
|
+
padding: calc(var(--spacing-base) * 3);
|
|
116
|
+
gap: calc(var(--spacing-base) * 2);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Sudut: pakai skala radius */
|
|
120
|
+
.panel {
|
|
121
|
+
border-radius: var(--radius-md);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<!-- Template: pakai skala utility, yang sudah turun dari --spacing -->
|
|
127
|
+
<div class="gap-2 p-3">
|
|
128
|
+
<!-- Sudut -->
|
|
129
|
+
<div class="rounded-md">
|
|
130
|
+
<!-- Tinggi kontrol: satu knop bernama, bukan literal tinggi -->
|
|
131
|
+
<button class="h-control-md"></button>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Jangkauan sumbu**: jarak (padding, margin, gap, ukuran) plus tinggi kontrol ikut
|
|
137
|
+
`theme-space`. Tipografi, ukuran ikon, dan tebal garis **tidak** ikut. Bentuk pil
|
|
138
|
+
semantik (`rounded-full` pada avatar, switch, radio, progress, spinner, dan track
|
|
139
|
+
slider) dikunci sebagai bentuk, jadi sumbu radius tidak menyentuhnya.
|
|
140
|
+
|
|
141
|
+
**Penegakan**: `node scripts/check-geometry-tokens.mjs` (`bun run check:libs:geometry`)
|
|
142
|
+
memindai `library/**/*.ts` dan `library/**/*.css`, lalu menggagalkan build saat
|
|
143
|
+
menemukan pelanggaran. Cek ini berjalan di `verify:libs` dan di CI pada tiap PR dan
|
|
144
|
+
push ke `main`. Yang ditolak:
|
|
145
|
+
|
|
146
|
+
| Jenis | Ditolak | Contoh |
|
|
147
|
+
| -------------- | -------------------------------------------------------------------------------- | --------------------- |
|
|
148
|
+
| Panjang | properti jarak dan ukuran dengan nilai `px`, `rem`, atau `em` mentah | `padding: 12px` |
|
|
149
|
+
| Radius | `border-radius` dengan panjang mentah | `border-radius: 10px` |
|
|
150
|
+
| Nilai arbitrer | utility jarak, ukuran, dan `rounded-*` yang kurung sikunya berisi panjang mentah | `min-w-[8rem]` |
|
|
151
|
+
|
|
152
|
+
Yang **tidak** ditolak, dan memang begitu adanya:
|
|
153
|
+
|
|
154
|
+
- nilai yang membaca token, termasuk `calc(var(--spacing-base) * 3)`,
|
|
155
|
+
`rounded-[var(--radius-base)]`, dan `rounded-[inherit]`;
|
|
156
|
+
- `border-radius: inherit` serta kata kunci tanpa besaran seperti `auto` dan `fit-content`;
|
|
157
|
+
- `0` dan persen, karena keduanya bukan besaran yang punya skala;
|
|
158
|
+
- tebal border dan outline, karena garis rambut tidak boleh ikut skala;
|
|
159
|
+
- ekspresi `calc()`, `min()`, `clamp()` di dalam kurung siku utility, dan satuan
|
|
160
|
+
viewport seperti `85vh`. Guard mempercayainya; kalau isinya ternyata angka mati,
|
|
161
|
+
itu yang lolos dari jaring ini;
|
|
162
|
+
- berkas `*.spec.ts`, karena test hanya mencerminkan class string komponennya. Satu
|
|
163
|
+
nilai diperbaiki di komponennya, bukan di dua tempat;
|
|
164
|
+
- `library/theme/styles/css/` yang justru tempat token didefinisikan.
|
|
165
|
+
|
|
166
|
+
**Pengecualian**: tinggal di
|
|
167
|
+
[`scripts/geometry-token-exceptions.json`](../../../scripts/geometry-token-exceptions.json).
|
|
168
|
+
Tiap baris wajib membawa `pattern`, `files`, dan `reason`; baris tanpa alasan
|
|
169
|
+
tertulis ditolak oleh guard itu sendiri, jadi daftar ini tidak bisa dipakai sebagai
|
|
170
|
+
tempat buang. `wave` bernilai `null` untuk pengecualian permanen, atau angka 2
|
|
171
|
+
sampai 5 untuk utang sementara yang dihapus oleh gelombang itu sendiri. Guard
|
|
172
|
+
mencetak sisa utang per gelombang tiap kali dijalankan, sehingga yang belum
|
|
173
|
+
dibereskan selalu berupa angka, bukan perasaan.
|
|
174
|
+
|
|
175
|
+
**Entry point baru**: tambahkan barisnya ke tabel inventaris sumbu di
|
|
176
|
+
[rationale spec 0003](../../../docs/specs/0003-jangkauan-sumbu-space-radius/rationale.md),
|
|
177
|
+
sebutkan sumbu mana yang berlaku dan seberapa jauh sudah tersambung. Tabel itu
|
|
178
|
+
rujukan tunggal soal sudut mana yang sengaja dikunci dan mana yang kelupaan.
|