@poodle64/ui 2026.8.7
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 +624 -0
- package/dist/components/ui/alert/alert-description.svelte +23 -0
- package/dist/components/ui/alert/alert-description.svelte.d.ts +9 -0
- package/dist/components/ui/alert/alert-title.svelte +23 -0
- package/dist/components/ui/alert/alert-title.svelte.d.ts +9 -0
- package/dist/components/ui/alert/alert.svelte +46 -0
- package/dist/components/ui/alert/alert.svelte.d.ts +28 -0
- package/dist/components/ui/alert/index.d.ts +4 -0
- package/dist/components/ui/alert/index.js +4 -0
- package/dist/components/ui/alert-dialog/alert-dialog-action.svelte +27 -0
- package/dist/components/ui/alert-dialog/alert-dialog-action.svelte.d.ts +9 -0
- package/dist/components/ui/alert-dialog/alert-dialog-cancel.svelte +27 -0
- package/dist/components/ui/alert-dialog/alert-dialog-cancel.svelte.d.ts +9 -0
- package/dist/components/ui/alert-dialog/alert-dialog-content.svelte +39 -0
- package/dist/components/ui/alert-dialog/alert-dialog-content.svelte.d.ts +11 -0
- package/dist/components/ui/alert-dialog/alert-dialog-description.svelte +20 -0
- package/dist/components/ui/alert-dialog/alert-dialog-description.svelte.d.ts +4 -0
- package/dist/components/ui/alert-dialog/alert-dialog-footer.svelte +23 -0
- package/dist/components/ui/alert-dialog/alert-dialog-footer.svelte.d.ts +5 -0
- package/dist/components/ui/alert-dialog/alert-dialog-header.svelte +23 -0
- package/dist/components/ui/alert-dialog/alert-dialog-header.svelte.d.ts +5 -0
- package/dist/components/ui/alert-dialog/alert-dialog-media.svelte +23 -0
- package/dist/components/ui/alert-dialog/alert-dialog-media.svelte.d.ts +5 -0
- package/dist/components/ui/alert-dialog/alert-dialog-overlay.svelte +20 -0
- package/dist/components/ui/alert-dialog/alert-dialog-overlay.svelte.d.ts +4 -0
- package/dist/components/ui/alert-dialog/alert-dialog-portal.svelte +7 -0
- package/dist/components/ui/alert-dialog/alert-dialog-portal.svelte.d.ts +3 -0
- package/dist/components/ui/alert-dialog/alert-dialog-title.svelte +20 -0
- package/dist/components/ui/alert-dialog/alert-dialog-title.svelte.d.ts +4 -0
- package/dist/components/ui/alert-dialog/alert-dialog-trigger.svelte +7 -0
- package/dist/components/ui/alert-dialog/alert-dialog-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/alert-dialog/alert-dialog.svelte +7 -0
- package/dist/components/ui/alert-dialog/alert-dialog.svelte.d.ts +3 -0
- package/dist/components/ui/alert-dialog/index.d.ts +13 -0
- package/dist/components/ui/alert-dialog/index.js +15 -0
- package/dist/components/ui/app-dialog/app-dialog.svelte +111 -0
- package/dist/components/ui/app-dialog/app-dialog.svelte.d.ts +41 -0
- package/dist/components/ui/app-dialog/index.d.ts +2 -0
- package/dist/components/ui/app-dialog/index.js +2 -0
- package/dist/components/ui/app-shell/app-nav.svelte +299 -0
- package/dist/components/ui/app-shell/app-nav.svelte.d.ts +22 -0
- package/dist/components/ui/app-shell/app-shell.svelte +675 -0
- package/dist/components/ui/app-shell/app-shell.svelte.d.ts +157 -0
- package/dist/components/ui/app-shell/index.d.ts +6 -0
- package/dist/components/ui/app-shell/index.js +6 -0
- package/dist/components/ui/app-shell/measure.d.ts +28 -0
- package/dist/components/ui/app-shell/measure.js +21 -0
- package/dist/components/ui/app-shell/texture.d.ts +32 -0
- package/dist/components/ui/app-shell/texture.js +27 -0
- package/dist/components/ui/app-shell/types.d.ts +134 -0
- package/dist/components/ui/app-shell/types.js +88 -0
- package/dist/components/ui/arc-gauge/arc-gauge.svelte +112 -0
- package/dist/components/ui/arc-gauge/arc-gauge.svelte.d.ts +15 -0
- package/dist/components/ui/arc-gauge/index.d.ts +3 -0
- package/dist/components/ui/arc-gauge/index.js +2 -0
- package/dist/components/ui/avatar/avatar-fallback.svelte +26 -0
- package/dist/components/ui/avatar/avatar-fallback.svelte.d.ts +4 -0
- package/dist/components/ui/avatar/avatar-image.svelte +17 -0
- package/dist/components/ui/avatar/avatar-image.svelte.d.ts +4 -0
- package/dist/components/ui/avatar/avatar.svelte +31 -0
- package/dist/components/ui/avatar/avatar.svelte.d.ts +4 -0
- package/dist/components/ui/avatar/index.d.ts +4 -0
- package/dist/components/ui/avatar/index.js +6 -0
- package/dist/components/ui/badge/badge.svelte +50 -0
- package/dist/components/ui/badge/badge.svelte.d.ts +38 -0
- package/dist/components/ui/badge/index.d.ts +2 -0
- package/dist/components/ui/badge/index.js +2 -0
- package/dist/components/ui/bar-row/bar-row.svelte +42 -0
- package/dist/components/ui/bar-row/bar-row.svelte.d.ts +12 -0
- package/dist/components/ui/bar-row/index.d.ts +2 -0
- package/dist/components/ui/bar-row/index.js +2 -0
- package/dist/components/ui/button/button.svelte +89 -0
- package/dist/components/ui/button/button.svelte.d.ts +70 -0
- package/dist/components/ui/button/index.d.ts +2 -0
- package/dist/components/ui/button/index.js +4 -0
- package/dist/components/ui/card/card-action.svelte +23 -0
- package/dist/components/ui/card/card-action.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-content.svelte +20 -0
- package/dist/components/ui/card/card-content.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-description.svelte +20 -0
- package/dist/components/ui/card/card-description.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-footer.svelte +23 -0
- package/dist/components/ui/card/card-footer.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-header.svelte +23 -0
- package/dist/components/ui/card/card-header.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-title.svelte +52 -0
- package/dist/components/ui/card/card-title.svelte.d.ts +8 -0
- package/dist/components/ui/card/card.svelte +25 -0
- package/dist/components/ui/card/card.svelte.d.ts +8 -0
- package/dist/components/ui/card/index.d.ts +8 -0
- package/dist/components/ui/card/index.js +10 -0
- package/dist/components/ui/checkbox/checkbox.svelte +36 -0
- package/dist/components/ui/checkbox/checkbox.svelte.d.ts +8 -0
- package/dist/components/ui/checkbox/index.d.ts +2 -0
- package/dist/components/ui/checkbox/index.js +2 -0
- package/dist/components/ui/command/command-dialog.svelte +42 -0
- package/dist/components/ui/command/command-dialog.svelte.d.ts +14 -0
- package/dist/components/ui/command/command-empty.svelte +17 -0
- package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-group.svelte +33 -0
- package/dist/components/ui/command/command-group.svelte.d.ts +7 -0
- package/dist/components/ui/command/command-input.svelte +36 -0
- package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-item.svelte +27 -0
- package/dist/components/ui/command/command-item.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-link-item.svelte +20 -0
- package/dist/components/ui/command/command-link-item.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-list.svelte +20 -0
- package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-loading.svelte +7 -0
- package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-separator.svelte +17 -0
- package/dist/components/ui/command/command-separator.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-shortcut.svelte +23 -0
- package/dist/components/ui/command/command-shortcut.svelte.d.ts +5 -0
- package/dist/components/ui/command/command.svelte +28 -0
- package/dist/components/ui/command/command.svelte.d.ts +8 -0
- package/dist/components/ui/command/index.d.ts +12 -0
- package/dist/components/ui/command/index.js +14 -0
- package/dist/components/ui/command-palette/command-palette.svelte +94 -0
- package/dist/components/ui/command-palette/command-palette.svelte.d.ts +40 -0
- package/dist/components/ui/command-palette/index.d.ts +3 -0
- package/dist/components/ui/command-palette/index.js +2 -0
- package/dist/components/ui/context-column/context-column.svelte +56 -0
- package/dist/components/ui/context-column/context-column.svelte.d.ts +14 -0
- package/dist/components/ui/context-column/index.d.ts +2 -0
- package/dist/components/ui/context-column/index.js +2 -0
- package/dist/components/ui/data-table/data-table.svelte.d.ts +40 -0
- package/dist/components/ui/data-table/data-table.svelte.js +110 -0
- package/dist/components/ui/data-table/flex-render.svelte +40 -0
- package/dist/components/ui/data-table/flex-render.svelte.d.ts +33 -0
- package/dist/components/ui/data-table/index.d.ts +3 -0
- package/dist/components/ui/data-table/index.js +3 -0
- package/dist/components/ui/data-table/render-helpers.d.ts +90 -0
- package/dist/components/ui/data-table/render-helpers.js +99 -0
- package/dist/components/ui/data-table-tanstack/data-table-tanstack.svelte +247 -0
- package/dist/components/ui/data-table-tanstack/data-table-tanstack.svelte.d.ts +79 -0
- package/dist/components/ui/data-table-tanstack/index.d.ts +3 -0
- package/dist/components/ui/data-table-tanstack/index.js +2 -0
- package/dist/components/ui/data-table-toolbar/data-table-toolbar.svelte +102 -0
- package/dist/components/ui/data-table-toolbar/data-table-toolbar.svelte.d.ts +32 -0
- package/dist/components/ui/data-table-toolbar/index.d.ts +3 -0
- package/dist/components/ui/data-table-toolbar/index.js +2 -0
- package/dist/components/ui/detail-panel/detail-panel.svelte +95 -0
- package/dist/components/ui/detail-panel/detail-panel.svelte.d.ts +26 -0
- package/dist/components/ui/detail-panel/index.d.ts +2 -0
- package/dist/components/ui/detail-panel/index.js +2 -0
- package/dist/components/ui/dialog/dialog-close.svelte +11 -0
- package/dist/components/ui/dialog/dialog-close.svelte.d.ts +4 -0
- package/dist/components/ui/dialog/dialog-content.svelte +48 -0
- package/dist/components/ui/dialog/dialog-content.svelte.d.ts +13 -0
- package/dist/components/ui/dialog/dialog-description.svelte +20 -0
- package/dist/components/ui/dialog/dialog-description.svelte.d.ts +4 -0
- package/dist/components/ui/dialog/dialog-footer.svelte +35 -0
- package/dist/components/ui/dialog/dialog-footer.svelte.d.ts +8 -0
- package/dist/components/ui/dialog/dialog-header.svelte +20 -0
- package/dist/components/ui/dialog/dialog-header.svelte.d.ts +5 -0
- package/dist/components/ui/dialog/dialog-overlay.svelte +20 -0
- package/dist/components/ui/dialog/dialog-overlay.svelte.d.ts +4 -0
- package/dist/components/ui/dialog/dialog-portal.svelte +7 -0
- package/dist/components/ui/dialog/dialog-portal.svelte.d.ts +3 -0
- package/dist/components/ui/dialog/dialog-title.svelte +17 -0
- package/dist/components/ui/dialog/dialog-title.svelte.d.ts +4 -0
- package/dist/components/ui/dialog/dialog-trigger.svelte +11 -0
- package/dist/components/ui/dialog/dialog-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/dialog/dialog.svelte +7 -0
- package/dist/components/ui/dialog/dialog.svelte.d.ts +3 -0
- package/dist/components/ui/dialog/index.d.ts +11 -0
- package/dist/components/ui/dialog/index.js +13 -0
- package/dist/components/ui/dialog-section/dialog-section.svelte +20 -0
- package/dist/components/ui/dialog-section/dialog-section.svelte.d.ts +8 -0
- package/dist/components/ui/dialog-section/index.d.ts +2 -0
- package/dist/components/ui/dialog-section/index.js +2 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +44 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +9 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +31 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte.d.ts +10 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte.d.ts +8 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte.d.ts +8 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +27 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte.d.ts +8 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte.d.ts +3 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte.d.ts +5 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte.d.ts +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte.d.ts +3 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu.svelte.d.ts +3 -0
- package/dist/components/ui/dropdown-menu/index.d.ts +18 -0
- package/dist/components/ui/dropdown-menu/index.js +18 -0
- package/dist/components/ui/empty-state/empty-state.svelte +44 -0
- package/dist/components/ui/empty-state/empty-state.svelte.d.ts +14 -0
- package/dist/components/ui/empty-state/index.d.ts +2 -0
- package/dist/components/ui/empty-state/index.js +2 -0
- package/dist/components/ui/error-state/error-state.svelte +52 -0
- package/dist/components/ui/error-state/error-state.svelte.d.ts +10 -0
- package/dist/components/ui/error-state/index.d.ts +2 -0
- package/dist/components/ui/error-state/index.js +2 -0
- package/dist/components/ui/info-tip/index.d.ts +2 -0
- package/dist/components/ui/info-tip/index.js +2 -0
- package/dist/components/ui/info-tip/info-tip.svelte +37 -0
- package/dist/components/ui/info-tip/info-tip.svelte.d.ts +10 -0
- package/dist/components/ui/input/index.d.ts +2 -0
- package/dist/components/ui/input/index.js +4 -0
- package/dist/components/ui/input/input.svelte +48 -0
- package/dist/components/ui/input/input.svelte.d.ts +13 -0
- package/dist/components/ui/input-group/index.d.ts +7 -0
- package/dist/components/ui/input-group/index.js +9 -0
- package/dist/components/ui/input-group/input-group-addon.svelte +53 -0
- package/dist/components/ui/input-group/input-group-addon.svelte.d.ts +32 -0
- package/dist/components/ui/input-group/input-group-button.svelte +49 -0
- package/dist/components/ui/input-group/input-group-button.svelte.d.ts +32 -0
- package/dist/components/ui/input-group/input-group-input.svelte +23 -0
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +11 -0
- package/dist/components/ui/input-group/input-group-text.svelte +22 -0
- package/dist/components/ui/input-group/input-group-text.svelte.d.ts +5 -0
- package/dist/components/ui/input-group/input-group-textarea.svelte +23 -0
- package/dist/components/ui/input-group/input-group-textarea.svelte.d.ts +3 -0
- package/dist/components/ui/input-group/input-group.svelte +24 -0
- package/dist/components/ui/input-group/input-group.svelte.d.ts +5 -0
- package/dist/components/ui/label/index.d.ts +2 -0
- package/dist/components/ui/label/index.js +4 -0
- package/dist/components/ui/label/label.svelte +20 -0
- package/dist/components/ui/label/label.svelte.d.ts +4 -0
- package/dist/components/ui/loading-state/index.d.ts +2 -0
- package/dist/components/ui/loading-state/index.js +2 -0
- package/dist/components/ui/loading-state/loading-state.svelte +28 -0
- package/dist/components/ui/loading-state/loading-state.svelte.d.ts +8 -0
- package/dist/components/ui/page-header/index.d.ts +2 -0
- package/dist/components/ui/page-header/index.js +2 -0
- package/dist/components/ui/page-header/page-header.svelte +100 -0
- package/dist/components/ui/page-header/page-header.svelte.d.ts +17 -0
- package/dist/components/ui/panel/index.d.ts +2 -0
- package/dist/components/ui/panel/index.js +2 -0
- package/dist/components/ui/panel/panel.svelte +54 -0
- package/dist/components/ui/panel/panel.svelte.d.ts +16 -0
- package/dist/components/ui/password-input/index.d.ts +2 -0
- package/dist/components/ui/password-input/index.js +4 -0
- package/dist/components/ui/password-input/password-input.svelte +48 -0
- package/dist/components/ui/password-input/password-input.svelte.d.ts +8 -0
- package/dist/components/ui/popover/index.d.ts +4 -0
- package/dist/components/ui/popover/index.js +6 -0
- package/dist/components/ui/popover/popover-content.svelte +45 -0
- package/dist/components/ui/popover/popover-content.svelte.d.ts +7 -0
- package/dist/components/ui/popover/popover-trigger.svelte +7 -0
- package/dist/components/ui/popover/popover-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/popover/popover.svelte +7 -0
- package/dist/components/ui/popover/popover.svelte.d.ts +3 -0
- package/dist/components/ui/progress/index.d.ts +2 -0
- package/dist/components/ui/progress/index.js +4 -0
- package/dist/components/ui/progress/progress.svelte +30 -0
- package/dist/components/ui/progress/progress.svelte.d.ts +4 -0
- package/dist/components/ui/scorecard/index.d.ts +2 -0
- package/dist/components/ui/scorecard/index.js +2 -0
- package/dist/components/ui/scorecard/scorecard.svelte +35 -0
- package/dist/components/ui/scorecard/scorecard.svelte.d.ts +8 -0
- package/dist/components/ui/select/index.d.ts +12 -0
- package/dist/components/ui/select/index.js +14 -0
- package/dist/components/ui/select/select-content.svelte +45 -0
- package/dist/components/ui/select/select-content.svelte.d.ts +11 -0
- package/dist/components/ui/select/select-group-heading.svelte +21 -0
- package/dist/components/ui/select/select-group-heading.svelte.d.ts +10 -0
- package/dist/components/ui/select/select-group.svelte +17 -0
- package/dist/components/ui/select/select-group.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-item.svelte +38 -0
- package/dist/components/ui/select/select-item.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-label.svelte +20 -0
- package/dist/components/ui/select/select-label.svelte.d.ts +6 -0
- package/dist/components/ui/select/select-portal.svelte +7 -0
- package/dist/components/ui/select/select-portal.svelte.d.ts +3 -0
- package/dist/components/ui/select/select-scroll-down-button.svelte +23 -0
- package/dist/components/ui/select/select-scroll-down-button.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-scroll-up-button.svelte +23 -0
- package/dist/components/ui/select/select-scroll-up-button.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-separator.svelte +18 -0
- package/dist/components/ui/select/select-separator.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-trigger.svelte +29 -0
- package/dist/components/ui/select/select-trigger.svelte.d.ts +8 -0
- package/dist/components/ui/select/select.svelte +11 -0
- package/dist/components/ui/select/select.svelte.d.ts +3 -0
- package/dist/components/ui/separator/index.d.ts +2 -0
- package/dist/components/ui/separator/index.js +4 -0
- package/dist/components/ui/separator/separator.svelte +23 -0
- package/dist/components/ui/separator/separator.svelte.d.ts +4 -0
- package/dist/components/ui/skeleton/index.d.ts +2 -0
- package/dist/components/ui/skeleton/index.js +4 -0
- package/dist/components/ui/skeleton/skeleton.svelte +17 -0
- package/dist/components/ui/skeleton/skeleton.svelte.d.ts +5 -0
- package/dist/components/ui/sonner/index.d.ts +1 -0
- package/dist/components/ui/sonner/index.js +1 -0
- package/dist/components/ui/sonner/sonner.svelte +34 -0
- package/dist/components/ui/sonner/sonner.svelte.d.ts +4 -0
- package/dist/components/ui/sparkline/index.d.ts +3 -0
- package/dist/components/ui/sparkline/index.js +2 -0
- package/dist/components/ui/sparkline/sparkline.svelte +90 -0
- package/dist/components/ui/sparkline/sparkline.svelte.d.ts +16 -0
- package/dist/components/ui/stat-card/index.d.ts +2 -0
- package/dist/components/ui/stat-card/index.js +2 -0
- package/dist/components/ui/stat-card/stat-card.svelte +77 -0
- package/dist/components/ui/stat-card/stat-card.svelte.d.ts +34 -0
- package/dist/components/ui/stat-list/index.d.ts +3 -0
- package/dist/components/ui/stat-list/index.js +2 -0
- package/dist/components/ui/stat-list/stat-list.svelte +80 -0
- package/dist/components/ui/stat-list/stat-list.svelte.d.ts +23 -0
- package/dist/components/ui/status/index.d.ts +1 -0
- package/dist/components/ui/status/index.js +1 -0
- package/dist/components/ui/status-badge/index.d.ts +3 -0
- package/dist/components/ui/status-badge/index.js +2 -0
- package/dist/components/ui/status-badge/status-badge.svelte +53 -0
- package/dist/components/ui/status-badge/status-badge.svelte.d.ts +39 -0
- package/dist/components/ui/switch/index.d.ts +2 -0
- package/dist/components/ui/switch/index.js +4 -0
- package/dist/components/ui/switch/switch.svelte +39 -0
- package/dist/components/ui/switch/switch.svelte.d.ts +4 -0
- package/dist/components/ui/table/index.d.ts +10 -0
- package/dist/components/ui/table/index.js +12 -0
- package/dist/components/ui/table/table-body.svelte +20 -0
- package/dist/components/ui/table/table-body.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-caption.svelte +20 -0
- package/dist/components/ui/table/table-caption.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-cell.svelte +20 -0
- package/dist/components/ui/table/table-cell.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-classes.d.ts +15 -0
- package/dist/components/ui/table/table-classes.js +15 -0
- package/dist/components/ui/table/table-footer.svelte +20 -0
- package/dist/components/ui/table/table-footer.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-head.svelte +23 -0
- package/dist/components/ui/table/table-head.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-header.svelte +20 -0
- package/dist/components/ui/table/table-header.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-row.svelte +23 -0
- package/dist/components/ui/table/table-row.svelte.d.ts +5 -0
- package/dist/components/ui/table/table.svelte +37 -0
- package/dist/components/ui/table/table.svelte.d.ts +20 -0
- package/dist/components/ui/tabs/index.d.ts +5 -0
- package/dist/components/ui/tabs/index.js +7 -0
- package/dist/components/ui/tabs/tabs-content.svelte +13 -0
- package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs-list.svelte +16 -0
- package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte +16 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs.svelte +19 -0
- package/dist/components/ui/tabs/tabs.svelte.d.ts +4 -0
- package/dist/components/ui/textarea/index.d.ts +2 -0
- package/dist/components/ui/textarea/index.js +4 -0
- package/dist/components/ui/textarea/textarea.svelte +22 -0
- package/dist/components/ui/textarea/textarea.svelte.d.ts +5 -0
- package/dist/components/ui/tile-grid/index.d.ts +2 -0
- package/dist/components/ui/tile-grid/index.js +2 -0
- package/dist/components/ui/tile-grid/tile-grid.svelte +66 -0
- package/dist/components/ui/tile-grid/tile-grid.svelte.d.ts +43 -0
- package/dist/components/ui/tooltip/index.d.ts +6 -0
- package/dist/components/ui/tooltip/index.js +8 -0
- package/dist/components/ui/tooltip/tooltip-content.svelte +52 -0
- package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +11 -0
- package/dist/components/ui/tooltip/tooltip-portal.svelte +7 -0
- package/dist/components/ui/tooltip/tooltip-portal.svelte.d.ts +4 -0
- package/dist/components/ui/tooltip/tooltip-provider.svelte +7 -0
- package/dist/components/ui/tooltip/tooltip-provider.svelte.d.ts +4 -0
- package/dist/components/ui/tooltip/tooltip-trigger.svelte +7 -0
- package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/tooltip/tooltip.svelte +7 -0
- package/dist/components/ui/tooltip/tooltip.svelte.d.ts +4 -0
- package/dist/styles.css +961 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +5 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
# @poodle64/ui
|
|
2
|
+
|
|
3
|
+
Household shared shadcn-svelte component primitives (bits-ui), extracted from
|
|
4
|
+
the estate's most conformant consuming app's frontend — the best-looking,
|
|
5
|
+
most battle-tested implementation of each primitive — plus `alert`/`popover`/
|
|
6
|
+
`progress`/`tabs` from the first app that migrated onto this package (WP-51
|
|
7
|
+
Lane WP) and needed them.
|
|
8
|
+
|
|
9
|
+
`bits-ui` is a required peerDependency: components share compound-component
|
|
10
|
+
context across the package, so a duplicated `bits-ui` instance is a real
|
|
11
|
+
functional bug (mismatched types at best). `mode-watcher` and `svelte-sonner`
|
|
12
|
+
are optional peers (`peerDependenciesMeta`), needed only if the consuming app
|
|
13
|
+
uses `@poodle64/ui/sonner` (a single dark-mode store for `mode-watcher`, one
|
|
14
|
+
toast queue for `svelte-sonner`'s `toast()` + `Toaster` pair — a duplicated
|
|
15
|
+
instance there means a `Toaster` that never sees the app's own `toast()`
|
|
16
|
+
calls). Declare whichever peers you use directly in your own `package.json`:
|
|
17
|
+
pnpm auto-installs missing peers, but an explicit dependency is what lets
|
|
18
|
+
Renovate track the version and `pnpm ls` show it.
|
|
19
|
+
|
|
20
|
+
Every app previously vendored its own copy of these primitives and restyled them
|
|
21
|
+
through its `@poodle64/design-tokens` alias layer. That let apps differ by palette,
|
|
22
|
+
but a fix (an accessibility bug, a focus-trap issue) had to be applied once per app.
|
|
23
|
+
This package is the same restyling mechanism: components consume shadcn's standard
|
|
24
|
+
CSS variable names (`bg-primary`, `text-foreground`, `--radius`, …), and both the
|
|
25
|
+
component code and the mapping those names resolve through now live once, here.
|
|
26
|
+
Apps differ by palette and nothing else. Override `--ds-color-*` and the whole
|
|
27
|
+
shadcn surface follows; a consuming app writes no alias layer of its own.
|
|
28
|
+
|
|
29
|
+
## What is here
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
src/lib/
|
|
33
|
+
utils.ts cn() (clsx + tailwind-merge) and the shared TS helper types
|
|
34
|
+
styles.css the component stylesheet: scale keys, .ds-edge, .ds-chip/.ds-dot,
|
|
35
|
+
the dialogue-section divider rule
|
|
36
|
+
components/ui/ one directory per component: the shadcn-svelte primitives
|
|
37
|
+
(bits-ui behaviour + shadcn markup/variants) and the composed
|
|
38
|
+
page-chrome components built on top of them
|
|
39
|
+
dist/ generated by `pnpm build` (@sveltejs/package); never edit
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Primitives** (26 — battle-tested implementations pulled from whichever app had
|
|
43
|
+
them first, not an invented "ideal" list): `alert`, `alert-dialog`, `avatar`,
|
|
44
|
+
`badge`, `button`, `card`, `checkbox`, `command`, `data-table`, `dialog`,
|
|
45
|
+
`dropdown-menu`, `input`, `input-group`, `label`, `password-input`, `popover`,
|
|
46
|
+
`progress`, `select`, `separator`, `skeleton`, `sonner`, `switch`, `table`,
|
|
47
|
+
`tabs`, `textarea`, `tooltip`.
|
|
48
|
+
|
|
49
|
+
`avatar` is root, image and fallback only; the group and badge variants in the
|
|
50
|
+
upstream registry had no consumer in any surveyed app. It is here because
|
|
51
|
+
`AppShell`'s `identity` slot needs one, and a consumer filling a slot the shell
|
|
52
|
+
itself defines should not have to keep a private copy of a primitive to do it.
|
|
53
|
+
|
|
54
|
+
Not yet included: `sheet` — no app's `ui/` has vendored it yet. Add it here
|
|
55
|
+
(`pnpm dlx shadcn-svelte@latest add <name>` inside `packages/ui`, or hand-port from
|
|
56
|
+
a sibling app once one adopts it) the first time a converging app actually needs it;
|
|
57
|
+
do not invent it speculatively.
|
|
58
|
+
|
|
59
|
+
**Deliberately not here: `chart` and `form`.** Two apps keep local copies of each,
|
|
60
|
+
and both stay local for reasons that are about coupling rather than effort.
|
|
61
|
+
|
|
62
|
+
`form` in the upstream registry is a Formsnap wrapper over Superforms. Adding it
|
|
63
|
+
would make a form library a peer of the whole design system and decide, for every
|
|
64
|
+
app, how validation and submission work — which is an application-architecture
|
|
65
|
+
choice, not a design one. An app that uses Superforms already gets its markup from
|
|
66
|
+
`input`, `label`, `input-group` and `button` here; what it keeps locally is the
|
|
67
|
+
binding layer, which is where it belongs.
|
|
68
|
+
|
|
69
|
+
`chart` is a LayerChart wrapper, and it needs the `chart-1`…`chart-5` colour keys
|
|
70
|
+
this stylesheet deliberately does not register — chart hue is the one part of the
|
|
71
|
+
shadcn surface the estate genuinely disagrees on, and registering it here would
|
|
72
|
+
claim a key no app can then override without a stylesheet-order coin toss (see
|
|
73
|
+
the one-owner-per-key note below). It becomes a candidate the day two apps want
|
|
74
|
+
the same chart palette; until then a shared chart component would be a shared
|
|
75
|
+
disagreement.
|
|
76
|
+
|
|
77
|
+
**Composed components** (20). Primitives are not what makes an app look like an
|
|
78
|
+
app — the page chrome is. These are the cross-cutting surfaces every route
|
|
79
|
+
composes from, so a household app gets its layout language from the package
|
|
80
|
+
rather than rebuilding it:
|
|
81
|
+
|
|
82
|
+
| Import | What it is |
|
|
83
|
+
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
|
+
| `page-header` | The only page-title pattern: optional `breadcrumbs` snippet, eyebrow, an optional title, one clamped subtitle, an `info` tooltip, an `actions` slot. Omit `title` for a header that is a breadcrumb bar. |
|
|
85
|
+
| `panel` | The generic titled card: optional icon, subtitle and trailing actions over a body that can opt out of padding. |
|
|
86
|
+
| `detail-panel` | The entity-detail surface: header with icon/eyebrow/title/`StatusBadge`/close, scrollable body, footer of actions. |
|
|
87
|
+
| `context-column` | The persistent right-hand column: a standing `StatList` plus an optional detail that flows in on select. |
|
|
88
|
+
| `app-dialog` | The dialogue frame: titled header, scrollable body, footer action bar, five sizes (`xs`…`xl`), and an `onOpenChange` for the dismissals the caller did not drive. |
|
|
89
|
+
| `dialog-section` | One section of a dialogue body; adjacent sections are divided automatically. |
|
|
90
|
+
| `stat-card` | A single metric that earns its space (label, value, unit, sub, status dot, and `valueTone` to colour the figure itself). |
|
|
91
|
+
| `stat-list` | A route's low-context integers as a label→value list. Zero-aware: `muted` keeps a healthy zero quiet. |
|
|
92
|
+
| `arc-gauge` | A radial capacity/percentage ring for a single 0–100 metric, in a footprint too compact for a `stat-card`. |
|
|
93
|
+
| `bar-row` | A labelled horizontal bar with a trailing tabular value, for a ranked list (usage, rank, token burn). |
|
|
94
|
+
| `scorecard` | A compact 0/1/2 dot-row health strip for several independent checks read at a glance. |
|
|
95
|
+
| `sparkline` | An inline multi-series area+line trend for a row or card with room for a trend but not a full chart. |
|
|
96
|
+
| `status` / `status-badge` | The fixed five-state vocabulary (`success \| warning \| error \| info \| neutral`) and the one state chip, with `pulse` for a state still in motion and `class` for placement. `status-badge` alone also accepts `'primary'`, a brand-emphasis extension outside the shared vocabulary — `stat-card`, `stat-list` and `data-table-toolbar` never see it. |
|
|
97
|
+
| `empty-state` / `error-state` / `loading-state` | The shared blank, error and loading surfaces. Never hand-roll one. |
|
|
98
|
+
| `info-tip` | One tooltip pattern: a small info trigger, or wrap an existing affordance as children. |
|
|
99
|
+
| `data-table-toolbar` | Search field plus filter-chip groups for a TanStack table. Owns no state; fires callbacks. |
|
|
100
|
+
| `data-table-tanstack` | The TanStack-backed table: global search, column filters, master-detail row select, opt-in bulk selection, responsive column hiding, a first-class empty branch. |
|
|
101
|
+
|
|
102
|
+
**The application shell** (`app-shell`, `command-palette`). Page chrome is not
|
|
103
|
+
what makes an app feel like an app either — the shell is. Five household
|
|
104
|
+
frontends were surveyed before this API was settled, and between them they had
|
|
105
|
+
built five shells: a rail-plus-drawer, an eleven-file collapsible sidebar tree
|
|
106
|
+
under its own top bar, and three header-only bars that each answered the mobile
|
|
107
|
+
question differently. They agreed on almost nothing structurally while trying to
|
|
108
|
+
be the same thing.
|
|
109
|
+
|
|
110
|
+
There is now exactly one shell shape, no prop to choose a different one.
|
|
111
|
+
Operator ruling, 31/07/2026: every household app renders the same composition,
|
|
112
|
+
a permanent side rail on `md`+ (an overlay drawer below it) carrying primary
|
|
113
|
+
navigation, brand and the collapse toggle, **plus** a real top navbar (search,
|
|
114
|
+
leading/trailing slots, theme toggle, identity), always both together, with
|
|
115
|
+
content that is always full-width. Apps do not choose variants, content modes,
|
|
116
|
+
or spacing.
|
|
117
|
+
|
|
118
|
+
Everything the apps otherwise differed on turned out to be a slot, not a variant.
|
|
119
|
+
The package therefore imports no app store, no app route and no app brand:
|
|
120
|
+
|
|
121
|
+
```svelte
|
|
122
|
+
<script lang="ts">
|
|
123
|
+
import { page } from '$app/state';
|
|
124
|
+
import { goto } from '$app/navigation';
|
|
125
|
+
import AppShell, { type NavGroup } from '@poodle64/ui/app-shell';
|
|
126
|
+
import CommandPalette from '@poodle64/ui/command-palette';
|
|
127
|
+
import { nav } from '$lib/config/navigation'; // typed as NavGroup[]
|
|
128
|
+
|
|
129
|
+
let paletteOpen = $state(false);
|
|
130
|
+
</script>
|
|
131
|
+
|
|
132
|
+
<AppShell {nav} brandTitle="Console" currentPath={page.url.pathname}
|
|
133
|
+
onSearch={() => (paletteOpen = true)}>
|
|
134
|
+
{@render children()}
|
|
135
|
+
</AppShell>
|
|
136
|
+
<CommandPalette bind:open={paletteOpen} {nav} onNavigate={goto} />
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
That is the whole minimum. Everything below is optional.
|
|
140
|
+
|
|
141
|
+
| Prop | Purpose |
|
|
142
|
+
| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
143
|
+
| `nav` | Bare `NavItem`s, `NavGroup`s, or a mix. Consecutive bare items collapse into one run; an emptied group renders nothing. |
|
|
144
|
+
| `currentPath` | Active state, and closing the mobile nav on navigation. |
|
|
145
|
+
| `collapsible`, `collapsed` | An icon-only rail collapse toggle whose state binds out so an app can persist it. |
|
|
146
|
+
| `brand` / `brandTitle` + `brandMark` / `homeHref` | Full control of the lockup, or the wordmark-plus-mark shorthand. |
|
|
147
|
+
| `identity` | The signed-in surface. Rendered once, at the end of the top bar. |
|
|
148
|
+
| `context`, `actions` | Leading and trailing top-bar slots: a store/tenant switcher, app-level action buttons. |
|
|
149
|
+
| `banner` | Full-width region under the bar: reconnect notices, trial warnings. |
|
|
150
|
+
| `sidebar` | A secondary, route-scoped column between the nav and the page body. |
|
|
151
|
+
| `onSearch`, `searchLabel`, `searchShortcut` | Provide `onSearch` to render the search affordance at all. |
|
|
152
|
+
| `themeToggle`, `onToggleTheme` | Defaults to `mode-watcher`. Set `themeToggle={false}` when the app puts theming inside its own user menu. |
|
|
153
|
+
| `measure` | How wide the page body may get, from a named scale. Defaults to `full` (no cap). |
|
|
154
|
+
| `texture` | The house atmosphere on the content region. Defaults to `none`. |
|
|
155
|
+
| `padded`, `mainClass` | Padding, and extra classes on the scrolling content container. |
|
|
156
|
+
|
|
157
|
+
### Nested navigation
|
|
158
|
+
|
|
159
|
+
A section with its own navigation puts it on the item, as `children`, and the
|
|
160
|
+
rail discloses it in place:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
{
|
|
164
|
+
label: 'Education',
|
|
165
|
+
href: '/education',
|
|
166
|
+
icon: BookOpen,
|
|
167
|
+
children: [
|
|
168
|
+
{ label: 'Open architecture', href: '/education/open-architecture' },
|
|
169
|
+
{ label: 'Autonomy', href: '/education/autonomy' }
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
That is the whole API. An item with no `children` renders and behaves exactly as
|
|
175
|
+
it did before the field existed.
|
|
176
|
+
|
|
177
|
+
It exists because the alternative was two left-hand columns: an app whose
|
|
178
|
+
sections have inner navigation had nowhere to put it in the rail, so it put
|
|
179
|
+
modules in `nav` and the current section's pages in `sidebar`. The other way out
|
|
180
|
+
(modules along the top bar, rail for the current module) was rejected on mobile:
|
|
181
|
+
it leaves two navigation surfaces that both need collapsing and both want the
|
|
182
|
+
same hamburger. One nested tree collapses to one drawer.
|
|
183
|
+
|
|
184
|
+
The behaviour, and why:
|
|
185
|
+
|
|
186
|
+
- **The parent stays a destination.** Its label navigates; a separate chevron
|
|
187
|
+
expands. Folding both into the link cannot be made honest, since
|
|
188
|
+
`aria-expanded` on something that navigates away announces a state the user
|
|
189
|
+
never observes; and making the label expand-only would silently change what
|
|
190
|
+
`href` means the day an app adds children to an item that already had one.
|
|
191
|
+
- **A group holding the current page is open**, on first paint, with nothing
|
|
192
|
+
clicked. A rail that will not show you where you are is the defect this
|
|
193
|
+
feature exists to remove.
|
|
194
|
+
- **A deliberate toggle wins, and keeps winning** for the life of the shell,
|
|
195
|
+
which in a SvelteKit layout is the session. It overrides the path default rather than
|
|
196
|
+
replacing it, so an untouched group still opens as you walk into it while a
|
|
197
|
+
group you made a decision about keeps your decision. Nothing is written to
|
|
198
|
+
storage: a shared package writing to a fixed key would collide with the app's
|
|
199
|
+
own, and with itself on a page rendering two navs.
|
|
200
|
+
- **One level, enforced by the type.** `children` holds `NavChildItem`, which is
|
|
201
|
+
`NavItem` minus `children`, so a second nesting is a compile error where the
|
|
202
|
+
nav is authored. The rail is 15.5rem and every level costs an indent; by depth
|
|
203
|
+
three the label has less room than the chevron.
|
|
204
|
+
- **A collapsed rail renders no tree.** 3.5rem of icons cannot hold one, and a
|
|
205
|
+
hover flyout would be a second interaction surface with its own positioning,
|
|
206
|
+
touch and focus story. The parent stays an icon-only link to its own page; the
|
|
207
|
+
way back is the Collapse control the user just used.
|
|
208
|
+
- **The drawer renders the same tree.** It is the same element as the rail, and
|
|
209
|
+
it is never collapsed.
|
|
210
|
+
|
|
211
|
+
What this does not absorb, and deliberately: a `children` list is flat and one
|
|
212
|
+
deep, so a section whose own navigation is itself grouped under sub-headings, or
|
|
213
|
+
whose rows disclose a third level, does not lift into the rail whole. The rail is
|
|
214
|
+
not the place to fix that. At 15.5rem a third level leaves roughly 128px for the
|
|
215
|
+
label, which is about fifteen characters. A section that deep keeps its deepest
|
|
216
|
+
level on its own page, where there is width for it, and the `sidebar` snippet
|
|
217
|
+
stays the right home for a column that is not `NavItem`-shaped at all: a document
|
|
218
|
+
tree, a table of contents, a filter panel.
|
|
219
|
+
|
|
220
|
+
`NavItem` / `NavGroup` are exported so an app types its own config against them.
|
|
221
|
+
They carry **no notion of who may see an item**: two surveyed apps gate
|
|
222
|
+
navigation on admin or per-module permission, and both do it with their own auth
|
|
223
|
+
store. Apps filter before they pass (`nav.filter((i) => !i.adminOnly || user.isAdmin)`),
|
|
224
|
+
and that is what keeps this package free of the auth coupling that made the best
|
|
225
|
+
shell in the estate unliftable in the first place.
|
|
226
|
+
|
|
227
|
+
`currentPath` is a prop rather than a `$app/state` import for the same reason:
|
|
228
|
+
this package is built with `svelte-package` and has no SvelteKit runtime, so
|
|
229
|
+
importing it would make SvelteKit a hard peer and make the shell untestable
|
|
230
|
+
outside a running app.
|
|
231
|
+
|
|
232
|
+
`CommandPalette` ships beside the shell because it carried the identical
|
|
233
|
+
coupling to a hardcoded navigation module; leaving it behind would have stranded
|
|
234
|
+
the shell's search affordance. It binds ⌘K / Ctrl-K itself (`shortcut={false}` to
|
|
235
|
+
opt out) and takes a `children` snippet for app-specific command groups beneath
|
|
236
|
+
the navigation group.
|
|
237
|
+
|
|
238
|
+
The shell paints its chrome from `--ds-shell-chrome`, registered as the `shell`
|
|
239
|
+
colour key. It is deliberately **not** `sidebar`: those keys stay the app's to
|
|
240
|
+
define, and one-owner-per-key is what keeps an override from depending on
|
|
241
|
+
stylesheet order. An app that already has a chrome hue points it there in one
|
|
242
|
+
line, `:root { --ds-shell-chrome: var(--sidebar); }`, and `--ds-shell-rail-width`
|
|
243
|
+
retunes the rail.
|
|
244
|
+
|
|
245
|
+
Chrome **ink** follows the same pattern, through `--ds-shell-chrome-foreground`
|
|
246
|
+
and `--ds-shell-chrome-muted-foreground`. Both default to the page's own
|
|
247
|
+
foreground tokens, so nothing moves until an app asks; set them to invert the
|
|
248
|
+
whole chrome — rail, bar, every chrome control and every nav row — against the
|
|
249
|
+
palette:
|
|
250
|
+
|
|
251
|
+
```css
|
|
252
|
+
:root {
|
|
253
|
+
--ds-shell-chrome: var(--ds-color-primary);
|
|
254
|
+
--ds-shell-chrome-foreground: var(--ds-color-primary-foreground);
|
|
255
|
+
--ds-shell-chrome-muted-foreground: color-mix(
|
|
256
|
+
in oklch,
|
|
257
|
+
var(--ds-color-primary-foreground) 70%,
|
|
258
|
+
transparent
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`AppNav` used outside the chrome (the `sidebar` slot's own column) keeps the
|
|
264
|
+
page's ink, so inverting the rail does not drag a secondary nav with it.
|
|
265
|
+
|
|
266
|
+
### The content measure
|
|
267
|
+
|
|
268
|
+
How wide the page body may get, chosen once in the layout from a scale of four:
|
|
269
|
+
|
|
270
|
+
```svelte
|
|
271
|
+
<AppShell {nav} currentPath={page.url.pathname} measure="page">
|
|
272
|
+
{@render children()}
|
|
273
|
+
</AppShell>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
| Value | Width | Reach for it when the page is |
|
|
277
|
+
| ------- | -------- | -------------------------------------------------------------------- |
|
|
278
|
+
| `prose` | `72ch` | long-form running text: documentation, an article, a policy, a guide |
|
|
279
|
+
| `page` | `80rem` | an everyday page: a form, a detail view, settings, a wizard |
|
|
280
|
+
| `wide` | `120rem` | an index or a dashboard: card grids, tables, charts, board columns |
|
|
281
|
+
| `full` | no cap | a canvas that should use the whole panel: a map, a diagram, a board |
|
|
282
|
+
|
|
283
|
+
`full` is the default, so a shell that does not mention `measure` renders exactly
|
|
284
|
+
as it did before the prop existed — verified, not assumed: the content region's
|
|
285
|
+
markup and geometry were diffed against the pre-change build across three
|
|
286
|
+
surfaces at three viewports and are identical on every field (`harness/drive.md`).
|
|
287
|
+
Adoption is deliberate, one app at a time.
|
|
288
|
+
|
|
289
|
+
It exists because "content is always full-width" did not remove the width
|
|
290
|
+
decision, it pushed it into every page. Surveyed at 2560px, one consumer had six
|
|
291
|
+
distinct caps across nine routes — each a hand-written `mx-auto max-w-*` at the
|
|
292
|
+
top of a `+page.svelte`, none wrong on its own, no two agreeing — using between
|
|
293
|
+
15% and 79% of the width available. Across the estate that is 42 hand-rolled caps
|
|
294
|
+
in 34 page files, spanning eight different `max-w-*` values. A scale with four
|
|
295
|
+
names cannot drift like that.
|
|
296
|
+
|
|
297
|
+
**Why `prose` is a tier at all.** Because a shared measure that let running text
|
|
298
|
+
span a large display would be worse than what it replaces, and worse everywhere
|
|
299
|
+
at once. Measured in Chromium at 2560px: the same paragraph runs to **311
|
|
300
|
+
characters per line** uncapped, against an accepted band of 45–90 for continuous
|
|
301
|
+
text. `prose` puts it at 80. It is stated in `ch` rather than `rem` deliberately —
|
|
302
|
+
a reading measure is a count of characters, not a length, so it has to track
|
|
303
|
+
whatever face and size the app actually set.
|
|
304
|
+
|
|
305
|
+
Every tier is a CSS custom property, so an app retunes one without waiting on a
|
|
306
|
+
release of this package:
|
|
307
|
+
|
|
308
|
+
```css
|
|
309
|
+
:root {
|
|
310
|
+
--ds-shell-measure-prose: 66ch;
|
|
311
|
+
--ds-shell-measure-page: 72rem;
|
|
312
|
+
--ds-shell-measure-wide: 100rem;
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Two things worth knowing before you set it:
|
|
317
|
+
|
|
318
|
+
- **The cap is a ceiling, never a floor.** `page` caps at 80rem; a 1440px laptop
|
|
319
|
+
has less than that available, so nothing changes there. A measure never narrows
|
|
320
|
+
a window that was already narrower than the tier.
|
|
321
|
+
- **`prose` moves when the font does.** A webfont arriving after first paint
|
|
322
|
+
reflows the column, because `ch` is measured in the face that actually
|
|
323
|
+
rendered. That is the tier doing its job rather than a bug; `page` and `wide`
|
|
324
|
+
are lengths and never move.
|
|
325
|
+
- **The cap is the border box, so `padded` spends its padding inside it.**
|
|
326
|
+
`measure="page"` with the default padding is 80rem of box holding 78rem of
|
|
327
|
+
content, which is the same arithmetic as the `mx-auto max-w-4xl px-6` idiom it
|
|
328
|
+
replaces. An app running `padded={false}` and its own page padding gets the cap
|
|
329
|
+
flush, which is usually what it wanted.
|
|
330
|
+
|
|
331
|
+
Set it in the layout, not the page. A page reaching for `measure` is the habit
|
|
332
|
+
this replaces; a route group that genuinely differs (a docs section inside an
|
|
333
|
+
app of dashboards) gets its own layout, which is where a shared decision belongs.
|
|
334
|
+
|
|
335
|
+
### The content texture
|
|
336
|
+
|
|
337
|
+
The house atmosphere — a faint dot-grid floor with a soft accent vignette in the
|
|
338
|
+
top-right — painted once, by the shell, on the region that scrolls:
|
|
339
|
+
|
|
340
|
+
```svelte
|
|
341
|
+
<AppShell {nav} currentPath={page.url.pathname} texture="grid">
|
|
342
|
+
{@render children()}
|
|
343
|
+
</AppShell>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
| Value | What it paints | Reach for it when |
|
|
347
|
+
| ------ | ------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
348
|
+
| `grid` | a dot-grid floor plus one accent vignette in the corner | the app wants the house "instrument-grade" surface rather than flat page |
|
|
349
|
+
| `none` | nothing at all | the default |
|
|
350
|
+
|
|
351
|
+
`none` is the default, so a shell that does not mention `texture` renders exactly
|
|
352
|
+
as it did before the prop existed. Verified, not assumed, and reproducibly so:
|
|
353
|
+
|
|
354
|
+
```sh
|
|
355
|
+
node harness/additivity.mjs ui-v2026.8.3
|
|
356
|
+
# 15 surface/viewport pairs, 105 compared fields (including the screenshot hash)
|
|
357
|
+
# IDENTICAL on every field and every pixel — the change is additive.
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
`harness/additivity.mjs` builds the package at any base ref in a throwaway git
|
|
361
|
+
worktree, renders the five surfaces an existing consumer already has at 2560px,
|
|
362
|
+
1440px and 360px, and diffs the markup, both attribute sets, the computed box and
|
|
363
|
+
background properties, the geometry and the rendered pixels. Adoption is
|
|
364
|
+
deliberate, one app at a time.
|
|
365
|
+
|
|
366
|
+
**Why it lives on the shell rather than being a class an app applies.** Because
|
|
367
|
+
that is the entire defect it fixes. Two apps had built this same picture
|
|
368
|
+
separately, under two names, in two `app.css` files, and one of them shipped it
|
|
369
|
+
as a helper class routes opt into, which reached four routes out of about
|
|
370
|
+
fifteen. Whether a given page wore the house atmosphere therefore came down to
|
|
371
|
+
which pages someone had happened to touch, and no amount of care at the call site
|
|
372
|
+
fixes that: a texture an app has to _remember_ to apply is not a texture, it is a
|
|
373
|
+
coin toss with a stylesheet. On the shell there is one place to say it and no
|
|
374
|
+
per-route decision left to get wrong.
|
|
375
|
+
|
|
376
|
+
It is one texture rather than a menu. The two apps wanted the same picture and
|
|
377
|
+
differed only on values: how dark the dots are, how the vignette is tinted, how
|
|
378
|
+
far apart the dots sit. Those are custom properties, retuned in one
|
|
379
|
+
declaration without waiting on a release of this package:
|
|
380
|
+
|
|
381
|
+
```css
|
|
382
|
+
:root {
|
|
383
|
+
--ds-shell-texture-grid-ink: oklch(0.68 0.02 250 / 0.5);
|
|
384
|
+
--ds-shell-texture-vignette-ink: color-mix(in oklch, var(--primary) 8%, transparent);
|
|
385
|
+
--ds-shell-texture-grid-pitch: 24px;
|
|
386
|
+
--ds-shell-texture-vignette-height: 40rem;
|
|
387
|
+
--ds-shell-texture-vignette-at: 15% -10%;
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
The defaults derive from the app's own `--foreground` and `--primary`, so the
|
|
392
|
+
grid inverts between light and dark with no per-mode override and the vignette
|
|
393
|
+
carries the app's brand rather than a colour this package picked. A plain wash
|
|
394
|
+
instead of a grid is the same declaration with `--ds-shell-texture-grid-ink: transparent`.
|
|
395
|
+
|
|
396
|
+
Three things worth knowing before you set it:
|
|
397
|
+
|
|
398
|
+
- **It is a background, not a layer.** Both apps that built this first reached for
|
|
399
|
+
an absolutely positioned `::before` inside the scroller, which is subtly wrong
|
|
400
|
+
three ways: at `z-index: auto` a positioned box paints _above_ non-positioned
|
|
401
|
+
content, so the atmosphere tints the page instead of sitting under it (invisible
|
|
402
|
+
only because it is faint); `z-index: -1` trades that for sinking behind an
|
|
403
|
+
ancestor's background; and it has to be excused from hit-testing by hand. A
|
|
404
|
+
background cannot be hit-tested, always paints beneath every descendant, adds no
|
|
405
|
+
box to the flex column and opens no stacking context. Proved rather than
|
|
406
|
+
claimed: an opaque card photographs byte-identically with the texture on and
|
|
407
|
+
off, while the floor beside it does not.
|
|
408
|
+
- **It does not print.** A 30px dot grid prints as banding and a vignette as a
|
|
409
|
+
corner smudge, so on paper the texture is suppressed and the content region goes
|
|
410
|
+
white. That lives here rather than in each app's print rules; it is the same
|
|
411
|
+
copy both apps had already written for themselves. `html`/`body` stay the app's
|
|
412
|
+
to decide.
|
|
413
|
+
- **It travels with the content.** A scroll container's background defaults to
|
|
414
|
+
being pinned to its own box, which would leave the dots hanging motionless while
|
|
415
|
+
the page slid over them; `background-attachment: local` makes it the floor the
|
|
416
|
+
page sits on. Driven by photographing bare floor either side of a half-pitch
|
|
417
|
+
scroll, with the frozen behaviour forced on as a control.
|
|
418
|
+
- **The corner is a knob because a gradient position is physical.** There is no
|
|
419
|
+
logical form of `at 85%`, so an RTL app wanting the glow at the reading-start
|
|
420
|
+
corner sets `--ds-shell-texture-vignette-at`. Driven at `dir=rtl`, at a 24px
|
|
421
|
+
root size, and under `forced-colors: active`; none of those move the texture on
|
|
422
|
+
their own.
|
|
423
|
+
|
|
424
|
+
Set it in the layout, once. That is the whole point of the prop.
|
|
425
|
+
|
|
426
|
+
### The active nav row: primary is a fill, never an ink
|
|
427
|
+
|
|
428
|
+
The active row is marked with a 12% `--ds-color-primary` tint, a primary edge
|
|
429
|
+
bar on the rail, a weight step to 500, and `aria-current="page"`.
|
|
430
|
+
Its **label** is painted in the chrome's own foreground, not in the brand colour.
|
|
431
|
+
|
|
432
|
+
That is deliberate. `--ds-color-primary` is the one token every app overrides,
|
|
433
|
+
and the only constraint stated where an app picks it is that it clear AA against
|
|
434
|
+
its own `-foreground` pair: the fill case. Painting it as ink on the chrome
|
|
435
|
+
would impose a second, stricter requirement that nothing states and that
|
|
436
|
+
constrains a brand hue far more tightly. Measured in Chromium, a warm amber that
|
|
437
|
+
is 7.69:1 as a fill was **1.90:1** as an active nav label, and a saturated blue
|
|
438
|
+
that is 5:1 as a fill was **2.87:1**. Both palettes were entirely sanctioned. So
|
|
439
|
+
the shell stops asking a colour it does not control to be legible text, and no
|
|
440
|
+
app is ever told to repaint its brand to make the shared shell readable.
|
|
441
|
+
|
|
442
|
+
An app whose primary genuinely is legible as ink on its chrome can put it back,
|
|
443
|
+
and owns the contrast in doing so:
|
|
444
|
+
|
|
445
|
+
```css
|
|
446
|
+
:root {
|
|
447
|
+
--ds-nav-ink-active: var(--ds-color-primary);
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Check that at ≥4.5:1 against `--ds-color-surface-1` (the chrome), not against
|
|
452
|
+
`--ds-color-background`; they are different surfaces, and the chrome is the
|
|
453
|
+
darker of the two. `harness/drive.mjs` gates the default path across three
|
|
454
|
+
palettes in both themes on every run.
|
|
455
|
+
|
|
456
|
+
### Measuring the content region
|
|
457
|
+
|
|
458
|
+
About overflow, not width — for how wide the body is allowed to get, see
|
|
459
|
+
[The content measure](#the-content-measure) above.
|
|
460
|
+
|
|
461
|
+
`<main>` is the scrolling content region and carries
|
|
462
|
+
`data-slot="app-shell-content"`. A consuming app's "no horizontal overflow" check
|
|
463
|
+
must measure **that element**, not the document:
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
const region = page.locator('[data-slot="app-shell-content"]');
|
|
467
|
+
expect(await region.evaluate((el) => el.scrollWidth - el.clientWidth)).toBeLessThanOrEqual(0);
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
`overflow-y: auto` makes `overflow-x` compute to `auto` too, so a wide child's
|
|
471
|
+
excess ends up in this box and never reaches
|
|
472
|
+
`document.documentElement.scrollWidth` — the number nearly every app's overflow
|
|
473
|
+
test reads, and one that cannot move. A page can scroll sideways on a phone with
|
|
474
|
+
that suite green throughout.
|
|
475
|
+
|
|
476
|
+
The shell constrains its own boxes, but a child wider than the region has to
|
|
477
|
+
carry its own scroller: `@poodle64/ui/table` does (`data-slot="table-container"`,
|
|
478
|
+
and `containerClass` if you need to cap its height for a sticky header), and a
|
|
479
|
+
`<pre>` or an unbreakable string needs one from the page.
|
|
480
|
+
|
|
481
|
+
The table is the TanStack-shaped pair the household actually runs, not a
|
|
482
|
+
`rows`/`columns` config API. The page owns the `Table` instance (built with
|
|
483
|
+
`createSvelteTable` from `@poodle64/ui/data-table`) and passes it in; the
|
|
484
|
+
component owns how it looks and how a row is picked. For a small static table
|
|
485
|
+
that does not earn a table instance, `@poodle64/ui/table` also exports the
|
|
486
|
+
`TH_CLASS` / `TD_CLASS` / `TH_HIDDEN_UNTIL_XL` constants for the raw-`<table>`
|
|
487
|
+
idiom.
|
|
488
|
+
|
|
489
|
+
A column's `meta.class` reaches both its `<th>` and its `<td>`; `meta.headClass`
|
|
490
|
+
/ `meta.cellClass` reach one cell only, additive on top of `class` (the
|
|
491
|
+
cell-specific slot wins on a conflicting Tailwind utility). Reach for the split
|
|
492
|
+
whenever a class must differ between the two — a truncating column needs
|
|
493
|
+
`max-w-0` on the `<td>` to ellipsis instead of pushing every other column out,
|
|
494
|
+
but the same class on the `<th>` collapses the heading over its neighbour:
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
{ accessorKey: 'filename', header: 'Document', meta: { class: 'w-full', cellClass: 'max-w-0' } }
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
## Consuming the package
|
|
501
|
+
|
|
502
|
+
Published to public npm under the `@poodle64` scope, same as `@poodle64/design-tokens`
|
|
503
|
+
— no registry config, no `.npmrc`, and no auth token needed to install.
|
|
504
|
+
|
|
505
|
+
```bash
|
|
506
|
+
pnpm add @poodle64/ui @poodle64/design-tokens
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
```svelte
|
|
510
|
+
<script lang="ts">
|
|
511
|
+
import { Button } from '@poodle64/ui/button';
|
|
512
|
+
import * as Dialog from '@poodle64/ui/dialog';
|
|
513
|
+
import PageHeader from '@poodle64/ui/page-header';
|
|
514
|
+
import StatusBadge from '@poodle64/ui/status-badge';
|
|
515
|
+
import type { Status } from '@poodle64/ui/status';
|
|
516
|
+
</script>
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
Composed components export both a default and a named binding, so either import
|
|
520
|
+
style works. `stat-list` also exports its `StatItem` type, and
|
|
521
|
+
`data-table-toolbar` its `ChipGroup` / `ChipSpec` types.
|
|
522
|
+
|
|
523
|
+
Each component is its own subpath export (`@poodle64/ui/<name>`), matching
|
|
524
|
+
shadcn-svelte's own convention; a flat barrel would collide on the generic names
|
|
525
|
+
(`Root`, `Content`, `Trigger`) that most primitives share.
|
|
526
|
+
|
|
527
|
+
**`CardTitle` takes an optional `level`.** It renders a `<div>` by default, as
|
|
528
|
+
upstream shadcn does, because a card title is not always a heading. Where it IS
|
|
529
|
+
the heading for that card's content — the common case on a dashboard — pass the
|
|
530
|
+
level and it becomes a real `<h1>`–`<h6>`:
|
|
531
|
+
|
|
532
|
+
```svelte
|
|
533
|
+
<Card.Title level={2}>Estate summary</Card.Title>
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Nothing else changes: the class list is identical either way, so `level` moves
|
|
537
|
+
the document outline and not a pixel. Worth knowing before a migration, because
|
|
538
|
+
the default is silent — an app replacing its own `<h3>` card titles with this
|
|
539
|
+
component loses every heading below its page `<h1>` with no error, no lint hit
|
|
540
|
+
and no visual difference.
|
|
541
|
+
|
|
542
|
+
**Two lines in the app's `app.css`**, after the token imports:
|
|
543
|
+
|
|
544
|
+
```css
|
|
545
|
+
@import '@poodle64/design-tokens/tokens.tw.css';
|
|
546
|
+
@import '@poodle64/design-tokens/tokens.css';
|
|
547
|
+
@import '@poodle64/ui/styles.css'; /* required: theme registration + component styles */
|
|
548
|
+
@source '../node_modules/@poodle64/ui/dist'; /* Tailwind content scan */
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
`@poodle64/ui/styles.css` is **required, not optional**. It carries two things.
|
|
552
|
+
|
|
553
|
+
First, the shadcn semantic surface: `--card`, `--popover`, `--muted`, `--accent`,
|
|
554
|
+
`--secondary`, `--input` and their `-foreground` pairs, each mapped to a `--ds-*`
|
|
555
|
+
token and registered with Tailwind so `bg-card`, `bg-muted`, `border-input` and
|
|
556
|
+
friends actually generate CSS. Declaring those names as plain custom properties
|
|
557
|
+
is not enough: the variable exists, Tailwind still does not know it is a colour,
|
|
558
|
+
and the utility compiles to no rule at all. That is a silent failure with no
|
|
559
|
+
build error, no lint hit and no failing test, which is exactly how it went
|
|
560
|
+
unnoticed through a full app migration (#3).
|
|
561
|
+
|
|
562
|
+
Second, what the variable layer does not cover: the `text-display` / `text-body` /
|
|
563
|
+
`text-stat` / `tracking-eyebrow` scale keys, the `.ds-edge` card treatment, the
|
|
564
|
+
`.ds-dialog-section` divider rule, the `.ds-chip` / `.ds-dot` / `.ds-ink` status
|
|
565
|
+
classes, and the two things that make this package's overlays animate at all —
|
|
566
|
+
`@custom-variant data-open` / `data-closed` (bits-ui emits `data-state="open"`,
|
|
567
|
+
so a bare `data-open:` utility matches nothing without them) and an import of
|
|
568
|
+
`tw-animate-css`, which defines `animate-in`, `fade-in-0`, `zoom-in-95` and
|
|
569
|
+
`slide-in-from-*`. Both used to be the app's job, and an app that never did the
|
|
570
|
+
job got dialogues, dropdowns, popovers and selects that opened with no
|
|
571
|
+
transition, silently. A consuming app now declares neither; one that already
|
|
572
|
+
declares them loses nothing, since the definitions are identical.
|
|
573
|
+
|
|
574
|
+
It holds no palette. Every value resolves through a `--ds-*` token, so choosing a
|
|
575
|
+
palette stays a matter of overriding `--ds-color-*` in your own `app.css`, and
|
|
576
|
+
`.dark` flows through untouched. Any single name can still be overridden by
|
|
577
|
+
re-declaring it after the import, which is now a deliberate deviation rather than
|
|
578
|
+
setup every app has to repeat.
|
|
579
|
+
|
|
580
|
+
Sidebar and chart colours are not registered here: this package ships no sidebar
|
|
581
|
+
or chart component, and those are the one part of the surface apps genuinely
|
|
582
|
+
differ on. An app using them registers them itself.
|
|
583
|
+
|
|
584
|
+
`@source` is needed because the package's Tailwind classes live in
|
|
585
|
+
`node_modules/@poodle64/ui/dist`, outside the app's own `src/`, so the default
|
|
586
|
+
source scan misses them. Without it the components render unstyled (no build
|
|
587
|
+
error, no lint hit; the classes just never reach the compiled CSS).
|
|
588
|
+
|
|
589
|
+
## Verifying a change
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
pnpm test # build + vitest: jsdom interaction, and compiled-CSS gates
|
|
593
|
+
pnpm run check # svelte-check
|
|
594
|
+
pnpm run test:browser # build the harness and drive it in a real browser
|
|
595
|
+
|
|
596
|
+
# Before releasing a change that claims to be additive (minutes, not seconds):
|
|
597
|
+
node harness/additivity.mjs ui-v2026.8.3
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
`pnpm test` includes the gates that compile the built package through the real
|
|
601
|
+
Tailwind chain, exactly as a consuming app wires it — a class that generates no
|
|
602
|
+
rule, a colour that resolves to nothing, a registered theme key nothing reads, a
|
|
603
|
+
variant whose selector cannot match the DOM, and a sizing utility that has
|
|
604
|
+
silently changed meaning all fail there rather than shipping.
|
|
605
|
+
|
|
606
|
+
`pnpm run test:browser` is the leg nothing else can stand in for: jsdom applies
|
|
607
|
+
no stylesheet and returns unresolved `var(…)` literals, so it passes just as
|
|
608
|
+
happily on a dead animation, an unregistered colour and a layout that does not
|
|
609
|
+
exist. `harness/drive.md` records what is asserted, what each defect looked like
|
|
610
|
+
before it was caught, and the measurement traps worth inheriting.
|
|
611
|
+
|
|
612
|
+
`harness/additivity.mjs` is the leg that runs across two builds. Every additive
|
|
613
|
+
prop this package has added promised the same thing — that a consumer who never
|
|
614
|
+
names it renders identically — and that promise used to be checked by hand and
|
|
615
|
+
written up as prose nobody could re-derive. It now builds the base ref in a
|
|
616
|
+
throwaway worktree and diffs the markup, the computed boxes, the geometry and the
|
|
617
|
+
pixels of the surfaces a consumer gets without opting into anything, exiting
|
|
618
|
+
non-zero and naming the field on any difference.
|
|
619
|
+
|
|
620
|
+
## Releasing
|
|
621
|
+
|
|
622
|
+
1. Change a component; bump `version` in `package.json` (CalVer).
|
|
623
|
+
2. `pnpm build`, which runs `svelte-package` then `publint` (package.json/exports sanity).
|
|
624
|
+
3. Commit, tag `ui-v<version>`, push the tag; CI publishes to public npm.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { cn, type WithElementRef } from '../../../utils';
|
|
4
|
+
import type { Snippet } from 'svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
children,
|
|
10
|
+
...restProps
|
|
11
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
} = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<div
|
|
17
|
+
bind:this={ref}
|
|
18
|
+
data-slot="alert-description"
|
|
19
|
+
class={cn('text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm', className)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
import { type WithElementRef } from '../../../utils';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
};
|
|
7
|
+
declare const AlertDescription: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
8
|
+
type AlertDescription = ReturnType<typeof AlertDescription>;
|
|
9
|
+
export default AlertDescription;
|