zabi-components 8.0.0 → 8.1.0
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/CHANGELOG.md +1924 -0
- package/README.md +243 -10
- package/THEME.md +149 -332
- package/THEMING.md +1192 -266
- package/dist/atoms/ActionPanel.svelte +15 -5
- package/dist/atoms/Avatar.svelte +104 -0
- package/dist/atoms/Avatar.svelte.d.ts +34 -0
- package/dist/atoms/Badge.svelte +36 -7
- package/dist/atoms/Badge.svelte.d.ts +2 -2
- package/dist/atoms/Button.svelte +177 -27
- package/dist/atoms/Button.svelte.d.ts +30 -2
- package/dist/atoms/Card.svelte +4 -4
- package/dist/atoms/CardHeader.svelte +16 -4
- package/dist/atoms/Checkbox.svelte +48 -21
- package/dist/atoms/Checkbox.svelte.d.ts +4 -2
- package/dist/atoms/CodeBlock.svelte +20 -2
- package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
- package/dist/atoms/ColorPicker.svelte +259 -49
- package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
- package/dist/atoms/Container.svelte +16 -8
- package/dist/atoms/DateField.svelte +107 -0
- package/dist/atoms/DateField.svelte.d.ts +53 -0
- package/dist/atoms/FieldMessages.svelte +69 -0
- package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
- package/dist/atoms/FloatingActionButton.svelte +174 -0
- package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
- package/dist/atoms/Heading.svelte +37 -5
- package/dist/atoms/Heading.svelte.d.ts +7 -0
- package/dist/atoms/IconButton.svelte +220 -24
- package/dist/atoms/IconButton.svelte.d.ts +49 -4
- package/dist/atoms/Input.svelte +253 -52
- package/dist/atoms/Input.svelte.d.ts +32 -2
- package/dist/atoms/List.svelte +4 -1
- package/dist/atoms/ListItem.svelte +44 -11
- package/dist/atoms/ListItem.svelte.d.ts +5 -0
- package/dist/atoms/Progress.svelte +27 -8
- package/dist/atoms/Progress.svelte.d.ts +4 -0
- package/dist/atoms/Radio.svelte +19 -3
- package/dist/atoms/Radio.svelte.d.ts +4 -2
- package/dist/atoms/Rating.svelte +494 -0
- package/dist/atoms/Rating.svelte.d.ts +56 -0
- package/dist/atoms/Select.svelte +433 -91
- package/dist/atoms/Select.svelte.d.ts +49 -3
- package/dist/atoms/SelectionControl.svelte +94 -17
- package/dist/atoms/SelectionControl.svelte.d.ts +4 -3
- package/dist/atoms/Skeleton.svelte +1 -1
- package/dist/atoms/Slider.svelte +309 -0
- package/dist/atoms/Slider.svelte.d.ts +40 -0
- package/dist/atoms/Spinner.svelte +57 -0
- package/dist/atoms/Spinner.svelte.d.ts +19 -0
- package/dist/atoms/Table.svelte +151 -6
- package/dist/atoms/Table.svelte.d.ts +15 -3
- package/dist/atoms/TemporalField.svelte +140 -0
- package/dist/atoms/TemporalField.svelte.d.ts +32 -0
- package/dist/atoms/Text.svelte +23 -11
- package/dist/atoms/Text.svelte.d.ts +6 -3
- package/dist/atoms/Textarea.svelte +55 -44
- package/dist/atoms/Textarea.svelte.d.ts +14 -2
- package/dist/atoms/ThemeToggle.svelte +223 -44
- package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
- package/dist/atoms/TimeField.svelte +108 -0
- package/dist/atoms/TimeField.svelte.d.ts +54 -0
- package/dist/atoms/Toast.svelte +17 -4
- package/dist/atoms/Toast.svelte.d.ts +2 -0
- package/dist/atoms/Toggle.svelte +41 -8
- package/dist/atoms/Toggle.svelte.d.ts +11 -2
- package/dist/atoms/Tooltip.svelte +508 -23
- package/dist/atoms/Tooltip.svelte.d.ts +11 -0
- package/dist/atoms/ZabiStringsProvider.svelte +45 -0
- package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
- package/dist/atoms/index.d.ts +17 -0
- package/dist/atoms/index.js +12 -0
- package/dist/atoms/selection-control.styles.d.ts +36 -5
- package/dist/atoms/selection-control.styles.js +40 -9
- package/dist/components/atoms/index.d.ts +17 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +36 -5
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +80 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +46 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +53 -7
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +5 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +15 -1
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +28 -0
- package/dist/components/util/app-shell.d.ts.map +1 -0
- package/dist/components/util/avatar.d.ts +36 -0
- package/dist/components/util/avatar.d.ts.map +1 -0
- package/dist/components/util/bottom-sheet.d.ts +30 -0
- package/dist/components/util/bottom-sheet.d.ts.map +1 -0
- package/dist/components/util/bottom-tab-bar.d.ts +35 -0
- package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
- package/dist/components/util/calendar.d.ts +56 -0
- package/dist/components/util/calendar.d.ts.map +1 -0
- package/dist/components/util/collapsible.d.ts +58 -0
- package/dist/components/util/collapsible.d.ts.map +1 -0
- package/dist/components/util/confirm-dialog.d.ts +12 -0
- package/dist/components/util/confirm-dialog.d.ts.map +1 -0
- package/dist/components/util/date.d.ts +96 -0
- package/dist/components/util/date.d.ts.map +1 -0
- package/dist/components/util/drawer.d.ts +13 -0
- package/dist/components/util/drawer.d.ts.map +1 -0
- package/dist/components/util/dropdown.d.ts +47 -0
- package/dist/components/util/dropdown.d.ts.map +1 -0
- package/dist/components/util/field.d.ts +39 -0
- package/dist/components/util/field.d.ts.map +1 -0
- package/dist/components/util/fit-in-viewport.d.ts +201 -0
- package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
- package/dist/components/util/focus-utils.d.ts +75 -2
- package/dist/components/util/focus-utils.d.ts.map +1 -1
- package/dist/components/util/hydration.d.ts +45 -0
- package/dist/components/util/hydration.d.ts.map +1 -0
- package/dist/components/util/keyboard-inset.d.ts +18 -0
- package/dist/components/util/keyboard-inset.d.ts.map +1 -0
- package/dist/components/util/media-grid.d.ts +51 -0
- package/dist/components/util/media-grid.d.ts.map +1 -0
- package/dist/components/util/media.d.ts +6 -0
- package/dist/components/util/media.d.ts.map +1 -0
- package/dist/components/util/overlay-chrome.d.ts +53 -0
- package/dist/components/util/overlay-chrome.d.ts.map +1 -0
- package/dist/components/util/overlay.d.ts +72 -0
- package/dist/components/util/overlay.d.ts.map +1 -0
- package/dist/components/util/photo.d.ts +215 -0
- package/dist/components/util/photo.d.ts.map +1 -0
- package/dist/components/util/portal.d.ts +23 -0
- package/dist/components/util/portal.d.ts.map +1 -0
- package/dist/components/util/pull-to-refresh.d.ts +27 -0
- package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
- package/dist/components/util/radio-keys.d.ts +25 -0
- package/dist/components/util/radio-keys.d.ts.map +1 -0
- package/dist/components/util/rating.d.ts +23 -0
- package/dist/components/util/rating.d.ts.map +1 -0
- package/dist/components/util/ready-made-strings.d.ts +77 -0
- package/dist/components/util/ready-made-strings.d.ts.map +1 -0
- package/dist/components/util/scroll-padding.d.ts +20 -0
- package/dist/components/util/scroll-padding.d.ts.map +1 -0
- package/dist/components/util/segmented-control.d.ts +16 -0
- package/dist/components/util/segmented-control.d.ts.map +1 -0
- package/dist/components/util/select.d.ts +41 -0
- package/dist/components/util/select.d.ts.map +1 -0
- package/dist/components/util/sheet-drag.d.ts +60 -0
- package/dist/components/util/sheet-drag.d.ts.map +1 -0
- package/dist/components/util/sidebar.d.ts +63 -0
- package/dist/components/util/sidebar.d.ts.map +1 -0
- package/dist/components/util/sortable-list.d.ts +65 -0
- package/dist/components/util/sortable-list.d.ts.map +1 -0
- package/dist/components/util/stepper.d.ts +40 -0
- package/dist/components/util/stepper.d.ts.map +1 -0
- package/dist/components/util/swipeable-list-item.d.ts +36 -0
- package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
- package/dist/components/util/theme-mode.d.ts +80 -0
- package/dist/components/util/theme-mode.d.ts.map +1 -0
- package/dist/components/util/toaster.d.ts +93 -0
- package/dist/components/util/toaster.d.ts.map +1 -0
- package/dist/components/util/tooltip.d.ts +10 -0
- package/dist/components/util/tooltip.d.ts.map +1 -0
- package/dist/components/util/top-navbar.d.ts +11 -0
- package/dist/components/util/top-navbar.d.ts.map +1 -0
- package/dist/components/util/touch-target.d.ts +31 -0
- package/dist/components/util/touch-target.d.ts.map +1 -0
- package/dist/components/util/zabi-strings.d.ts +133 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -0
- package/dist/create-theme/cli.js +169 -0
- package/dist/create-theme/index.d.ts +176 -0
- package/dist/create-theme/index.js +817 -0
- package/dist/create-theme/lib/ramp-math.js +128 -0
- package/dist/create-theme/lib/resolve.js +141 -0
- package/dist/create-theme/theme-data.js +1116 -0
- package/dist/index.d.ts +80 -2
- package/dist/index.js +57 -2
- package/dist/molecules/Alert.svelte +33 -5
- package/dist/molecules/Alert.svelte.d.ts +2 -0
- package/dist/molecules/AppBar.svelte +415 -0
- package/dist/molecules/AppBar.svelte.d.ts +69 -0
- package/dist/molecules/AvatarGroup.svelte +116 -0
- package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
- package/dist/molecules/BottomSheet.svelte +540 -0
- package/dist/molecules/BottomSheet.svelte.d.ts +93 -0
- package/dist/molecules/BottomTabBar.svelte +297 -0
- package/dist/molecules/BottomTabBar.svelte.d.ts +64 -0
- package/dist/molecules/Calendar.svelte +597 -0
- package/dist/molecules/Calendar.svelte.d.ts +72 -0
- package/dist/molecules/Collapsible.svelte +243 -0
- package/dist/molecules/Collapsible.svelte.d.ts +44 -0
- package/dist/molecules/CollapsibleGroup.svelte +126 -0
- package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
- package/dist/molecules/ComponentDemo.svelte +16 -4
- package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
- package/dist/molecules/ConfirmDialog.svelte +253 -0
- package/dist/molecules/ConfirmDialog.svelte.d.ts +55 -0
- package/dist/molecules/ContactForm.svelte +34 -17
- package/dist/molecules/ContactForm.svelte.d.ts +8 -0
- package/dist/molecules/Drawer.svelte +394 -0
- package/dist/molecules/Drawer.svelte.d.ts +55 -0
- package/dist/molecules/Dropdown.svelte +509 -48
- package/dist/molecules/Dropdown.svelte.d.ts +45 -6
- package/dist/molecules/DropdownItem.svelte +122 -0
- package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
- package/dist/molecules/EmptyState.svelte +55 -10
- package/dist/molecules/EmptyState.svelte.d.ts +8 -0
- package/dist/molecules/FormField.svelte +9 -1
- package/dist/molecules/FormField.svelte.d.ts +2 -0
- package/dist/molecules/Header.svelte +1 -1
- package/dist/molecules/ImageUpload.svelte +383 -70
- package/dist/molecules/ImageUpload.svelte.d.ts +47 -2
- package/dist/molecules/MediaGrid.svelte +527 -0
- package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
- package/dist/molecules/Modal.svelte +288 -72
- package/dist/molecules/Modal.svelte.d.ts +51 -2
- package/dist/molecules/NavigationMenuContent.svelte +58 -1
- package/dist/molecules/NavigationMenuItem.svelte +0 -1
- package/dist/molecules/NavigationMenuLink.svelte +1 -1
- package/dist/molecules/NavigationMenuList.svelte +10 -3
- package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
- package/dist/molecules/Page.svelte +32 -2
- package/dist/molecules/Page.svelte.d.ts +11 -0
- package/dist/molecules/PhotoGrid.svelte +433 -0
- package/dist/molecules/PhotoGrid.svelte.d.ts +62 -0
- package/dist/molecules/PhotoViewer.svelte +1116 -0
- package/dist/molecules/PhotoViewer.svelte.d.ts +56 -0
- package/dist/molecules/PropsTable.svelte +22 -10
- package/dist/molecules/PropsTable.svelte.d.ts +3 -0
- package/dist/molecules/PullToRefresh.svelte +263 -0
- package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
- package/dist/molecules/RadioGroup.svelte +52 -14
- package/dist/molecules/RadioGroup.svelte.d.ts +14 -3
- package/dist/molecules/Section.svelte +1 -1
- package/dist/molecules/SegmentedControl.svelte +323 -0
- package/dist/molecules/SegmentedControl.svelte.d.ts +34 -0
- package/dist/molecules/SidebarBrandHeader.svelte +11 -1
- package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
- package/dist/molecules/SidebarFooter.svelte +29 -6
- package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
- package/dist/molecules/SlideUp.svelte +220 -61
- package/dist/molecules/SlideUp.svelte.d.ts +26 -0
- package/dist/molecules/SortableList.svelte +632 -0
- package/dist/molecules/SortableList.svelte.d.ts +69 -0
- package/dist/molecules/Stepper.svelte +548 -0
- package/dist/molecules/Stepper.svelte.d.ts +39 -0
- package/dist/molecules/StickyActionBar.svelte +191 -0
- package/dist/molecules/StickyActionBar.svelte.d.ts +65 -0
- package/dist/molecules/SwipeableListItem.svelte +334 -0
- package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
- package/dist/molecules/Tabs.svelte +233 -9
- package/dist/molecules/Tabs.svelte.d.ts +7 -0
- package/dist/molecules/Toaster.svelte +350 -5
- package/dist/molecules/Toaster.svelte.d.ts +95 -2
- package/dist/molecules/ToasterToast.svelte +253 -67
- package/dist/molecules/ToasterToast.svelte.d.ts +9 -0
- package/dist/molecules/UnsavedChangesBar.svelte +212 -0
- package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
- package/dist/molecules/index.d.ts +46 -0
- package/dist/molecules/index.js +27 -0
- package/dist/molecules/toast-store.d.ts +53 -7
- package/dist/molecules/toast-store.js +21 -0
- package/dist/organisms/AppShell.svelte +196 -0
- package/dist/organisms/AppShell.svelte.d.ts +61 -0
- package/dist/organisms/SidebarAccountPanel.svelte +107 -12
- package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
- package/dist/organisms/SidebarNavigation.svelte +96 -15
- package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
- package/dist/organisms/SidebarPanel.svelte +31 -10
- package/dist/organisms/SidebarShell.svelte +162 -11
- package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
- package/dist/organisms/TopNavbar.svelte +256 -31
- package/dist/organisms/TopNavbar.svelte.d.ts +26 -0
- package/dist/organisms/index.d.ts +5 -0
- package/dist/organisms/index.js +3 -0
- package/dist/types/components.d.ts +4 -0
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/index.d.ts +605 -25
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/variants.ts +17 -1
- package/dist/util/app-shell.d.ts +27 -0
- package/dist/util/app-shell.js +72 -0
- package/dist/util/avatar.d.ts +35 -0
- package/dist/util/avatar.js +52 -0
- package/dist/util/bottom-sheet.d.ts +29 -0
- package/dist/util/bottom-sheet.js +43 -0
- package/dist/util/bottom-tab-bar.d.ts +34 -0
- package/dist/util/bottom-tab-bar.js +54 -0
- package/dist/util/calendar.d.ts +55 -0
- package/dist/util/calendar.js +74 -0
- package/dist/util/collapsible.d.ts +57 -0
- package/dist/util/collapsible.js +23 -0
- package/dist/util/confirm-dialog.d.ts +11 -0
- package/dist/util/confirm-dialog.js +2 -0
- package/dist/util/date.d.ts +95 -0
- package/dist/util/date.js +237 -0
- package/dist/util/drawer.d.ts +12 -0
- package/dist/util/drawer.js +7 -0
- package/dist/util/dropdown.d.ts +46 -0
- package/dist/util/dropdown.js +21 -0
- package/dist/util/field.d.ts +38 -0
- package/dist/util/field.js +38 -0
- package/dist/util/fit-in-viewport.d.ts +200 -0
- package/dist/util/fit-in-viewport.js +399 -0
- package/dist/util/focus-utils.d.ts +75 -2
- package/dist/util/focus-utils.js +182 -9
- package/dist/util/hydration.d.ts +44 -0
- package/dist/util/hydration.js +58 -0
- package/dist/util/keyboard-inset.d.ts +17 -0
- package/dist/util/keyboard-inset.js +53 -0
- package/dist/util/media-grid.d.ts +50 -0
- package/dist/util/media-grid.js +81 -0
- package/dist/util/media.d.ts +5 -0
- package/dist/util/media.js +10 -0
- package/dist/util/overlay-chrome.d.ts +52 -0
- package/dist/util/overlay-chrome.js +56 -0
- package/dist/util/overlay.d.ts +71 -0
- package/dist/util/overlay.js +240 -0
- package/dist/util/photo.d.ts +214 -0
- package/dist/util/photo.js +206 -0
- package/dist/util/portal.d.ts +22 -0
- package/dist/util/portal.js +50 -0
- package/dist/util/pull-to-refresh.d.ts +26 -0
- package/dist/util/pull-to-refresh.js +30 -0
- package/dist/util/radio-keys.d.ts +24 -0
- package/dist/util/radio-keys.js +49 -0
- package/dist/util/rating.d.ts +22 -0
- package/dist/util/rating.js +26 -0
- package/dist/util/ready-made-strings.d.ts +76 -0
- package/dist/util/ready-made-strings.js +67 -0
- package/dist/util/scroll-padding.d.ts +19 -0
- package/dist/util/scroll-padding.js +44 -0
- package/dist/util/segmented-control.d.ts +15 -0
- package/dist/util/segmented-control.js +1 -0
- package/dist/util/select.d.ts +40 -0
- package/dist/util/select.js +63 -0
- package/dist/util/sheet-drag.d.ts +59 -0
- package/dist/util/sheet-drag.js +241 -0
- package/dist/util/sidebar.d.ts +62 -0
- package/dist/util/sidebar.js +38 -0
- package/dist/util/sortable-list.d.ts +64 -0
- package/dist/util/sortable-list.js +53 -0
- package/dist/util/stepper.d.ts +39 -0
- package/dist/util/stepper.js +38 -0
- package/dist/util/swipeable-list-item.d.ts +35 -0
- package/dist/util/swipeable-list-item.js +32 -0
- package/dist/util/theme-mode.d.ts +79 -0
- package/dist/util/theme-mode.js +121 -0
- package/dist/util/toaster.d.ts +92 -0
- package/dist/util/toaster.js +75 -0
- package/dist/util/tooltip.d.ts +9 -0
- package/dist/util/tooltip.js +18 -0
- package/dist/util/top-navbar.d.ts +10 -0
- package/dist/util/top-navbar.js +5 -0
- package/dist/util/touch-target.d.ts +30 -0
- package/dist/util/touch-target.js +30 -0
- package/dist/util/zabi-strings.d.ts +132 -0
- package/dist/util/zabi-strings.js +105 -0
- package/dist/zabi-components-colors.css +5 -2
- package/dist/zabi-components-theme-dark-only.css +543 -241
- package/dist/zabi-components-theme-dark.css +543 -241
- package/dist/zabi-components-theme-only.css +1584 -367
- package/dist/zabi-components-theme.css +1584 -367
- package/dist/zabi-components.css +3885 -518
- package/docs/lucide-icons.md +40 -17
- package/docs/theme-imports.md +81 -11
- package/package.json +35 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,1924 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## Changelog policy (theme/token surface)
|
|
6
|
+
|
|
7
|
+
Whenever token or CSS import API surface changes, include:
|
|
8
|
+
- exported path additions/removals/deprecations (`package.json` `exports`)
|
|
9
|
+
- token rename/add/remove (for `--color-*` and `--zabi-*`)
|
|
10
|
+
- mapping rule updates (for example dark semantic mapping)
|
|
11
|
+
- migration guidance when compatibility aliases remain temporarily
|
|
12
|
+
|
|
13
|
+
## [Unreleased]
|
|
14
|
+
|
|
15
|
+
## [8.1.0] - 2026-10-06
|
|
16
|
+
|
|
17
|
+
8.1.0 adds a theme generator (`zabi-theme`, `createTheme`) and an accent colour,
|
|
18
|
+
a mobile set (AppShell, AppBar, BottomTabBar, BottomSheet, Drawer, Select as a
|
|
19
|
+
sheet, safe areas, 44px touch targets) and more than 25 new components. Form fields,
|
|
20
|
+
focus rings and light-mode surfaces are easier to see, and `strings` and
|
|
21
|
+
`ZabiStringsProvider` replace every built-in English text. Nothing from 8.0.0
|
|
22
|
+
was removed or renamed: every `exports` path, prop and token is still there.
|
|
23
|
+
8.0.1 was never published, so everything in the 8.0.1 section below (valid theme
|
|
24
|
+
files, the three-line Tailwind setup working, ColorPicker on the server,
|
|
25
|
+
Progress named) is part of 8.1.0.
|
|
26
|
+
|
|
27
|
+
### Upgrade notes
|
|
28
|
+
|
|
29
|
+
Coming from 8.0.0, these are visible without a code change. Each bullet says
|
|
30
|
+
what to do.
|
|
31
|
+
|
|
32
|
+
**Appearance**
|
|
33
|
+
|
|
34
|
+
- **Form field edges are darker.** `--color-input-border` is `base-500` in
|
|
35
|
+
light and dark (was `base-350` light, `base-300` dark) and
|
|
36
|
+
`--color-input-border-hover` is `base-550` in light. The edge is guarded at
|
|
37
|
+
3:1 against the field fill, the page and the card, in both themes. A theme
|
|
38
|
+
of yours that overrides the edge can now report a pair below 3:1 in the
|
|
39
|
+
generator check. The dark field edge on the overlay surface (sheets, modals)
|
|
40
|
+
is about 2:1 against the panel and is not guarded. Nothing to do unless you
|
|
41
|
+
override the edge.
|
|
42
|
+
- **`Progress` uses new tokens.** The fill is `--color-progress-fill` (default
|
|
43
|
+
`--color-action-primary`) and the track `--color-progress-track` (default
|
|
44
|
+
`--color-input`); utilities `bg-progress-fill` and `bg-progress-track`. The
|
|
45
|
+
fill used to be the hard-coded brand step 600, so a bar changes colour in an
|
|
46
|
+
app that pins a primary action, and in dark. Fill on track is guarded at
|
|
47
|
+
3:1. Set the two tokens to keep the old colour.
|
|
48
|
+
- **Light mode has a visible surface ladder, stronger tints and shadows.** See
|
|
49
|
+
Changed: the page, cards, tinted fills, hover fills, overlay edge and shadow
|
|
50
|
+
all move. A card is 1.18:1 on the page (was 1.10). Check screenshots; no
|
|
51
|
+
token was renamed.
|
|
52
|
+
- **Focus rings are one step darker in light and lighter in dark.**
|
|
53
|
+
`--color-focus` is `brand-600` in both themes (light was `brand-500` at
|
|
54
|
+
2.99:1 on the page; dark was `brand-500`, 2.91:1 on the overlay; dark is
|
|
55
|
+
#92a9ff by default). The muted ring in dark is one step lighter
|
|
56
|
+
(`--color-focus-ring-muted`, `base-600`).
|
|
57
|
+
- **The computed `box-shadow` of a focused element lists five shadows**, not
|
|
58
|
+
two, because the ring now composes with shadow utilities. A test that reads
|
|
59
|
+
the ring colour from it should pick the 4px spread.
|
|
60
|
+
- **Dark surfaces are `color-mix()` expressions** over `--zabi-base-*`
|
|
61
|
+
(`--color-surface-raised`, `-elevated`, `-overlay`). The pixels are the same;
|
|
62
|
+
`getComputedStyle` reports `color(srgb …)`, not `rgb(…)`, so a test that
|
|
63
|
+
compares the string needs updating.
|
|
64
|
+
- **Placeholder text is one step stronger** in both themes
|
|
65
|
+
(`--color-input-placeholder`: #61616a light, #a1a1aa dark; 4.5:1 or more on
|
|
66
|
+
the resting, hovered, focused and pressed field).
|
|
67
|
+
- **Checked checkboxes and radios use the primary action fill**, with the
|
|
68
|
+
primary button's text colour on the tick and dot.
|
|
69
|
+
- **Dropdown menus, Select lists, the ColorPicker popover and NavigationMenu
|
|
70
|
+
panels have 16px corners** (were 8px), and other nested corners are
|
|
71
|
+
concentric: the Card in a Modal, MediaGrid's check mark, Alert's close
|
|
72
|
+
button, rows in a `.list-group`, the avatar in SidebarFooter's profile
|
|
73
|
+
button.
|
|
74
|
+
- **`font-sans` and `font-mono` follow the theme's font tokens**
|
|
75
|
+
(`--font-family-sans`, `--font-family-mono`), not Tailwind's system stacks.
|
|
76
|
+
If you used `font-sans` to get the system font, write the stack yourself.
|
|
77
|
+
- **`color-scheme` comes from the theme**: `light` by default and for
|
|
78
|
+
`data-theme="light"`, `dark` under `.dark` and `data-theme="dark"` (also when
|
|
79
|
+
both `class="dark"` and `data-theme="light"` are set). Native controls,
|
|
80
|
+
scrollbars, date pickers and autofill follow the theme. A
|
|
81
|
+
`<meta name="color-scheme">` tag is not needed, and a `color-scheme` rule of
|
|
82
|
+
your own must come after the theme import to win.
|
|
83
|
+
|
|
84
|
+
**Markup, roles and accessible names**
|
|
85
|
+
|
|
86
|
+
- **A `Select` is found by role `combobox`, not `button`.** Its trigger has
|
|
87
|
+
`role="combobox"` and reports `aria-invalid`, `aria-required` and
|
|
88
|
+
`aria-busy`. Its form control is a native `<select name>` (not a hidden
|
|
89
|
+
input), so code that looked for `input[type=hidden]` must look for the
|
|
90
|
+
select, and `required` now blocks the form. A form reset returns the Select
|
|
91
|
+
to the value it was rendered with. Update tests and selectors.
|
|
92
|
+
- **A loading `Button` or `IconButton` is `aria-disabled="true"` and
|
|
93
|
+
`aria-busy="true"`, not `disabled`.** It keeps keyboard focus and swallows
|
|
94
|
+
the press. CSS or tests that matched it with `:disabled`, `[disabled]` or a
|
|
95
|
+
strict `toBeDisabled()` must use `[aria-busy="true"]`.
|
|
96
|
+
- **`ThemeToggle` (two modes) has the constant name "Dark mode"** with
|
|
97
|
+
`aria-pressed`, not "Switch to dark mode" / "Switch to light mode"; update
|
|
98
|
+
tests and scripts that find the button by those names. It applies the stored
|
|
99
|
+
choice when it mounts (`storageKey={null}` turns that off) and no longer
|
|
100
|
+
writes an inline `color-scheme`.
|
|
101
|
+
- **`NavigationMenu` is a list of links and disclosure buttons**, not a
|
|
102
|
+
`menubar`: the list has `role="list"` and items carry no menu roles. Screen
|
|
103
|
+
readers announce it differently; keyboard use is unchanged.
|
|
104
|
+
- **A `List` row with no `href` in a List with no `onclick` is plain content**,
|
|
105
|
+
not a focusable button with an arrow.
|
|
106
|
+
- **Checkbox and Radio inputs are stretched invisibly over their box** instead
|
|
107
|
+
of being visually hidden, so `getByRole("checkbox").check()` works.
|
|
108
|
+
- **An unnamed `RadioGroup` submits nothing.** Give it a `name`. (The
|
|
109
|
+
generated name still groups the radios.)
|
|
110
|
+
- **A disabled `Dropdown` or `Select` option is `aria-disabled`, not
|
|
111
|
+
`disabled`**: reachable with the arrow keys, announced as disabled, not
|
|
112
|
+
selectable.
|
|
113
|
+
- A Select list and a Dropdown menu are one Tab stop (roving `tabindex`, 0 or
|
|
114
|
+
-1). `getFocusableElements` skips `tabindex="-1"`.
|
|
115
|
+
|
|
116
|
+
**Defaults and behaviour**
|
|
117
|
+
|
|
118
|
+
- **A binding that starts `undefined` on a bindable prop is now written back
|
|
119
|
+
as the default** (`""`, `false`, `null`). `let value = $state()` works with
|
|
120
|
+
`bind:value` everywhere; no bindable prop has a fallback expression any
|
|
121
|
+
more.
|
|
122
|
+
- **Controls are at least 44px on touch screens** (`pointer: coarse`), so
|
|
123
|
+
layouts there get taller (a checkbox list at an 8px gap is 52px a row).
|
|
124
|
+
Nothing changes with a mouse.
|
|
125
|
+
- **Every pressable control shows a `:active` pressed state** that does not
|
|
126
|
+
depend on hover. Hand-written `:hover` rules apply only under
|
|
127
|
+
`@media (hover: hover)`, so a tapped button no longer sticks in its hover
|
|
128
|
+
colour.
|
|
129
|
+
- **`Page` pads for the safe areas by default** (`safeArea`). An app that sets
|
|
130
|
+
`viewport-fit=cover` and already pads for the insets itself should pass
|
|
131
|
+
`safeArea={false}`. Without `viewport-fit=cover` the insets are zero and
|
|
132
|
+
nothing changes.
|
|
133
|
+
- **`Select` opens as a bottom sheet on a phone** (a touch screen narrower than
|
|
134
|
+
640px; `presentation="auto"`). Pass `presentation="popover"` to keep the
|
|
135
|
+
pop-over. It fills its container like Input (a Select in a flex row takes the
|
|
136
|
+
free width; give it a width such as `class="w-48"`, which is honoured now),
|
|
137
|
+
and a label too long for the trigger wraps instead of being cut.
|
|
138
|
+
`maxMenuHeight` defaults to `60dvh` (was `60vh`).
|
|
139
|
+
- **`Button` labels wrap.** A label that does not fit goes onto a second line
|
|
140
|
+
and the button grows (`min-height` instead of a fixed height); in a tight
|
|
141
|
+
flex row a button may shrink and wrap where it held its width.
|
|
142
|
+
- **Toasts show your message.** A toast with a `message` and no `title` shows
|
|
143
|
+
the message, not a default heading; only a toast with `detail` can be
|
|
144
|
+
expanded; the countdown sentence and "Click to stop" are off unless
|
|
145
|
+
`showCountdown`. A toast with no `duration` closes after 7 s (14 s when its
|
|
146
|
+
text is 121 to 240 characters, stays when longer); an error toast and a
|
|
147
|
+
toast with an action stay until dismissed. On touch, `onpausechange` reports
|
|
148
|
+
`paused` then `running` for a tap. Toasts also sit above the tab bar, the
|
|
149
|
+
home indicator and the on-screen keyboard, and move while an overlay with a
|
|
150
|
+
header and footer is open. Mount the Toaster inside `ZabiStringsProvider`
|
|
151
|
+
when you use it.
|
|
152
|
+
- **`Tooltip` opens on a tap and can be hovered.** A touch or pen press toggles
|
|
153
|
+
it, and it stays open until a second tap, a tap elsewhere, Escape, a scroll
|
|
154
|
+
or focus leaving (`touchDuration` defaults to `0`). The pointer can move from
|
|
155
|
+
the trigger onto the open bubble, so an open tooltip takes presses: keep
|
|
156
|
+
tooltips clear of other controls. A tooltip that would leave the viewport
|
|
157
|
+
flips or slides to stay 8px inside, and `data-placement` reports the side
|
|
158
|
+
used.
|
|
159
|
+
- **`Tabs` with long labels scroll** instead of wrapping onto several lines.
|
|
160
|
+
- **`AppBar` and `BottomTabBar` stay one row** (titles and labels get an
|
|
161
|
+
ellipsis; the bars are 57px and 65px at any text size). AppBar can be two
|
|
162
|
+
rows tall when the title would have under 72px beside the controls.
|
|
163
|
+
- **Overlays follow the on-screen keyboard** where it covers the page (Modal,
|
|
164
|
+
BottomSheet, SlideUp, Drawer), and Modal and SlideUp are at most `90dvh`.
|
|
165
|
+
- **Light-mode `Input`, `Textarea` and `Select` darken their border on hover**
|
|
166
|
+
and render text at 16px below 640px, so iOS Safari no longer zooms on focus.
|
|
167
|
+
- **`Table` and `PropsTable` sit on the card surface** with an elevated header
|
|
168
|
+
band; Skeleton and Header's variant chips use `bg-neutral-subtle`.
|
|
169
|
+
- **Inside `on-brand` and `on-accent` blocks** labels, links, borders and quiet
|
|
170
|
+
buttons change to the block's on-colour; a card inside a block gets the
|
|
171
|
+
page's colours back. Custom properties named `--zabi-theme-*` appear on a
|
|
172
|
+
block's parent.
|
|
173
|
+
- **`ImageUpload` no longer forces a 16rem minimum width**, and its `id` names
|
|
174
|
+
the control (the dropzone button), not the host. Set a width with `class` if
|
|
175
|
+
you relied on it.
|
|
176
|
+
- **`SidebarAccountPanel`'s `systemMode` default text is "Follows the
|
|
177
|
+
system".**
|
|
178
|
+
- `UnsavedChangesBar` and `ConfirmDialog` leave focus on the pressed button
|
|
179
|
+
while busy, where they moved it to their container.
|
|
180
|
+
- `Modal`'s `onclick` as a close signal is deprecated (see Deprecated).
|
|
181
|
+
|
|
182
|
+
**Theme files and the generator**
|
|
183
|
+
|
|
184
|
+
- **Dark theme files no longer restate the `--zabi-*` ramps.** `theme-dark`,
|
|
185
|
+
`theme-dark-only` and the dark part of `colors` only remap roles; the raw
|
|
186
|
+
palettes are declared once, in the light theme. Import the light theme
|
|
187
|
+
first: `@import "zabi-components/theme-only"; @import
|
|
188
|
+
"zabi-components/theme-dark-only";`. 8.0.0's `.dark`-only file carried a
|
|
189
|
+
copy; a dark file imported on its own now has no ramps. An override of
|
|
190
|
+
`--zabi-brand-*`, `--zabi-accent-*` or `--zabi-base-*` on `:root` applies in
|
|
191
|
+
both modes. The dark files are about 13 KB larger, because the block is
|
|
192
|
+
published for the class and attribute and for the system setting. See
|
|
193
|
+
`docs/theme-imports.md`.
|
|
194
|
+
- **The generator checks more role pairs than 8.0.0**, so a theme check that
|
|
195
|
+
passed on 8.0.0 can turn red. `createTheme` and `zabi-theme` resolve every
|
|
196
|
+
role pair the library's own guard checks (the field edge on fill, page and
|
|
197
|
+
card; the progress fill on its track; the muted and danger focus rings and
|
|
198
|
+
the control boundary on all five surface levels; the accent star outline on
|
|
199
|
+
an inset surface; the focus ring or its offset gap on an accent button; the
|
|
200
|
+
label on a selected and on a held selected pill tab). A pair below AA (4.5:1
|
|
201
|
+
text, 3:1 UI parts) is a warning, and fails under `--strict`. The generated
|
|
202
|
+
values are unchanged; the findings are real for that theme. Fix them with an
|
|
203
|
+
override (`--set`), for example `--color-focus` and `--color-link-hover` one
|
|
204
|
+
brand step darker, or use `--pin`.
|
|
205
|
+
- Theme files work with `[data-theme="dark"]`, and with `[data-theme="auto"]`
|
|
206
|
+
under `prefers-color-scheme: dark`. A page with no class and no attribute is
|
|
207
|
+
light, as before.
|
|
208
|
+
|
|
209
|
+
**Package**
|
|
210
|
+
|
|
211
|
+
- `package.json`: new `./create-theme` export and `zabi-theme` bin; `culori`
|
|
212
|
+
moved to `dependencies` (the generator uses it at run time);
|
|
213
|
+
`sideEffects: ["**/*.css"]`; `CHANGELOG.md` shipped in `files`; the `prepare`
|
|
214
|
+
script renamed `fix:lucide-types` (dev-only, so installing prints no
|
|
215
|
+
install-scripts notice; run it if your editor needs it). Every component
|
|
216
|
+
imports icons from `@lucide/svelte/icons/<name>`; import yours that way too.
|
|
217
|
+
No export, prop, token or file from 8.0.0 was removed or renamed.
|
|
218
|
+
|
|
219
|
+
### Added
|
|
220
|
+
|
|
221
|
+
#### Theming and the `zabi-theme` generator
|
|
222
|
+
|
|
223
|
+
- **`npx zabi-theme --brand "#0026EA"`** writes a CSS file with the full
|
|
224
|
+
`--zabi-brand-*` ramp; with `--accent` and `--neutral` also the
|
|
225
|
+
`--zabi-accent-*` ramp and all 21 `--zabi-base-*` steps; `--out` names the
|
|
226
|
+
file. Import it after `theme-only` and `theme-dark-only` and it covers light
|
|
227
|
+
and dark. The same is available in code as `createTheme({ brand, accent,
|
|
228
|
+
neutral })` from `zabi-components/create-theme`, which returns `{ css, tokens,
|
|
229
|
+
warnings, closest }`. The ramps sit on the library's lightness curve: your
|
|
230
|
+
colour gives the hue and chroma and is not pinned to a step, so the exact hex
|
|
231
|
+
may not appear, and the file header names the closest step. The generator
|
|
232
|
+
picks the text colour for the brand and accent fills.
|
|
233
|
+
- **The generator reports contrast** on stderr, in the file header and in
|
|
234
|
+
`warnings`; `--strict` exits 1 on a failed pair. `--set <token>=<value>` (or
|
|
235
|
+
`overrides`) moves a role and is checked too. `overrides` takes a flat map
|
|
236
|
+
(both modes) or `{ light, dark, both }`; the CLI has `--set-light` and
|
|
237
|
+
`--set-dark`. A light-only override is restated with the library's own dark
|
|
238
|
+
value in the dark rules, and the check reads each mode's own value.
|
|
239
|
+
- **`zabi-theme --pin`** (`createTheme({ pin: true })`): the primary action is
|
|
240
|
+
the exact brand colour in light, with derived hover and pressed states and a
|
|
241
|
+
label checked at 4.5:1. The focus ring and links take the colour where every
|
|
242
|
+
guarded pair still passes. Dark keeps the mirrored ramp step unless the colour
|
|
243
|
+
passes there; the header says which. A failing pair is a warning and the
|
|
244
|
+
colour is never moved. `--pin-accent` / `pin: { accent: true }` does the same
|
|
245
|
+
for the solid accent fill.
|
|
246
|
+
- **`neutralChroma`** (CLI `--neutral-chroma`, 0 to 0.1, needs `neutral`): the
|
|
247
|
+
OKLCH chroma at the neutral ramp's peak, for a clearly tinted neutral. With it
|
|
248
|
+
the translucent ink roles (`--color-action-secondary` and its hover and
|
|
249
|
+
pressed steps, `--color-surface-hover`, `-active`, `--color-border-overlay`
|
|
250
|
+
in light, `--shadow-color`) follow the neutral ramp, and the file restates the
|
|
251
|
+
dark values under the dark selectors.
|
|
252
|
+
- **A theming guide.** `THEMING.md` is the documented token API: quick start
|
|
253
|
+
with `zabi-theme`, import order, tables of the tokens an app may set (brand,
|
|
254
|
+
accent and neutral ramps, the "on" colours, fonts and weights, radius,
|
|
255
|
+
shadow, z-index) and the roles to read, light, dark and auto, what the
|
|
256
|
+
generator does and does not do, and what does not follow an override.
|
|
257
|
+
Renaming or removing a documented token is a breaking change; the token-name
|
|
258
|
+
snapshot in `npm run test:themes` enforces it. The docs site has a `/theming`
|
|
259
|
+
page with a brand switcher (`?brand=amber` opens any page in Amber, generated
|
|
260
|
+
from `#C17B00` and a warm neutral).
|
|
261
|
+
- **One brand override restyles light and dark.** The raw `--zabi-*` ramps are
|
|
262
|
+
declared once and the dark block only remaps roles; dark surfaces are mixed
|
|
263
|
+
from the neutral ramp, so warm or tinted greys carry through.
|
|
264
|
+
- **An accent colour.** `--zabi-accent-50` … `950`, `--color-accent-50` …
|
|
265
|
+
`950`, the roles `--color-accent`, `-hover`, `-active`, `-subtle`, `-border`,
|
|
266
|
+
`-text`, `--color-on-accent`, and the classes `bg-accent`, `text-accent`,
|
|
267
|
+
`border-accent` (with working `hover:` and `active:` forms, also
|
|
268
|
+
`active:bg-info-active` and `hover:bg-nav-menu-active-hover`). The
|
|
269
|
+
`energetic` tokens are unchanged. `Button` and `IconButton` take
|
|
270
|
+
`variant="accent"` (solid, own hover and pressed fills, `--color-on-accent`
|
|
271
|
+
label); `Badge variant="accent"` (subtle and solid); `Rating tone="accent"`.
|
|
272
|
+
- **The text on a fill can be set.** `--zabi-on-brand` (light, white) and
|
|
273
|
+
`--zabi-on-brand-dark` (dark, `brand-950`) feed `--color-on-brand`, which
|
|
274
|
+
`--color-action-primary-text` follows; `--zabi-on-accent` and
|
|
275
|
+
`--zabi-on-accent-dark` do the same for the accent. `Heading` and `Text` take
|
|
276
|
+
`tone="inherit" | "on-brand" | "on-accent"`; the classes `on-brand` and
|
|
277
|
+
`on-accent` set a block's text colour and make the focus ring visible on it;
|
|
278
|
+
`on-surface` marks a surface of your own inside a block.
|
|
279
|
+
THEMING.md ("Text and controls on a brand or accent block") says which button
|
|
280
|
+
variants stay legible there. Directly on a block an empty or ticked checkbox,
|
|
281
|
+
radio or toggle, a filled Rating star and status message text are not legible
|
|
282
|
+
(put them on a card); a card inside a block needs `:has()`.
|
|
283
|
+
- **Font tokens.** `--font-family-heading` (follows `--font-family-sans` until
|
|
284
|
+
set; applied to `h1`–`h6` and Heading), `--font-family-mono` (CodeBlock reads
|
|
285
|
+
it), `--font-weight-regular`, `-normal`, `-medium`, `-semibold`, `-bold`,
|
|
286
|
+
`--font-sans`, `--font-mono`, `--font-heading`, and a `font-heading` utility.
|
|
287
|
+
- **Dark mode by attribute and by the system setting.** The dark files apply to
|
|
288
|
+
`.dark`, `[data-theme="dark"]`, and `[data-theme="auto"]` when
|
|
289
|
+
`prefers-color-scheme` is dark; `[data-theme="light"]` stays light. Following
|
|
290
|
+
the system is opt-in. Put the class or attribute on `<html>`.
|
|
291
|
+
- **ThemeToggle `modes="three"`** steps through system, light and dark, writes
|
|
292
|
+
`data-theme` on `<html>`, shows a monitor, sun or moon and names itself
|
|
293
|
+
"Theme: system. Switch to light"; `labels` (including `labels.darkMode`)
|
|
294
|
+
replaces the words. `mode` (bindable), `onmodechange` and `storageKey` read or
|
|
295
|
+
drive the mode, or store it under your own key. It reads `dark`, `light` and
|
|
296
|
+
`auto`, follows changes made elsewhere, and shows the right icon before it
|
|
297
|
+
mounts.
|
|
298
|
+
- **Theme helpers** from the package root: `getThemeMode`, `setThemeMode`,
|
|
299
|
+
`isThemeDark`, `getStoredThemeMode`, `storeThemeMode`, `themeInitScript` and
|
|
300
|
+
the `ThemeMode` type. `themeInitScript()` returns a script for `<head>` that
|
|
301
|
+
applies the stored mode before first paint. TopNavbar takes `themeModes`,
|
|
302
|
+
`themeStorageKey` and `themeLabels`; SidebarAccountPanel takes
|
|
303
|
+
`themeModes="three"` with `onThemeModeChange`.
|
|
304
|
+
- New tokens: `--color-surface-inset` (`bg-surface-inset`; a recessed well on a
|
|
305
|
+
card, `base-100` light and `base-150` dark, AA text; dark `--color-input`
|
|
306
|
+
points at it and keeps its value; do not use `--color-input` for a well, it is
|
|
307
|
+
white in light), `--color-input-border-hover`, `--color-control-border`
|
|
308
|
+
(`border-control-border`, `text-control-border`; 3:1 on every surface level;
|
|
309
|
+
`--color-border-strong` is unchanged and decorative), `--color-focus-ring-muted`
|
|
310
|
+
(`.focus-ring--muted`), `--color-focus-ring-danger` (the error colour; the
|
|
311
|
+
on-colour inside a block), `--color-input-active` (`active:bg-input-active`;
|
|
312
|
+
Select's trigger uses it), `--color-action-primary-subtle-active` and
|
|
313
|
+
`--color-action-danger-subtle-active` (`active:bg-…`), `--zabi-list-row-radius`
|
|
314
|
+
(`.list-group` sets it for rows), `--color-progress-fill`,
|
|
315
|
+
`--color-progress-track`, `--zabi-avatar-ring`, and the documented star
|
|
316
|
+
colours `--zabi-rating-on`, `--zabi-rating-off`, `--zabi-rating-on-hover`,
|
|
317
|
+
`--zabi-rating-on-active`, `--zabi-rating-on-edge` (they apply when set on an
|
|
318
|
+
ancestor or by a class). The contrast guard holds the muted and danger focus
|
|
319
|
+
rings and the control boundary to 3:1 on all five surface levels, and fails
|
|
320
|
+
when a focus-ring rule reads a colour that is not in its pair list.
|
|
321
|
+
|
|
322
|
+
#### New components
|
|
323
|
+
|
|
324
|
+
- **SortableList** reorders items and leaves their content to you. Each row
|
|
325
|
+
has a drag handle (mouse and touch), arrow keys, Home and End on the handle,
|
|
326
|
+
optional move up and down buttons and a polite announcement of the new
|
|
327
|
+
position. `bind:items`, `onreorder` (item, old and new index),
|
|
328
|
+
`controls="manual"`, `strings`. Escape cancels a drag; a key that cannot move
|
|
329
|
+
further says so ("Hero section, already first"); a parent replacing `items`
|
|
330
|
+
mid-drag cancels it. Clicks on the handle or a move button do not bubble: put
|
|
331
|
+
them beside a header toggle, never inside another button. Handle and buttons
|
|
332
|
+
sit 8px apart on touch.
|
|
333
|
+
- **Collapsible**: a trigger wired to the panel it shows and hides, owning ids,
|
|
334
|
+
`aria-expanded`, `aria-controls` and the panel's name. `title` gives a
|
|
335
|
+
full-width header button with a chevron (`headingLevel` wraps it in a real
|
|
336
|
+
heading); a `trigger` snippet hands the wiring to your own `<button>`.
|
|
337
|
+
`bind:open`, `onopenchange`, `disabled`. Closed content stays in the DOM under
|
|
338
|
+
`hidden` (`unmountOnClose` removes it). If the panel closes while focus is
|
|
339
|
+
inside it, focus moves to the trigger. **CollapsibleGroup** makes an
|
|
340
|
+
accordion: opening one closes the others, or `multiple`; Arrow Up and Down,
|
|
341
|
+
Home and End move between headers. In a single-open group only the first
|
|
342
|
+
panel marked open renders open (also on the server, without
|
|
343
|
+
`onopenchange`); a disabled panel keeps its state.
|
|
344
|
+
- **ConfirmDialog** (on Modal): `title`, `message`, `variant` (`danger`,
|
|
345
|
+
`warning`, `info`, each with an icon; `danger` uses the danger button),
|
|
346
|
+
`confirmLabel`, `cancelLabel`. If `onconfirm` returns a promise the dialog
|
|
347
|
+
shows its loading state and cannot be dismissed until it settles: it closes on
|
|
348
|
+
success, stays open on failure and passes the error to `onerror`; returning
|
|
349
|
+
`false` keeps it open. `loading` does the same for your own request tracking;
|
|
350
|
+
`oncancel` reports how the user backed out; `loadingLabel` (default
|
|
351
|
+
"Working…") is announced once. Focus starts on Cancel, Enter only activates
|
|
352
|
+
the focused button, and it renders in `document.body` unless `portal={false}`.
|
|
353
|
+
It uses `role="alertdialog"`, draws no outline around the whole dialog while
|
|
354
|
+
loading and sets no `aria-busy` on the panel.
|
|
355
|
+
- **Drawer**: a modal panel from the side. `side` is `left`, `right`, `start` or
|
|
356
|
+
`end`; `size` is `sm`, `md` or `lg`, never wider than the screen; `title`,
|
|
357
|
+
`description`, a `footer` snippet; `dismissible`, `onclose({ reason })`,
|
|
358
|
+
`closeLabel`, `initialFocus`, `portal` (default true). Focus is trapped and
|
|
359
|
+
returns to the opener, the page does not scroll, the slide is skipped under
|
|
360
|
+
`prefers-reduced-motion`, it shares the scroll lock with Modal and SlideUp and
|
|
361
|
+
stacks with them. Its `onkeydown` runs after Escape handling; content with
|
|
362
|
+
nothing to focus is a Tab stop named by the title; the panel pads for the
|
|
363
|
+
bottom safe area; the close button takes taps in a 44px area.
|
|
364
|
+
- **MediaGrid**: image and video thumbnails to pick from (the grid only).
|
|
365
|
+
`getKey`, `getLabel`, `getUrl`; `bind:selected` or, with `multiple`,
|
|
366
|
+
`bind:selectedKeys`. A single selection moves and is never cleared by a repeat
|
|
367
|
+
press. `ondelete` shows a delete button named after each item (focus moves to
|
|
368
|
+
the item that took its place); `getType`, `getPoster`, `loading`, `empty`
|
|
369
|
+
snippet, `emptyHeadingLevel` (default 3), `minTileSize` (96px), `strings`. One
|
|
370
|
+
Tab stop; arrow keys move by the columns on screen, Home and End within the
|
|
371
|
+
row, Delete asks to delete; "Use the arrow keys to move between items." is read
|
|
372
|
+
when focus enters the grid and shown below it while it has keyboard focus. The delete button sits on the end corner, mirrors
|
|
373
|
+
in RTL, takes taps in a 44px area and shows a pressed state on touch.
|
|
374
|
+
- **Spinner**: the loading ring on its own. Sizes `xs` to `lg` (12, 14, 16,
|
|
375
|
+
20px); with a `label` it is a `status`, without one it is decorative; fades
|
|
376
|
+
instead of spinning under `prefers-reduced-motion`.
|
|
377
|
+
- **Slider**: a native range input in the library's colours. `bind:value`,
|
|
378
|
+
`min`, `max`, `step`, label, helper or error text, three sizes, `showValue`,
|
|
379
|
+
`formatValue` (also `aria-valuetext`).
|
|
380
|
+
- **UnsavedChangesBar**: Save and Discard while a form is `dirty`; sticky
|
|
381
|
+
(`position` `bottom` or `top`); `onsave` may return a promise (saving state;
|
|
382
|
+
a rejection keeps the bar and calls `onerror`); appearance announced politely;
|
|
383
|
+
never takes focus; focus returns to the edited field when it goes; `actions`
|
|
384
|
+
snippet. Warning before the page is left is your app's job.
|
|
385
|
+
- **AppShell**: the phone layout. `header` snippet (AppBar), `<main>` content,
|
|
386
|
+
`footer` snippet (BottomTabBar), a `dvh` column inside the safe areas. Sets
|
|
387
|
+
`--app-shell-top-inset` and `--app-shell-bottom-inset` (fallbacks 57px and
|
|
388
|
+
65px) on itself and on `<html>` while mounted, for overlays in
|
|
389
|
+
`document.body`. `contentElement="div"` inside a page that already has a
|
|
390
|
+
`<main>`.
|
|
391
|
+
- **AppBar**: a phone top bar with `title` (`headingLevel`, default 1;
|
|
392
|
+
`titleLines={2}` allows two lines), back control (`backHref` or `onback`,
|
|
393
|
+
`backLabel`), `leading` and `actions` snippets (up to two actions),
|
|
394
|
+
`collapseOnScroll` (stays while focus is inside; no animation under reduced
|
|
395
|
+
motion). The title always has at least 72px; a `leading` wider than the row is
|
|
396
|
+
held to the row. Parts carry `data-appbar-part` (`back`, `title`, `actions`);
|
|
397
|
+
the bar measures itself in the browser, so before hydration the title wraps to
|
|
398
|
+
the second row. At 180px wide no title shows beside a back control and two
|
|
399
|
+
actions.
|
|
400
|
+
- **BottomTabBar**: three to five links with an icon above a short label.
|
|
401
|
+
`items` take `href`, `label`, `icon`, `badge` (read with the label: "Inbox, 3
|
|
402
|
+
new"; `badgeLabel`, `badgeMax`). `active` is an href or path (`aria-current=
|
|
403
|
+
"page"`); left out, the bar follows the browser address, so pass
|
|
404
|
+
`active={page.url.pathname}` on the server. Fixed on its own and in the flow
|
|
405
|
+
in an AppShell (`position`), clear of the home indicator. Tabs are at least
|
|
406
|
+
44px with 8px gaps; under 52px wide the labels hide (icon only, label stays
|
|
407
|
+
the accessible name); tabs go under 44px only when five do not fit. The
|
|
408
|
+
active tab has a 2px outline in the action colour. With five tabs labels stay
|
|
409
|
+
11 to 12px at 200% text.
|
|
410
|
+
- **Rating**: one to `max` (default 5) stars, can be empty. `bind:value` is a
|
|
411
|
+
number or `null`; a radio group named by `label`, each star reads "4 of 5
|
|
412
|
+
stars"; arrow keys, Home and End; `name` submits it. A repeat press on the
|
|
413
|
+
selected star never clears it; `clearable` adds a clear button (Delete and
|
|
414
|
+
Backspace clear too). `readonly` shows a score as one image ("Quiz, 3.5 of 5
|
|
415
|
+
stars"), with fractions, `showValue`, `formatValue`; it submits nothing. 44px
|
|
416
|
+
targets at every `size`; `strings`; the empty star uses
|
|
417
|
+
`--color-control-border`.
|
|
418
|
+
- **SegmentedControl**: two to four choices in one row. `options` (`value`,
|
|
419
|
+
`label`, `icon`, `disabled`), `bind:value`, `onchange`. A radio group, not
|
|
420
|
+
tabs: one Tab stop, arrows move and select, `name` submits, a repeat press
|
|
421
|
+
does nothing. `fullWidth` (default true), labels wrap, 44px tall on touch.
|
|
422
|
+
- **BottomSheet**: a modal panel sliding up from the bottom. Snap points
|
|
423
|
+
(`snapPoints`, `"half"` and `"full"` by default; `bind:snap`); the grip drags
|
|
424
|
+
between them or down to close and is a button ("Expand" / "Collapse"). Content
|
|
425
|
+
scroll wins over dragging unless at its top. `title`, `description`, `footer`,
|
|
426
|
+
`isOpen`, `dismissible`, `portal`, `initialFocus`, `closeLabel`,
|
|
427
|
+
`onclose({ reason })` with the extra reason `"swipe"`. Safe areas, no animation
|
|
428
|
+
under reduced motion, a centred 40rem sheet from `md`. Half and full heights
|
|
429
|
+
are shares of what the on-screen keyboard leaves. The grip's focus ring is
|
|
430
|
+
drawn inside its 44px box; a click reaches the grip button, and flicks are
|
|
431
|
+
timed from the events' own times.
|
|
432
|
+
- **StickyActionBar**: keeps a form's main action in view at the bottom of the
|
|
433
|
+
screen or scrolling box, rises above the on-screen keyboard, reserves
|
|
434
|
+
`scroll-padding-bottom`, never takes focus. One per scrolling box; a short
|
|
435
|
+
form needs `class="flex min-h-full flex-col"`.
|
|
436
|
+
- **FloatingActionButton**: a round 56px primary button. `label` (required, the
|
|
437
|
+
accessible name), `extended`, `icon`, `href` (a link; a button otherwise),
|
|
438
|
+
`position` (`bottom-end` default, `bottom-start`, `bottom-center`). Above the
|
|
439
|
+
tab bar in an AppShell, above the safe area elsewhere; `--fab-bottom-offset`
|
|
440
|
+
lifts it; it reserves `scroll-padding-bottom` on what scrolls under it.
|
|
441
|
+
- **DateField** and **TimeField**: the browser's date and time inputs in the
|
|
442
|
+
library's Input. `bind:value` is `YYYY-MM-DD` or 24-hour `HH:mm`, `""` when
|
|
443
|
+
empty; `label`, `hint`, `error`, `min`, `max`, `step`, `required`, `disabled`,
|
|
444
|
+
`readonly`, `size`, `name`; work in FormField; ids start with `input-`.
|
|
445
|
+
**`formatDate` and `formatTime`** (package root) format those strings for a
|
|
446
|
+
locale (`formatDate("2026-10-06", "sv")` is "6 okt. 2026"), never shift by the
|
|
447
|
+
time zone, and return `""` for empty or invalid.
|
|
448
|
+
- **Calendar**: one month as an ARIA grid with event dots. `bind:month`
|
|
449
|
+
(`YYYY-MM`), `bind:selected` (`YYYY-MM-DD` or `null`), `events` (`date`,
|
|
450
|
+
`label`, `tone`; up to three dots a day), `weekStartsOn` (default Monday),
|
|
451
|
+
`locale`, `min`, `max`, `isDateDisabled`, `onselect`, `onmonthchange`,
|
|
452
|
+
`strings`. One Tab stop: arrows by day and week, Home and End within the
|
|
453
|
+
week, Page Up and Down by month (Shift: year). Days are named in full with
|
|
454
|
+
state and events and are 44px tall; unavailable days have a line through the
|
|
455
|
+
number. `locale` translates dates only, so pass `strings` for "today",
|
|
456
|
+
"selected", "unavailable". Pressing the selected day does nothing.
|
|
457
|
+
- **DropdownItem**: the menu item as a component for a Dropdown's custom
|
|
458
|
+
`children`; takes its role from the Dropdown and joins the arrow-key order. A
|
|
459
|
+
DropdownItem with custom children and a description is named by its content
|
|
460
|
+
alone.
|
|
461
|
+
- **Stepper**: progress through a multi-step form. `steps` (labels or `{ label,
|
|
462
|
+
description }`), `bind:current` (zero-based), `layout` (auto goes compact below
|
|
463
|
+
30rem of its own width: it suits about five short labels; compact segments are
|
|
464
|
+
narrower than 44px from seven steps with `interactive` on 320px), `size`
|
|
465
|
+
(`sm`, `md`, `lg`), `strings`. Completed steps show a check, the current one
|
|
466
|
+
its number, upcoming an outline; a navigation landmark with
|
|
467
|
+
`aria-current="step"`; a step change is announced once. `interactive` makes
|
|
468
|
+
completed steps buttons. It does not move focus: focus the new heading
|
|
469
|
+
yourself. In Safari 16 and older it needs a width from its parent in a flex
|
|
470
|
+
row.
|
|
471
|
+
- **PhotoGrid**: square thumbnails that open a viewer. `photos` (`src`,
|
|
472
|
+
`thumbSrc`, `alt`, `width`, `height`, `caption`, `id`), `columns` (3, then 4
|
|
473
|
+
and 5), `max` ("+N" tile), `onopen(index)`, `onadd` ("Add photo" tile),
|
|
474
|
+
`selectable` (`"single"` or `"multiple"`, `bind:selected`,
|
|
475
|
+
`bind:selectedKeys`; a single selection is never cleared by a repeat press and
|
|
476
|
+
each tile gets an Open button). One Tab stop, lazy loading, placeholder and
|
|
477
|
+
failure fallback. **PhotoViewer**: a full-screen dialog for them, `bind:index`,
|
|
478
|
+
`bind:isOpen`, caption, counter, up to three `actions` (more in a menu).
|
|
479
|
+
Swipe or arrows change photo; pinch, double tap, Ctrl+wheel or `+`/`-` zoom;
|
|
480
|
+
drag pans; swipe down, Escape or the close button close it
|
|
481
|
+
(`onclose({ reason })`). Focus returns to the tile; the thumbnail shows
|
|
482
|
+
blurred until the full image loads; neighbours are preloaded; dark in both
|
|
483
|
+
themes; safe areas. Double tap is timed from the touch events. Previous and
|
|
484
|
+
Next at the ends keep their plate and dim only the arrow.
|
|
485
|
+
- **Avatar** and **AvatarGroup**: `Avatar` is a round picture falling back to
|
|
486
|
+
initials (`name`, `src`, `size` `sm` 24, `md` 32, `lg` 48px, `alt`, `alt=""`
|
|
487
|
+
for decorative, `locale`); initials come from the first and last word, are in
|
|
488
|
+
the server markup and remain if the picture fails. They are `brand-200` on
|
|
489
|
+
`brand-700` in the default light theme (the subtle primary pair, 4.5:1).
|
|
490
|
+
`AvatarGroup` shows `people` overlapped as a list, up to `max` (default 4) and
|
|
491
|
+
"+3" (`strings.more`), `locale`. Not interactive.
|
|
492
|
+
- **SwipeableListItem**: a row with one or two `actions` revealed by a swipe
|
|
493
|
+
towards the inline start (touch and stylus; mirrored in RTL); a swipe only
|
|
494
|
+
reveals, an action runs on press. The same actions are behind a "more"
|
|
495
|
+
button (`showMoreButton`, on by default; with `false` offer another route).
|
|
496
|
+
Bindable `open`, `onopenchange`, `strings.actions`; one row open per list.
|
|
497
|
+
- **PullToRefresh**: wraps a list; pulling down at the top calls `onrefresh`
|
|
498
|
+
(touch only). Bindable `refreshing`, `disabled`, `threshold` (64px),
|
|
499
|
+
`strings`. A Refresh button shown on keyboard focus does the same; a status
|
|
500
|
+
region says "Refreshing" then "Updated". A rejected `onrefresh` ends the busy
|
|
501
|
+
state silently: report the failure in the app. In controlled mode it says
|
|
502
|
+
"Updated" whenever `refreshing` goes back to false.
|
|
503
|
+
- **`focusToasts()`** moves focus to the newest toast; focus returns when it is
|
|
504
|
+
dismissed, also from inside an open overlay.
|
|
505
|
+
|
|
506
|
+
#### Existing components
|
|
507
|
+
|
|
508
|
+
- **ImageUpload**: `onbrowse` replaces the native chooser and
|
|
509
|
+
`event.preventDefault()` in `onclick` keeps it closed. A video value (file
|
|
510
|
+
type, extension, or `previewType`) renders a `<video>` with controls, never
|
|
511
|
+
autoplaying; a `preview` snippet replaces the preview. `label` and `id` (the
|
|
512
|
+
dropzone is a native `<button>`); Change and Remove are named from the label
|
|
513
|
+
("Change logo") or `changeLabel` and `removeLabel`; the preview takes `alt`.
|
|
514
|
+
A dropped file is checked against `accept` and rejected through
|
|
515
|
+
`onfilereject`. Copy props: `browseText`, `changeText`, `removeText`,
|
|
516
|
+
`errorTitle`, `errorRecovery` (`false` leaves the line out), `selectedText`,
|
|
517
|
+
`removedText` ("Image selected" / "Image removed" are announced);
|
|
518
|
+
`actionsPlacement` (`"overlay"` or `"strip"`, strip by default for video).
|
|
519
|
+
- **IconButton**: `xs` size (24px, the minimum target; use `sm` or larger where
|
|
520
|
+
touch is primary); `pressed` (bindable, `aria-pressed` and a pressed style in
|
|
521
|
+
every variant; `event.preventDefault()` in `onclick` keeps the state; left
|
|
522
|
+
undefined it renders no `aria-pressed`); `tone="danger"` on `ghost` or
|
|
523
|
+
`outline` for a quiet destructive style. `href` on Button and IconButton
|
|
524
|
+
renders a real link (`target`, `rel`, `download`); a disabled or loading link
|
|
525
|
+
has no `href` and is `aria-disabled`.
|
|
526
|
+
- **Modal**: `portal` (renders in `document.body`; a theme class set below
|
|
527
|
+
`<body>` does not reach it); `onclose({ reason: "escape" | "backdrop" |
|
|
528
|
+
"close-button" })`; `dismissible={false}` (the close button stays focusable,
|
|
529
|
+
`aria-disabled`; setting `isOpen` still closes); `role="alertdialog"`;
|
|
530
|
+
`closeLabel` (default "Close"); `fullScreen` (or `"mobile"` below `md`);
|
|
531
|
+
`initialFocus` (also on SlideUp); typed `aria-describedby`, `aria-busy`,
|
|
532
|
+
`data-*`, `id`. SlideUp: `closeLabel`, `swipeToClose` (grip, downward swipe,
|
|
533
|
+
calls `onclick`), a `footer` snippet kept above the keyboard.
|
|
534
|
+
- **Toast and Toaster**: `pushToast` takes `action: { label, onclick,
|
|
535
|
+
dismissOnClick? }` (a button in the toast, announced with the message, closes
|
|
536
|
+
after the handler unless `dismissOnClick` is `false`; the countdown pauses
|
|
537
|
+
while pointer or focus is on it; give it a long `duration` or `0`; the
|
|
538
|
+
`ToastAction` type is exported); `duration` takes `"short"` (3 s), `"medium"`
|
|
539
|
+
(7 s), `"long"` (14 s), `"persistent"` or milliseconds, with `defaultDuration`
|
|
540
|
+
on Toaster (`TOAST_DURATIONS`, `ToastDuration` exported); `strings` for every
|
|
541
|
+
built-in word (`ToasterStrings`, `DEFAULT_TOASTER_STRINGS`), `aria-label`,
|
|
542
|
+
`showCountdown`, `onpausechange({ id, paused })`, `data-paused`; `closeLabel`
|
|
543
|
+
on Toast and Alert; `--toaster-bottom-offset` (72px clears a
|
|
544
|
+
FloatingActionButton); other attributes pass to the region. A held finger
|
|
545
|
+
pauses a toast, lifting resumes with at least 3 s left. Toasts do not fly
|
|
546
|
+
under reduced motion; one with an action stays until dismissed.
|
|
547
|
+
- **Dropdown**: options take `icon`, `tone: "danger"` and `description` (the
|
|
548
|
+
`DropdownOption` type is exported). `presentation` (`"auto"`, `"popover"`,
|
|
549
|
+
`"sheet"`; default `"popover"`): in a sheet the same menu opens in a
|
|
550
|
+
BottomSheet titled by the control's label; `<Dropdown presentation="sheet">`
|
|
551
|
+
is the library's action sheet (`sheetTitle`, `sheetSnap`, `sheetCloseLabel`,
|
|
552
|
+
`sheetExpandLabel`, `sheetCollapseLabel`, `fullWidth`, optional
|
|
553
|
+
`ariaLabelledby`). Menu boxes report `data-resolved-placement`.
|
|
554
|
+
- **Select**: renders a real `<select>` on the server, the working control
|
|
555
|
+
before hydration and with scripts off; on mount the custom list takes over,
|
|
556
|
+
and a choice made before is kept and reported once; `presentation="native"`
|
|
557
|
+
renders only the native select. `presentation` (`"auto"`, `"popover"`,
|
|
558
|
+
`"sheet"`, `"native"`), `strings` (including `listLabel`;
|
|
559
|
+
`DEFAULT_SELECT_STRINGS`, `SelectStrings`, `SelectPresentation` exported,
|
|
560
|
+
also from atoms; the six text props still win). The chosen option has a check
|
|
561
|
+
mark at the inline end and a fill, and every option carries `aria-selected`.
|
|
562
|
+
A disabled Select submits its value once hydrated and not before; the inline
|
|
563
|
+
message of a `required` Select is the browser's, in the browser's language; a
|
|
564
|
+
label longer than one line is cut in the native select and wraps in the custom
|
|
565
|
+
one, so the field can grow by a line.
|
|
566
|
+
- **Input, Textarea, Select**: `hint` and `error` (a hint is tied with
|
|
567
|
+
`aria-describedby`, an error sets the error state and is announced;
|
|
568
|
+
`variant` + `message` still work and your own `aria-describedby` is merged).
|
|
569
|
+
Input takes `leading` and `trailing` snippets and, for `type="password"`,
|
|
570
|
+
`revealable` (`revealLabel`, default "Show password", `aria-pressed`). Input,
|
|
571
|
+
Textarea, Select, Checkbox, Radio, Toggle and ThemeToggle accept their
|
|
572
|
+
element's native attributes (`autocomplete`, `inputmode`, `maxlength`,
|
|
573
|
+
`enterkeyhint`, `data-*`); Table and Text pass other attributes on. Toggle
|
|
574
|
+
takes `aria-label` and `aria-labelledby` ("Toggle" is only the last fallback).
|
|
575
|
+
`Checkbox` is exported from the atoms entry.
|
|
576
|
+
- **Table**: `stacked` (or `"sm"`, `"md"`, `"lg"`) lays rows out as label and
|
|
577
|
+
value pairs and drops the 20rem minimum; put `data-label` on each cell and
|
|
578
|
+
keep the `<thead>`; `captionHidden` keeps the caption as the accessible name
|
|
579
|
+
but hides it. A stacked cell keeps several children together on the value
|
|
580
|
+
side; a cell without `data-label` puts its content there too; an empty cell
|
|
581
|
+
leaves no blank line.
|
|
582
|
+
- **EmptyState**: `headingLevel` (1–6, default 2); `size="compact"` (tighter,
|
|
583
|
+
smaller title, a plain `<div>` not a named region; the default is still a
|
|
584
|
+
`<section>` named by its title).
|
|
585
|
+
- **TopNavbar**: nav items take `external` (default true for absolute URLs);
|
|
586
|
+
`collapseAt` (`"sm"`, `"md"` default, `"lg"`, `"xl"`; `TopNavbarCollapseAt`
|
|
587
|
+
exported).
|
|
588
|
+
- **Tabs**: `fullWidth` (for two or three tabs; scrolls when a share is too
|
|
589
|
+
small for the longest word). The rule under Tabs is `border-border`; a
|
|
590
|
+
selected pill uses `action-primary-subtle`.
|
|
591
|
+
- **SidebarShell**: `mobile="drawer"` hides the sidebar below `lg` and opens it
|
|
592
|
+
in a Drawer from the start side: `bind:isOpen`, `trigger` snippet
|
|
593
|
+
(`aria-expanded` wiring), `drawerTitle`, `closeLabel`, `onclose({ reason })`;
|
|
594
|
+
it closes when a link is followed and when the screen widens past 1024px;
|
|
595
|
+
`label` names the scrolling region (default "Navigation links").
|
|
596
|
+
SidebarNavigation declares and passes these on. Default `mobile="none"`
|
|
597
|
+
changes nothing.
|
|
598
|
+
- **Page**: `safeArea` (default true) pads left, right and bottom by
|
|
599
|
+
`env(safe-area-inset-*)`; nothing inside an AppShell or where the insets are
|
|
600
|
+
zero; your own `px-*` class replaces that side. Needs `viewport-fit=cover`.
|
|
601
|
+
- **Tooltip**: `touchDuration` (default `0`); one open at a time; a tooltip on a
|
|
602
|
+
disabled button is tied to it with `aria-describedby`. Do not put essential
|
|
603
|
+
information in a tooltip.
|
|
604
|
+
- **ColorPicker**: the colour map is two native sliders (saturation, lightness)
|
|
605
|
+
in one Tab stop, working by keyboard (Shift moves ten steps), touch and pen;
|
|
606
|
+
new strings `area`, `saturation`, `lightness`, `invalidHex`; the hue slider
|
|
607
|
+
shows its focus ring and is 44px on a coarse pointer.
|
|
608
|
+
- **CodeBlock**: `copyLabel`, `copiedLabel`; "copied" is announced through a
|
|
609
|
+
status region. **FormField**: `requiredLabel`.
|
|
610
|
+
#### Mobile and touch
|
|
611
|
+
|
|
612
|
+
- New props for phones: `Page` `safeArea`, `Modal` `fullScreen`, `SlideUp`
|
|
613
|
+
`swipeToClose` and `footer`, `TopNavbar` `collapseAt`, `Tabs` `fullWidth`,
|
|
614
|
+
`Tooltip` tap, `Select` and `Dropdown` `presentation`, `SidebarShell`
|
|
615
|
+
`mobile="drawer"`, `Toaster` `--toaster-bottom-offset`. Components for them:
|
|
616
|
+
AppShell, AppBar, BottomTabBar, BottomSheet, Drawer, StickyActionBar,
|
|
617
|
+
FloatingActionButton, SwipeableListItem, PullToRefresh, PhotoViewer. Safe areas
|
|
618
|
+
need `viewport-fit=cover`.
|
|
619
|
+
|
|
620
|
+
#### Accessibility
|
|
621
|
+
|
|
622
|
+
- A focus ring in forced colours: `.focus-ring`, the legacy `.focus-brand` and
|
|
623
|
+
`.focus-nav`, and the checkbox and radio row draw an outline there. The
|
|
624
|
+
current page has an outline in forced colours in SidebarNavigation,
|
|
625
|
+
SidebarPanel and TopNavbar. In forced colours every option of a Select list
|
|
626
|
+
has the same border and the chosen one is told apart by its check mark only.
|
|
627
|
+
- `docs/ACCESSIBILITY.md` has a "Library conventions" section (where focus goes
|
|
628
|
+
when a focused control is removed, the shared overlay stack and scroll lock,
|
|
629
|
+
disabled options, hover-revealed actions, focus in forced colours) and no
|
|
630
|
+
longer lists fixed issues as open; `docs/KEYBOARD_NAVIGATION.md` describes
|
|
631
|
+
what the components do.
|
|
632
|
+
|
|
633
|
+
#### Strings and hydration
|
|
634
|
+
|
|
635
|
+
- **`ZabiStringsProvider`**: wrap the app in `<ZabiStringsProvider
|
|
636
|
+
strings={…}>` and every component under it uses those words unless an
|
|
637
|
+
instance says otherwise. One entry per component with a `strings` object
|
|
638
|
+
(including `avatarGroup`, `swipeableListItem`, `pullToRefresh`), keyed by prop
|
|
639
|
+
name for components with single-text props (`toast`, `alert`, `codeBlock`,
|
|
640
|
+
`imageUpload`, `unsavedChangesBar`), and a `common` group (close, back, expand,
|
|
641
|
+
collapse, required, showPassword, search, confirm, cancel). Order, later wins:
|
|
642
|
+
built-in English, provider, the instance's `strings`, its own text props. It
|
|
643
|
+
is Svelte context (per request on the server, nestable, reaches portalled
|
|
644
|
+
overlays); Modal, Drawer, SlideUp, BottomSheet, Toaster, Rating and the
|
|
645
|
+
components above read it; landmark `ariaLabel`s are not covered.
|
|
646
|
+
`getZabiStrings()`, `DEFAULT_ZABI_COMMON_STRINGS` and the types are exported.
|
|
647
|
+
No translations ship; the README has a complete Swedish example. Nothing
|
|
648
|
+
changes outside a provider.
|
|
649
|
+
- **Every built-in text can be replaced**: `strings` on TopNavbar,
|
|
650
|
+
SidebarFooter, SidebarAccountPanel, SidebarNavigation, SidebarBrandHeader,
|
|
651
|
+
ColorPicker, ContactForm, PropsTable, ComponentDemo, Select, Toaster, Stepper,
|
|
652
|
+
Calendar, MediaGrid, SortableList, Rating, PullToRefresh. The README's "Texts
|
|
653
|
+
and other languages" lists the prop for each component.
|
|
654
|
+
- **A choice made before the page hydrates is kept.** A Checkbox, Radio,
|
|
655
|
+
RadioGroup, Rating, SegmentedControl or Select changed in the server's markup
|
|
656
|
+
was set back at hydration; the bound value now follows the native input and
|
|
657
|
+
the change callback fires once (a form reset still returns to the rendered
|
|
658
|
+
value). A bound Select value with no matching option is kept and submitted as
|
|
659
|
+
itself.
|
|
660
|
+
- **No component drops keyboard focus on hydration** (Heading, Text,
|
|
661
|
+
CardHeader, AppBar's title, EmptyState's heading, Container, AppShell,
|
|
662
|
+
Collapsible); a guard fails the build if a dynamic element comes back.
|
|
663
|
+
|
|
664
|
+
#### Package and build
|
|
665
|
+
|
|
666
|
+
- Exports and package fields are listed under Upgrade notes. `zabi-components/types`
|
|
667
|
+
matches the components again (see Fixed). `npm run build:css` works in a fresh
|
|
668
|
+
checkout with no `dist/`.
|
|
669
|
+
- The docs site honours `data-theme`, and Storybook's toolbar Theme control
|
|
670
|
+
drives its sidebar, toolbar and docs pages. `docs/theme-imports.md` says what
|
|
671
|
+
to expect when Tailwind and `zabi-components/css` are combined (the compiled
|
|
672
|
+
stylesheet carries its own copy of each utility, so import order decides: with
|
|
673
|
+
Tailwind in the app import `theme-only` and `theme-dark-only` instead).
|
|
674
|
+
`THEME.md` no longer tells apps to import the compiled stylesheet after
|
|
675
|
+
`theme-only` or to use `theme()` for a colour. `THEME_QUICK_REFERENCE.md`
|
|
676
|
+
shows the current primary colour and dark mirror; `docs/VARIANTS.md`, the
|
|
677
|
+
README, the Storybook introduction and the showcase guide cover the new
|
|
678
|
+
components. The component docs site examples are written in English. The
|
|
679
|
+
site's catalog sidebar is a drawer below 1024px and its "On this page" list
|
|
680
|
+
marks the section being read; the site sets `viewport-fit=cover`.
|
|
681
|
+
|
|
682
|
+
### Changed
|
|
683
|
+
|
|
684
|
+
- **Light mode has a visible surface ladder.** `--color-surface-base`,
|
|
685
|
+
`--color-page` and `--color-background-tertiary` move from `base-100` to
|
|
686
|
+
`base-150`; `--color-surface-elevated` from `base-50` to `base-100`; a card is
|
|
687
|
+
1.18:1 on the page (was 1.10) and a nested card 1.10:1 (was 1.04).
|
|
688
|
+
`--color-card-active` and `--color-surface-overlay-hover` are `base-150`.
|
|
689
|
+
- **Light tinted fills read on a white card.** `--color-<family>-subtle` moves
|
|
690
|
+
from step 100 to 200 and `--color-<family>-border` from 200 to 300 for
|
|
691
|
+
success, warning, error, energetic and info (1.36:1 on a card, was 1.16);
|
|
692
|
+
`--color-neutral-subtle` is `base-250`, `--color-neutral-border` `base-300`;
|
|
693
|
+
`--color-action-primary-subtle` and `-subtle-hover` are `brand-200` and
|
|
694
|
+
`brand-300`.
|
|
695
|
+
- **Light hover and secondary fills are stronger.** `--color-surface-hover` and
|
|
696
|
+
`--color-surface-active` are 9% and 15% ink (were 6% and 11%);
|
|
697
|
+
`--color-action-secondary`, `-hover`, `-active` are 10%, 15%, 20% (were 7%,
|
|
698
|
+
12%, 17%).
|
|
699
|
+
- **Overlays have an edge in light** (`--color-border-overlay` is a 10% ink tint,
|
|
700
|
+
was transparent); **light shadows are stronger** (`--shadow-color` `24 24 27`,
|
|
701
|
+
`--shadow-opacity` 0.14; were `0 0 0` and 0.1).
|
|
702
|
+
- **Light fields and disabled controls follow the new page**:
|
|
703
|
+
`--color-input-hover` `base-100`, `--color-input-disabled` the page colour,
|
|
704
|
+
`--color-action-disabled` `base-250`, `--color-action-disabled-border`
|
|
705
|
+
`base-300`.
|
|
706
|
+
- **Focus tokens**: `--color-focus` is `brand-600` in both themes;
|
|
707
|
+
`--color-focus-weak`, `-medium`, `-strong` each move one step;
|
|
708
|
+
`--color-nav-menu-focus` follows `--color-focus`; `.focus-ring--muted` (ghost
|
|
709
|
+
and link buttons, the AppBar back control, the SortableList handle) reads
|
|
710
|
+
`--color-focus-ring-muted`, 3.7:1 or more on every surface level (dark was
|
|
711
|
+
`base-500`, 2.49:1 on elevated, 1.98:1 on overlay). Dark mode otherwise keeps
|
|
712
|
+
its values: `.dark` restates the values it used to inherit.
|
|
713
|
+
- **Dark mapping**: `--color-action-primary-text` follows `--color-on-brand`;
|
|
714
|
+
dark `--color-base-*` steps alias the mirrored `--zabi-base-*` step; the dark
|
|
715
|
+
block holds no raw palette and no hex value.
|
|
716
|
+
- **Menus share one edge and surface**: Dropdown, NavigationMenuContent and the
|
|
717
|
+
ColorPicker popover use `border-border-overlay` on `bg-surface-overlay` (the
|
|
718
|
+
ColorPicker popover no longer sits on the inset field colour in dark). Dropdown
|
|
719
|
+
menus and Select lists use the 16px overlay radius, and Select's search field
|
|
720
|
+
and options sit 4px closer to the list's edge. ColorPicker's popover and
|
|
721
|
+
NavigationMenu's panels use the overlay radius.
|
|
722
|
+
- **Nested corners are concentric** where a rounded element sits closer to its
|
|
723
|
+
container's corner than that corner's radius: the Card in a Modal, the avatar
|
|
724
|
+
in SidebarFooter's profile button, MediaGrid's check mark (a rounded square)
|
|
725
|
+
and video badge, Alert's close button, ListItem rows in a `.list-group`.
|
|
726
|
+
- Input, Textarea and Select text is 16px below 640px (also ColorPicker's hex
|
|
727
|
+
field and the search fields in Select and the sidebars); sizes unchanged from
|
|
728
|
+
`sm` up and control heights unchanged.
|
|
729
|
+
- **Checked checkboxes and radios use the primary action fill**, tick and dot in
|
|
730
|
+
the primary button's text colour (was a paler step at 2.8:1 on the light page).
|
|
731
|
+
- Input, Select and Textarea darken their border on hover in light
|
|
732
|
+
(`--color-input-border-hover`); error, success, warning and disabled fields
|
|
733
|
+
are unchanged. Select's trigger placeholder uses the placeholder colour.
|
|
734
|
+
- Table and PropsTable sit on the card surface with an elevated header band;
|
|
735
|
+
Skeleton and Header's variant chips use `bg-neutral-subtle`.
|
|
736
|
+
- Sidebar search fields, the selected panel item, the elevated panel and the
|
|
737
|
+
profile button use full-strength rings instead of 40–80% alpha ones; dashed
|
|
738
|
+
empty states keep only their dashed border. SidebarNavigation's search
|
|
739
|
+
placeholder keeps 4.5:1 on hover.
|
|
740
|
+
- **Controls are 44px on touch** (`@media (pointer: coarse)`): Button, IconButton,
|
|
741
|
+
Input and the Select trigger at `sm` and `md` (`lg` is 48px already; use it
|
|
742
|
+
for the main action on a phone), and Slider rows, Checkbox, Radio and Toggle
|
|
743
|
+
rows, Tabs, Collapsible triggers, Dropdown items, Select options (48px),
|
|
744
|
+
NavigationMenu, TopNavbar and Sidebar items, SortableList controls and toast
|
|
745
|
+
buttons. The close buttons of Alert, Toast, Modal, SlideUp and Drawer and the
|
|
746
|
+
Toggle switch keep their size and take taps in a 44px area. IconButton `xs`
|
|
747
|
+
stays 24px.
|
|
748
|
+
- **Every pressable control shows a `:active` state**; a disabled Toggle or Tab
|
|
749
|
+
no longer shows one. A toggled-on ghost, outline, link or danger-tone
|
|
750
|
+
IconButton shows a pressed fill distinct from hover and 1.25:1 or more from
|
|
751
|
+
rest. The selected pill Tab's pressed fill is 1.42:1 from rest (was 1.24:1)
|
|
752
|
+
and its label keeps 4.89:1.
|
|
753
|
+
- SlideUp pads its content for the bottom safe area and is at most `90dvh`
|
|
754
|
+
(was `90vh`); Modal is at most `90dvh`.
|
|
755
|
+
- **Tabs scroll sideways when they do not fit**, with a fade on the edge that has
|
|
756
|
+
more, and bring the selected and keyboard-focused tab into view; labels no
|
|
757
|
+
longer wrap or squeeze.
|
|
758
|
+
- **Toasts**: sit above the tab bar and home indicator (the stack clears
|
|
759
|
+
`--app-shell-bottom-inset` and the bottom safe area; below `sm` it spans the
|
|
760
|
+
width with 16px gutters; a `viewport` Toast at the top clears the top safe
|
|
761
|
+
area and an AppShell header); keep clear of an overlay's footer (16px above
|
|
762
|
+
it, or at the top when there is no room; a centred desktop Modal changes
|
|
763
|
+
nothing); take the larger free stretch above the panel or between header and
|
|
764
|
+
footer, scrolling there, without covering an overlay's title and close button;
|
|
765
|
+
sit above the on-screen keyboard; `--toaster-bottom-offset` is not added on top
|
|
766
|
+
of an overlay's footer. Tab moves from an overlay's last control to the toast
|
|
767
|
+
controls and back, and Escape in a toast returns focus without closing the
|
|
768
|
+
overlay. Padding, gaps and button targets are in px and the status icon stays
|
|
769
|
+
20px at enlarged text (a two-sentence toast at 375px and 200% text is at most
|
|
770
|
+
420px tall, was 594px); buttons wrap under the title when it would get less
|
|
771
|
+
than 8rem; the stack is capped to the screen and scrolls. The countdown says
|
|
772
|
+
"1 second", not "1 seconds".
|
|
773
|
+
- **Tooltip**: flips or slides to stay 8px inside the viewport, also with a
|
|
774
|
+
mouse; below 640px a `left` or `right` tooltip opens above or below; a tap
|
|
775
|
+
opens it when the finger comes up where it went down, so a scroll that starts
|
|
776
|
+
on the trigger no longer flashes it; `data-placement` reports the side used.
|
|
777
|
+
- **Overlays stay above the on-screen keyboard** where it covers the page; the
|
|
778
|
+
focused field is scrolled into view.
|
|
779
|
+
- **Overlay headers stay compact at large text sizes.** In BottomSheet, Modal,
|
|
780
|
+
Drawer and SlideUp the header's padding, gaps, grip and close button are in px
|
|
781
|
+
and the title grows to 1.3 times and stops; at 200% text a half-height sheet
|
|
782
|
+
gives the content 378 of 576px (was 182). Titles break at a hyphen in the
|
|
783
|
+
page's language and a too-long word no longer sticks out of Drawer or SlideUp.
|
|
784
|
+
- **Select fills its container** and no longer changes width with the chosen
|
|
785
|
+
option; its trigger is a `combobox` reporting `aria-invalid` (with `error`, the
|
|
786
|
+
error variant or a failed `required` check), `aria-required` and `aria-busy`;
|
|
787
|
+
`required` blocks an empty form, shows the browser's message as the error and
|
|
788
|
+
focuses the trigger; a long label wraps; the form control is a `<select
|
|
789
|
+
name>`; a reset returns it to its rendered value; it opens as a sheet on a
|
|
790
|
+
phone; the chevron and the Dropdown panel do not animate under reduced
|
|
791
|
+
motion; keyboard focus in the native select moves to the trigger at handover;
|
|
792
|
+
`aria-label` and `aria-labelledby` name the native select too; with several
|
|
793
|
+
empty required Selects only the form's first invalid control takes focus.
|
|
794
|
+
- **A loading Button or IconButton** is `aria-disabled` and `aria-busy` (see
|
|
795
|
+
Upgrade notes); a loading link keeps its place in the Tab order. Button labels
|
|
796
|
+
wrap (`min-height`: 32, 40, 48px; 44px minimum on touch).
|
|
797
|
+
- **A disabled Dropdown option stays focusable** (`aria-disabled`).
|
|
798
|
+
- **A toast with an action stays until dismissed** unless given a `duration`;
|
|
799
|
+
with no duration a toast closes after 7 s (see Upgrade notes); a message with
|
|
800
|
+
no title is the toast's text.
|
|
801
|
+
- **ThemeToggle** (two modes) has the name "Dark mode" and applies the stored
|
|
802
|
+
choice on mount in both modes; it no longer writes an inline `color-scheme`
|
|
803
|
+
and removes a stale one, so adding or removing `class="dark"` from your own
|
|
804
|
+
script moves native controls with the tokens.
|
|
805
|
+
- **EmptyState at `size="compact"`** is a plain `<div>`.
|
|
806
|
+
- **ImageUpload** fills its container; `id` names the control; Change and Remove
|
|
807
|
+
appear on `:focus-visible` (not any focus), wrap and truncate in a narrow
|
|
808
|
+
container, stay visible on coarse pointers and where hover is unavailable, and
|
|
809
|
+
sit on an opaque plate so "Change" is readable over any image (it was 2.59:1 in
|
|
810
|
+
dark over a light image).
|
|
811
|
+
- **`font-sans` and `font-mono` follow the theme.**
|
|
812
|
+
- **A List row with nothing to do is plain content**; a disabled plain ListItem
|
|
813
|
+
row no longer carries `aria-disabled`.
|
|
814
|
+
- Checkbox and Radio inputs are stretched invisibly over their box.
|
|
815
|
+
- Textarea and Select status messages use the same text colour step as Input's.
|
|
816
|
+
- **Icons are imported one file at a time** from `@lucide/svelte/icons/<name>`,
|
|
817
|
+
not the `@lucide/svelte` barrel: an app importing one Button from the root
|
|
818
|
+
compiles 366 modules where it compiled 3,800 (233 from
|
|
819
|
+
`zabi-components/atoms`). The re-exported icons keep their names.
|
|
820
|
+
- The `viewport` prop on NavigationMenu only sets `isMobile` on the context for
|
|
821
|
+
your own children; it has no effect of its own, and the docs now say so.
|
|
822
|
+
- **`Toast`/`Alert`/`CodeBlock`/`ImageUpload`/`UnsavedChangesBar`** follow
|
|
823
|
+
`ZabiStringsProvider`.
|
|
824
|
+
|
|
825
|
+
### Deprecated
|
|
826
|
+
|
|
827
|
+
- **Modal's `onclick` is deprecated as a close signal.** It is still called with
|
|
828
|
+
the event on Escape, a backdrop click and the close button; use `onclose`.
|
|
829
|
+
- In `zabi-components/types`, members that were exported but never existed
|
|
830
|
+
(`open`, `className`, `position`, Button's `ariaLabel` and icon props) stay as
|
|
831
|
+
optional and deprecated so existing code keeps compiling.
|
|
832
|
+
|
|
833
|
+
### Fixed
|
|
834
|
+
|
|
835
|
+
- **`strings` props and `ZabiStringsProvider`:** a key set to `undefined` keeps
|
|
836
|
+
the English default instead of blanking the text (`null` and an empty string
|
|
837
|
+
are still honoured).
|
|
838
|
+
- **`Progress`:** a passed `id` is on the wrapper only (it appeared on the
|
|
839
|
+
wrapper and the bar); `aria-valuenow` is clamped to 0..`max`; a non-positive
|
|
840
|
+
or non-finite `max` falls back to 100; new `aria-label` and `aria-labelledby`
|
|
841
|
+
props name the progressbar.
|
|
842
|
+
- **`ColorPicker`:** Escape closes the popover and returns focus to the swatch
|
|
843
|
+
(and does not close a Modal around it); opening by keyboard moves focus in;
|
|
844
|
+
typing a hex redraws the colour map; with a `label`, the hex input and swatch
|
|
845
|
+
are named by it. Its popover could be placed below the viewport at 320px; a
|
|
846
|
+
press on a toast no longer closes it.
|
|
847
|
+
- **`Select`:** the popover listbox is named by the field's label (still "Select
|
|
848
|
+
options" with no label); **`Dropdown`** gets an optional `ariaLabelledby`
|
|
849
|
+
prop. Arrow keys get past a disabled option. The Select trigger's pressed
|
|
850
|
+
state shows in light (was the resting fill) and is 1.27:1 against rest in
|
|
851
|
+
light and 1.31:1 in dark (was 1.10:1). A Select no longer changes its value
|
|
852
|
+
when the page hydrates. A disabled Select dims its value. Dropdown and Select
|
|
853
|
+
return focus to the trigger when the menu closes from inside it; focus your
|
|
854
|
+
own handler moved elsewhere is left alone. A Select list and a Dropdown menu
|
|
855
|
+
are one Tab stop: Shift+Tab goes to the search field in one step, and a
|
|
856
|
+
character typed on an option goes to the search field or the matching item.
|
|
857
|
+
A press on a toast no longer closes an open Select or Dropdown.
|
|
858
|
+
- **`SwipeableListItem`:** a long action label wraps (it was clipped at the start
|
|
859
|
+
at 200% text), the action area is capped at two thirds of the row, and the
|
|
860
|
+
"more" button stays 44px at any text size. With `showMoreButton={false}` focus
|
|
861
|
+
has nowhere to return to after an action.
|
|
862
|
+
- A long Modal title no longer squeezes the close button or runs out of the
|
|
863
|
+
panel (the title wraps, also inside a single long word; the close button keeps
|
|
864
|
+
32px).
|
|
865
|
+
- A Tooltip with `strategy="fixed"` no longer covers its trigger when it opens
|
|
866
|
+
above or to the left; a closed tooltip no longer widens the page; it has an
|
|
867
|
+
edge in forced colours, is centred correctly in RTL and its arrow points at its
|
|
868
|
+
trigger in RTL; only one tooltip is open at a time and the pointer can cross a
|
|
869
|
+
diagonal to its far corner. Escape with a tooltip showing dismisses the
|
|
870
|
+
tooltip only, not the modal or sheet it sits in.
|
|
871
|
+
- A Dropdown stays on screen: measured before paint, it flips when the preferred
|
|
872
|
+
side does not fit, slides back when neither does and is capped to the viewport
|
|
873
|
+
with its own scroll only when it must be (8px margin). `placement` is still
|
|
874
|
+
the preferred side; a menu that fits is positioned as before. A Dropdown, Select
|
|
875
|
+
list or NavigationMenu panel is no longer cut off by a scrolling or clipping
|
|
876
|
+
ancestor such as a Modal's content (the nearest clipping ancestor decides the
|
|
877
|
+
side; when it fits on neither it is positioned against the viewport; an
|
|
878
|
+
ancestor with a transform or filter cannot be escaped), and a menu leaves a
|
|
879
|
+
clipping container only when that puts it on screen. A height-capped Dropdown
|
|
880
|
+
scrolls inside its rounded panel. Dropdown follows the writing direction
|
|
881
|
+
(`bottom-start` and `top-start` sit on the start edge, item text aligns to it).
|
|
882
|
+
- NavigationMenu fits a narrow screen: the list wraps onto further rows and a
|
|
883
|
+
content panel slides back inside the viewport. A press on a toast no longer
|
|
884
|
+
closes a NavigationMenu panel or the TopNavbar menu.
|
|
885
|
+
- TopNavbar's phone menu closes after a link in it is followed, when
|
|
886
|
+
`currentPath` changes, on Escape (focus returns to the menu button), on a click
|
|
887
|
+
or focus move outside the bar and when the screen widens past the breakpoint;
|
|
888
|
+
a press on a control behind the open menu still reaches it. It is at most as
|
|
889
|
+
tall as the screen below the bar and scrolls, allows for the top safe area,
|
|
890
|
+
its links fill the row, and `aria-controls` on the menu button is set only
|
|
891
|
+
while the menu exists. The menu button stays on screen with a long brand or
|
|
892
|
+
enlarged text (the brand truncates below the breakpoint).
|
|
893
|
+
- Toasts fit a 320px screen (the 18rem minimum width no longer overflows); the
|
|
894
|
+
close button takes taps in its full 44px area; `aria-controls` on the expand
|
|
895
|
+
button is set only while the detail is open; focus no longer falls to the page
|
|
896
|
+
after "Okay" or "Click to stop" is pressed; dismissing the focused toast moves
|
|
897
|
+
focus to the next toast or back to where it was; a tap neither sticks nor
|
|
898
|
+
dismisses; toasts do not fly in or out under `prefers-reduced-motion`.
|
|
899
|
+
- A disabled Checkbox, Radio or RadioGroup row no longer dips, changes fill or
|
|
900
|
+
shows the pressed fill when pressed or hovered; stacked checkbox and radio
|
|
901
|
+
rows no longer overlap on touch; the row shows its hover
|
|
902
|
+
(`--color-surface-hover`, `--color-surface-active`, was a fixed `base-100`).
|
|
903
|
+
Checkbox shows its loading ring (replacing the tick, fading under reduced
|
|
904
|
+
motion).
|
|
905
|
+
- Alert's close button shows the library's focus ring and mirrors in RTL.
|
|
906
|
+
- State variants beside a semantic colour class work: the hand-written colour
|
|
907
|
+
classes sit outside every cascade layer, so `text-description
|
|
908
|
+
hover:text-headline` never changed on hover. Thirteen such variants the
|
|
909
|
+
components use are restated by hand (the hover colour of close buttons, the
|
|
910
|
+
hover border of outline buttons, the disabled text colour of fields and ghost
|
|
911
|
+
buttons).
|
|
912
|
+
- Accent hover and pressed states work: `hover:bg-accent-hover` and
|
|
913
|
+
`active:bg-accent-active` did nothing next to `bg-accent`.
|
|
914
|
+
- An overlay opened later is always on top: Modal and SlideUp take a z-index
|
|
915
|
+
that grows with the number of open overlays, so a modal opened from a portalled
|
|
916
|
+
one no longer opens behind it; a modal opened while a portalled drawer is open
|
|
917
|
+
(and the reverse) is drawn on top.
|
|
918
|
+
- Modal and SlideUp keep Tab inside, and still close on Escape, when focus has
|
|
919
|
+
fallen out of the dialog because the focused control was disabled or removed.
|
|
920
|
+
A modal or sheet with nothing focusable takes focus itself. Modal, Drawer,
|
|
921
|
+
SlideUp and BottomSheet no longer take focus from content that focused itself
|
|
922
|
+
inside the panel as it opened. Focus returns to a replaced element (the one
|
|
923
|
+
that now has the opener's id) instead of `<body>`. Modal and SlideUp do not
|
|
924
|
+
slide in under `prefers-reduced-motion`.
|
|
925
|
+
- The focus ring survives a shadow: `.focus-ring` on an element with a `shadow-*`
|
|
926
|
+
or `ring-*` utility (also `shadow-none`) drew no ring (the SidebarNavigation
|
|
927
|
+
search field, the selected SidebarPanel row, an interactive Card, any
|
|
928
|
+
IconButton given a shadow); it now composes. A List no longer clips the focus
|
|
929
|
+
ring of its rows.
|
|
930
|
+
- Placeholder contrast: placeholder text and the format hint of an empty
|
|
931
|
+
DateField or TimeField was 3.40:1 on a dark field and 4.40:1 on a hovered light
|
|
932
|
+
one; see Upgrade notes. A disabled field's placeholder is no stronger than a
|
|
933
|
+
disabled value.
|
|
934
|
+
- Native controls follow the dark theme under `.dark` as well as `data-theme`
|
|
935
|
+
(scrollbars, date and time pickers, other native form controls, autofill); a
|
|
936
|
+
page that sets `color-scheme` on `<html>` keeps its value.
|
|
937
|
+
- Hover colours no longer stick after a tap: the hand-written `:hover` rules in
|
|
938
|
+
the theme files (`.bg-action-primary:hover` and 41 more) now sit in `@media
|
|
939
|
+
(hover: hover)`.
|
|
940
|
+
- ImageUpload keeps keyboard focus (to Change after a selection, to the dropzone
|
|
941
|
+
after Remove, untouched when focus is elsewhere), cancels a file dropped while
|
|
942
|
+
`disabled`, its actions layer no longer takes clicks meant for the preview, its
|
|
943
|
+
error message is tied to the Change button when a preview is showing, and a
|
|
944
|
+
disabled dropzone no longer takes the primary border on hover.
|
|
945
|
+
- ActionPanel shows its hover and pressed states (the fill is drawn over the
|
|
946
|
+
card). A selected Tabs pill shows its pressed state and a selected List row its
|
|
947
|
+
selected border. ListItem rows show a hover and a pressed fill.
|
|
948
|
+
SidebarFooter's avatar ring used a colour token that does not exist and now uses
|
|
949
|
+
the border colour. A `dark:` variant in Section's accent background, which
|
|
950
|
+
followed the OS setting and never applied, was removed.
|
|
951
|
+
- ThemeToggle shows the right icon before it mounts (both icons are in the
|
|
952
|
+
markup and the `dark` class picks one); it works with `data-theme`.
|
|
953
|
+
- ConfirmDialog no longer draws a focus outline around the whole dialog while
|
|
954
|
+
loading and sets no `aria-busy` on the panel.
|
|
955
|
+
- A Badge with a long label wraps: it is at least 20, 24 or 28px tall, grows when
|
|
956
|
+
the label wraps and is never wider than its container; a one-line badge is
|
|
957
|
+
unchanged to the pixel and a wrapped one reads as a rounded rectangle.
|
|
958
|
+
- An AppBar's title is never squeezed out (see AppBar).
|
|
959
|
+
- Keyboard focus is visible in forced colours (see Accessibility).
|
|
960
|
+
- **`zabi-components/types` matches the components again.** Every exported
|
|
961
|
+
`*Props` interface declares the props its component accepts today: `ModalProps`
|
|
962
|
+
has `isOpen`, the interfaces have `class`, and the new props are there.
|
|
963
|
+
`SelectProps.options`, `AlertProps.message` and `TooltipProps.content` are
|
|
964
|
+
optional, as on the components.
|
|
965
|
+
- A bindable prop bound to `undefined` no longer stops the page from hydrating
|
|
966
|
+
(RadioGroup threw, and 36 other bindable props could); see Upgrade notes.
|
|
967
|
+
- Button and IconButton (including `variant="accent"`) do not scale when pressed
|
|
968
|
+
under `prefers-reduced-motion`; built-in loading rings (Button, IconButton,
|
|
969
|
+
Input, Textarea, ActionPanel, Toggle) fade instead of spinning, Checkbox's
|
|
970
|
+
ring stops; Toggle's loading ring has its gap back.
|
|
971
|
+
- Input, Textarea and Select no longer point `aria-describedby` at a message that
|
|
972
|
+
is not rendered.
|
|
973
|
+
- `fullWidth` Tabs scroll instead of breaking a word; BottomSheet's grip answers
|
|
974
|
+
a mouse click; a flick on a sheet is measured from the events' own times.
|
|
975
|
+
- Drawer content clears the home indicator; MediaGrid's remove button shows a
|
|
976
|
+
pressed state on touch; SidebarAccountPanel's three-mode theme row follows a
|
|
977
|
+
theme changed elsewhere; CodeBlock announces "copied".
|
|
978
|
+
- The brand menu in the docs site header stays on screen (it opened past the
|
|
979
|
+
right edge at 768px and 1024px); the Pine and Citron accents follow the Iris
|
|
980
|
+
recipe in light (fill at step 600 with a white label, focus ring at step 600;
|
|
981
|
+
Pine's light ring was 1.93:1 on the page, now 4.19:1); `theme-color` follows
|
|
982
|
+
the theme toggle; the site's cards carry `shadow-sm` and the showcase uses
|
|
983
|
+
surface tokens in place of raw ramp steps.
|
|
984
|
+
- README links to RELEASING.md point at the repository, and the README says to
|
|
985
|
+
install `@lucide/svelte` and import icons per file.
|
|
986
|
+
|
|
987
|
+
## [8.0.1] - 2026-09-29
|
|
988
|
+
|
|
989
|
+
Fixes the documented setup. In 8.0.0 (and 7.0.2) the three-line Tailwind setup
|
|
990
|
+
from the README left components unstyled; `@import "zabi-components/css"` was
|
|
991
|
+
the only import that worked. No tokens or `exports` paths were added, removed
|
|
992
|
+
or renamed.
|
|
993
|
+
|
|
994
|
+
### Fixed
|
|
995
|
+
|
|
996
|
+
- **Theme files are valid CSS again.** `theme`, `theme-only`, `theme-dark` and
|
|
997
|
+
`theme-dark-only` were written without semicolons between declarations, so a
|
|
998
|
+
CSS parser read all 355 tokens as one declaration. `scripts/build-css.js` now
|
|
999
|
+
terminates each declaration.
|
|
1000
|
+
- **`theme` and `theme-only` carry an `@source` directive** for the package's
|
|
1001
|
+
own components. Tailwind CSS v4 does not scan `node_modules`, so the classes
|
|
1002
|
+
the components use were never generated. No `@source` line is needed in your
|
|
1003
|
+
own CSS.
|
|
1004
|
+
- **`theme` and `theme-only` ship the hand-written component rules**:
|
|
1005
|
+
`.focus-ring` and its variants, `.text-action-primary`, the action hover,
|
|
1006
|
+
active and disabled states, the semantic colour classes and the `z-*` scale.
|
|
1007
|
+
These existed only in the compiled `css` bundle. Without them a primary
|
|
1008
|
+
button's label took the colour of its fill, and nothing had a focus ring.
|
|
1009
|
+
- **ColorPicker renders on the server.** Its teardown ran in `onDestroy`, which
|
|
1010
|
+
also runs during SSR, and threw `window is not defined`. It is now returned
|
|
1011
|
+
from `onMount`.
|
|
1012
|
+
- **Progress has an accessible name.** Its label was a `<label for>`, which
|
|
1013
|
+
only names form controls, so screen readers announced a progressbar with no
|
|
1014
|
+
name. The label is now referenced through `aria-labelledby`.
|
|
1015
|
+
|
|
1016
|
+
### Changed
|
|
1017
|
+
|
|
1018
|
+
- The universal-selector scrollbar rules (`* { scrollbar-width: thin }` and the
|
|
1019
|
+
`*::-webkit-scrollbar` family) are in the compiled `css` bundle only, not in
|
|
1020
|
+
the theme files, so importing a theme does not restyle every scrollbar in
|
|
1021
|
+
your app. `.scrollbar-semantic` is in both and is the opt-in.
|
|
1022
|
+
- `theme` and `theme-only` grew from about 22 kB to about 35 kB.
|
|
1023
|
+
- `scripts/validate-theme.js` parses the theme files instead of pattern-matching
|
|
1024
|
+
them, and fails the build if the token block does not parse or the component
|
|
1025
|
+
rules are missing. The old check passed on the broken files.
|
|
1026
|
+
|
|
1027
|
+
### Migration
|
|
1028
|
+
|
|
1029
|
+
Nothing to change if you import `zabi-components/css`. If you worked around the
|
|
1030
|
+
broken theme files with your own `@source` line or your own copies of
|
|
1031
|
+
`.focus-ring` or `.text-action-primary`, you can remove them.
|
|
1032
|
+
|
|
1033
|
+
## [8.0.0] - 2026-09-22
|
|
1034
|
+
|
|
1035
|
+
Consolidates two development cycles. 7.1.0 and 7.2.0 were both prepared but
|
|
1036
|
+
never published, so the last release on npm is 7.0.2 and everything below ships
|
|
1037
|
+
together in 8.0.0.
|
|
1038
|
+
|
|
1039
|
+
### Breaking changes since 7.0.2
|
|
1040
|
+
|
|
1041
|
+
What can break a working 7.0.2 consumer. Each item is detailed in the sections
|
|
1042
|
+
that follow.
|
|
1043
|
+
|
|
1044
|
+
- **The `zabi-components/react` subpath is gone** from `exports`. It pointed at
|
|
1045
|
+
`dist/react`, which was never built, so importing from it already failed at
|
|
1046
|
+
resolution — but the subpath no longer exists at all.
|
|
1047
|
+
- **Three tokens were removed**: `--color-action-primary-disabled`,
|
|
1048
|
+
`--color-action-secondary-disabled`, `--color-action-danger-disabled`. Use the
|
|
1049
|
+
shared `--color-action-disabled`, `--color-action-disabled-text` and
|
|
1050
|
+
`--color-action-disabled-border` instead. Your own CSS referencing the old
|
|
1051
|
+
names will silently resolve to nothing, so grep for them before upgrading.
|
|
1052
|
+
- **Every chromatic ramp was regenerated** against a shared lightness curve, and
|
|
1053
|
+
the semantic families moved from step 500 to step 600. Rendered colour changes
|
|
1054
|
+
throughout, in both themes, with no opt-out. Screenshot tests will all churn.
|
|
1055
|
+
- **`max-w-*`, `w-*` and `min-w-*` with `xs`–`2xl` resolve to container sizes
|
|
1056
|
+
again** (`max-w-lg` is 32rem, not 1.5rem). This is a bug fix, but anyone who
|
|
1057
|
+
compensated for the broken values in their own CSS must undo that.
|
|
1058
|
+
- **No install scripts run in consumer installs.** The `postinstall` hook is
|
|
1059
|
+
gone; it is now a dev-only `prepare` script and is not in the tarball.
|
|
1060
|
+
|
|
1061
|
+
Not breaking, but worth knowing: the published tarball no longer contains
|
|
1062
|
+
`dist/lib/**`. Those were declaration files for this repo's demo site and
|
|
1063
|
+
showcase, emitted by mistake; no `exports` entry ever pointed at them.
|
|
1064
|
+
|
|
1065
|
+
### Design system
|
|
1066
|
+
|
|
1067
|
+
Outcome of a full design review of all 63 components. The headline change is
|
|
1068
|
+
that the colour ramps are now generated against one shared lightness curve, so
|
|
1069
|
+
the semantic families read as a single family instead of six unrelated colours.
|
|
1070
|
+
|
|
1071
|
+
#### Colour — BREAKING (visual)
|
|
1072
|
+
|
|
1073
|
+
- **All chromatic ramps regenerated** (`brand`, `citron`, `pine`, `iris`,
|
|
1074
|
+
`warning`, `error`) against a shared CIE L\* curve defined in
|
|
1075
|
+
`tokens/chromatic-scales.js`. `<ramp>-600` now means the same lightness in
|
|
1076
|
+
every ramp. Previously the ramps spanned L\* 34.6–75.0 at step 500 — a
|
|
1077
|
+
40-point spread at the same nominal step — and `pine` fell 29.8 points between
|
|
1078
|
+
400 and 500, which made half that ramp unusable.
|
|
1079
|
+
- **Semantic tokens moved to step 600** across the board. `warning`, `error` and
|
|
1080
|
+
`energetic` previously sat at step 500 (the dark mirror's fixed point) and so
|
|
1081
|
+
did not invert between themes at all, while `success` and `info` did. Solid
|
|
1082
|
+
badges were 2.15:1 (warning) and 1.97:1 (energetic) in light mode.
|
|
1083
|
+
- **New per-family tokens**: `--color-<family>-subtle` (step 100),
|
|
1084
|
+
`--color-<family>-border` (step 200). `--color-<family>-text` is now step 700
|
|
1085
|
+
for every family.
|
|
1086
|
+
- **`--color-action-primary` is now `brand-600`** (was `brand-950`). The primary
|
|
1087
|
+
action carries the brand colour in both themes.
|
|
1088
|
+
- **`--color-action-primary-active` no longer jumps across the ramp.** It was
|
|
1089
|
+
`brand-400` against a `brand-50` label — 1.66:1, so the label vanished while
|
|
1090
|
+
the button was held. Hover/active now step darker in light, lighter in dark.
|
|
1091
|
+
- **`--color-action-secondary`** is a neutral alpha tint instead of a 10% brand
|
|
1092
|
+
wash, which was too pale to read as a control.
|
|
1093
|
+
- **New**: `--color-action-disabled`, `--color-action-disabled-text`,
|
|
1094
|
+
`--color-action-disabled-border` — one quiet neutral pair shared by every
|
|
1095
|
+
variant. Disabled was previously `brand-500` + `opacity-50`, which made a
|
|
1096
|
+
disabled primary *lighter and more colourful* than an enabled secondary.
|
|
1097
|
+
- **New**: `--color-surface-hover`, `--color-surface-active` — surface-relative
|
|
1098
|
+
alpha tints. `hover:bg-base-100` resolved to exactly `--color-surface-base` in
|
|
1099
|
+
dark mode, so ghost buttons had no hover feedback on the app shell.
|
|
1100
|
+
- **New**: `--color-control-track{,-hover,-active}` for switch rails.
|
|
1101
|
+
- **Removed**: `--color-action-{primary,secondary,danger}-disabled` (use
|
|
1102
|
+
`--color-action-disabled`); the raw-hex `.dark` danger overrides (danger now
|
|
1103
|
+
mirrors through `--color-error-*` like every other family).
|
|
1104
|
+
- `--color-input` is now a raised surface (white in light, an inset step in
|
|
1105
|
+
dark) rather than a grey well that read as disabled.
|
|
1106
|
+
- `--color-input-placeholder` is wired up — it was defined but unused.
|
|
1107
|
+
- `--color-link` moved to `brand-700`: `brand-600` cleared AA on a card but only
|
|
1108
|
+
managed 4.42:1 on the page surface.
|
|
1109
|
+
|
|
1110
|
+
#### Geometry — BREAKING (visual)
|
|
1111
|
+
|
|
1112
|
+
- **One control height scale**: `sm` 32px, `md` 40px, `lg` 48px, shared by
|
|
1113
|
+
Button, IconButton, Input, Select and Textarea. They previously rendered at
|
|
1114
|
+
40/48/64, 32/40/48 and 38/46/50 — a `lg` Button stood 14px taller than the
|
|
1115
|
+
`lg` Input beside it.
|
|
1116
|
+
- **Four role-based radii** replace eight t-shirt radii: `--radius-control`
|
|
1117
|
+
(8px), `--radius-container` (12px), `--radius-overlay` (16px),
|
|
1118
|
+
`--radius-pill`. Radius no longer changes with size. `--radius-sm/md/lg/xl`
|
|
1119
|
+
remain as legacy aliases for consumer overrides.
|
|
1120
|
+
- Button type is monotonic again: weight is `font-medium` at every size (it used
|
|
1121
|
+
to drop to `font-normal` at `lg`, so the biggest button had the lightest
|
|
1122
|
+
label), and the arbitrary letter-spacing values are gone.
|
|
1123
|
+
- **Elevation is two steps**, not four. `shadow-sm` means raised (cards, tables,
|
|
1124
|
+
sidebars, toggle knobs), `shadow-lg` means floating (modals, sheets, menus,
|
|
1125
|
+
toasts); `shadow-none` stays the explicit absence of one. `shadow-md` and
|
|
1126
|
+
`shadow-xl` are gone, and so is the bare `shadow` on the Toggle knob, which
|
|
1127
|
+
resolved to a Tailwind default `app.css` never defined. An elevated Card no
|
|
1128
|
+
longer deepens its shadow on hover — it was already at the floating step, so
|
|
1129
|
+
the hover now changes background only.
|
|
1130
|
+
- **Layout spacing snaps to the 4px grid.** 30 half-step utilities
|
|
1131
|
+
(`gap-1.5`, `gap-2.5`, `px-2.5`, `px-3.5`, `py-2.5`, `space-y-1.5`, …) were
|
|
1132
|
+
setting a second rhythm against the first across forms, sidebars and badges.
|
|
1133
|
+
Margins are deliberately exempt: `mt-0.5` beside a first line of text is an
|
|
1134
|
+
optical alignment nudge, not rhythm, and Alert, ListItem, ToasterToast and the
|
|
1135
|
+
radio control still use it.
|
|
1136
|
+
- A one-row `Textarea` is now 40px like the `Input` beside it (was 44px, from
|
|
1137
|
+
`py-2.5`).
|
|
1138
|
+
|
|
1139
|
+
#### Components
|
|
1140
|
+
|
|
1141
|
+
- **`Badge`**: rebuilt. New `emphasis` prop (`subtle` | `solid`, default
|
|
1142
|
+
`subtle`), plus `class`, `children` and `...restProps` — none of which existed,
|
|
1143
|
+
despite stories shipping for two of them. Pill radius instead of 2px. Renders
|
|
1144
|
+
nothing when it has no label, rather than leaving an empty box.
|
|
1145
|
+
- **`Alert`**: tinted fill + tinted border instead of a bare 1px outline on a
|
|
1146
|
+
transparent ground; full-width by default with a new `inline` prop; one Lucide
|
|
1147
|
+
icon family instead of hand-rolled SVGs of mixed weight.
|
|
1148
|
+
- **`Heading`**: no longer hardcodes `font-family: "Nunito Sans"` in a local
|
|
1149
|
+
`<style>` block — it uses `--font-family-sans`, so rebranding the font token
|
|
1150
|
+
now actually rebrands headings. `level` is typed `1 | 2 | 3 | 4 | 5 | 6`
|
|
1151
|
+
instead of `number`; new `size` prop decouples visual size from semantic
|
|
1152
|
+
level; `children` snippet supported; display sizes carry negative tracking.
|
|
1153
|
+
- **New `SidebarShell`**: the chrome of a sidebar with the regions left open —
|
|
1154
|
+
width, surface, collapse behaviour, the shared inset and a scrolling middle.
|
|
1155
|
+
Regions are snippets (`header` / `children` / `footer`), each handed
|
|
1156
|
+
`{ collapsed, insetX }`. `SidebarNavigation` is 39 props on one component and
|
|
1157
|
+
is excellent right up until your sidebar is not shaped like that one, at
|
|
1158
|
+
which point none of the parts are reachable; compose `SidebarBrandHeader`,
|
|
1159
|
+
`SidebarNavSection` and `SidebarFooter` into the shell instead and skip the
|
|
1160
|
+
~30 props you never set. `SidebarNavigation` is now built on it rather than
|
|
1161
|
+
duplicating the chrome, so the two cannot drift. Additive — its prop API is
|
|
1162
|
+
unchanged.
|
|
1163
|
+
- **`SidebarNavigation`**: its Props table documented 7 of 39 props. All 39 are
|
|
1164
|
+
documented now, with types and defaults.
|
|
1165
|
+
- **`Text`**: now sits on the same type ramp as `Heading` rather than carrying
|
|
1166
|
+
its own — `size="md"` matches an `h6` and `size="lg"` an `h5`, line box
|
|
1167
|
+
included, and a new `xs` step (12px) extends the bottom. Leading is stated
|
|
1168
|
+
rather than inherited from Tailwind's defaults, so the shared steps stay
|
|
1169
|
+
locked together if the scale is retuned. **New `weight` prop** (`normal` |
|
|
1170
|
+
`medium` | `semibold` | `bold`) lets body copy carry emphasis without being
|
|
1171
|
+
promoted to a heading; `tone="label"` defaults to `medium`, every other tone
|
|
1172
|
+
to `normal`. Purely additive — existing `sm`/`md`/`lg` call sites are
|
|
1173
|
+
unchanged visually.
|
|
1174
|
+
- **`Input`**: raised surface, quieter placeholder, `min-w-48` removed.
|
|
1175
|
+
- **`Card`**: radius no longer scales with size; `outlined` uses `border-border`.
|
|
1176
|
+
|
|
1177
|
+
#### API consistency
|
|
1178
|
+
|
|
1179
|
+
- **Class overrides actually win now.** `class` being "merged last" only held
|
|
1180
|
+
for properties the component didn't already set: `rounded-control` and
|
|
1181
|
+
`rounded-container` are equal-specificity utilities, so the winner was
|
|
1182
|
+
whichever Tailwind emitted later. The sidebar's search field asked for
|
|
1183
|
+
`rounded-container` and rendered at 8px. Every component now merges through
|
|
1184
|
+
`cn()` (tailwind-merge, a new runtime dependency), with the role-based radii
|
|
1185
|
+
declared explicitly since `control`/`container`/`overlay`/`pill` are role
|
|
1186
|
+
names rather than scale values that tailwind-merge could recognise.
|
|
1187
|
+
- **`class` is now the public prop on every component.** 25 components exposed
|
|
1188
|
+
`className`, 15 exposed `class`, and 21 exposed neither. `className` is kept
|
|
1189
|
+
everywhere as a deprecated alias and both are merged, so existing call sites
|
|
1190
|
+
keep working.
|
|
1191
|
+
- Every component spreads `...restProps` onto its host element.
|
|
1192
|
+
- `Button`: `fullWidth` replaces `isFullWidth` (deprecated alias retained).
|
|
1193
|
+
|
|
1194
|
+
#### Guardrails
|
|
1195
|
+
|
|
1196
|
+
- **New** `scripts/check-ramp-lightness.js` — fails if a ramp drifts off the
|
|
1197
|
+
shared curve (±1.5 L\*) or develops a cliff (>15 L\* between adjacent steps).
|
|
1198
|
+
- **New** `scripts/check-contrast.js` — resolves every fill/foreground pair a
|
|
1199
|
+
component can render, in both themes, and fails below WCAG AA. 84 pairs.
|
|
1200
|
+
- **New** `scripts/check-control-geometry.js` — fails if controls disagree on
|
|
1201
|
+
height, or if any component uses a t-shirt radius.
|
|
1202
|
+
- **New** `scripts/generate-ramps.js` + `tokens/chromatic-scales.js` — ramp
|
|
1203
|
+
generation, wired into `npm run sync:tokens`.
|
|
1204
|
+
- `scripts/check-token-violations.js` now also rejects fixed ramp steps in
|
|
1205
|
+
interaction states (`hover:bg-base-*`, `active:bg-base-*`), which is how the
|
|
1206
|
+
invisible ghost hover got in.
|
|
1207
|
+
- **New** `scripts/generate-surfaces.js` + `tokens/surface-ladder.js` — the four
|
|
1208
|
+
dark surface levels are now derived from one wash ladder instead of four
|
|
1209
|
+
independently tuned hex values, and regenerate with `npm run sync:tokens`.
|
|
1210
|
+
`--color-surface-elevated` moves `#35353a` → `#363638` and
|
|
1211
|
+
`--color-surface-overlay` `#44444c` → `#454547` (slightly less blue); base and
|
|
1212
|
+
raised were already exactly on the ladder.
|
|
1213
|
+
- The ramp-interaction rule now also scans `src/lib/marketing` and the landing
|
|
1214
|
+
route. The rest of the checks guard what ships in the package, but
|
|
1215
|
+
`hover:bg-base-100` had reached two marketing specimens, where dark mode
|
|
1216
|
+
painted the hover darker than the card it sat on.
|
|
1217
|
+
- `scripts/check-token-violations.js` also rejects **off-scale shadows**
|
|
1218
|
+
(anything but `shadow-sm` / `shadow-lg` / `shadow-none`, including a bare
|
|
1219
|
+
`shadow`) and **half-step spacing** on gap/space/padding utilities. Margins
|
|
1220
|
+
are exempt by design. Comment lines are skipped so prose about shadow DOM
|
|
1221
|
+
doesn't trip it.
|
|
1222
|
+
- New scripts: `npm run check:ramps`, `check:contrast`, `check:geometry`, and
|
|
1223
|
+
`check:design` (all four). `build:css` and `check` run `check:design`.
|
|
1224
|
+
|
|
1225
|
+
#### Stories
|
|
1226
|
+
|
|
1227
|
+
- `Badge` → `AllVariantsWithIcons` no longer crashes. It used an invalid CSF
|
|
1228
|
+
render shape (`Component: 'div'` with a `children` array) cast through
|
|
1229
|
+
`as any`, so TypeScript could not catch it and it threw at runtime. Replaced
|
|
1230
|
+
with real harness components, which also fixes `WithChildren` and
|
|
1231
|
+
`WithCustomClass` (both silently rendered nothing).
|
|
1232
|
+
|
|
1233
|
+
### Forms
|
|
1234
|
+
|
|
1235
|
+
- **`ContactForm`**: invalid submissions no longer fall through to a native POST and page reload.
|
|
1236
|
+
- **`ImageUpload`**: `onchange` / `onclick` are now actually called; `value` supports `bind:value`; new `onfileselect({ file, url })` exposes the selected `File`. Change/Remove actions are visible on keyboard focus, and Space activates the drop zone.
|
|
1237
|
+
- **`ColorPicker`**: 3-digit hex (`#f00`) is parsed correctly; `value` supports `bind:value`.
|
|
1238
|
+
- **`Form`**: accepts standard form attributes and a `novalidate` prop; docs note that `onsubmit` handlers must call `preventDefault()` for client-side handling.
|
|
1239
|
+
|
|
1240
|
+
### Widgets
|
|
1241
|
+
|
|
1242
|
+
- **`Tabs`**: Arrow/Home/End move focus as well as selection; the tablist is no longer an extra tab stop; `activeTab` supports `bind:activeTab`.
|
|
1243
|
+
- **`Select`**: new `name` prop renders a hidden input for native form submission; the search field sits outside the `listbox`; string/number option values compare consistently.
|
|
1244
|
+
- **`Dropdown`**: new optional `header` snippet; Home/End/Space behave normally inside text inputs in the menu.
|
|
1245
|
+
- **`Toggle`**: new `id`, `name` and `value` props for label pairing and form submission.
|
|
1246
|
+
- **`ThemeToggle`**: a consumer `onclick` no longer overrides the toggle; pre-mount placeholder respects `size`/`variant` (no layout shift).
|
|
1247
|
+
|
|
1248
|
+
### Overlays
|
|
1249
|
+
|
|
1250
|
+
- **`Modal` / `SlideUp`**: `role="dialog"` moved from the backdrop to the panel; body scroll locked while open (nested-safe); focus trap picks up content added while open; Escape closes only the topmost dialog. `Modal` gains `showClose` (default `true`) so untitled modals still have a close button.
|
|
1251
|
+
- **`Toaster`**: countdown is no longer announced every second; it pauses on hover/focus; no double announcements from nested live regions.
|
|
1252
|
+
- **`NavigationMenu`**: fixed a leaked document `mousedown` listener; panels use the disclosure pattern (no `role="menu"` / `aria-haspopup`); new `ariaLabel` prop.
|
|
1253
|
+
- **`Dropdown`**: arrow keys, Home and End now reach `menuitemradio` and `menuitemcheckbox` items. The item query matched only `menuitem` and `option`, so a menu built from radio items had nothing to focus: opening it by keyboard focused nothing and Tab closed it, leaving the items unreachable.
|
|
1254
|
+
- **`Alert`**: `closable` now hides the alert; new bindable `open` prop.
|
|
1255
|
+
- **`Tooltip`**: Escape no longer steals focus; CSS custom properties no longer leak onto `:root`.
|
|
1256
|
+
- **`util/ssr-safe`**: timer helpers typed with `ReturnType<typeof setTimeout/setInterval>` instead of `NodeJS.Timeout`, so consumers don't need `@types/node`.
|
|
1257
|
+
|
|
1258
|
+
### Surfaces
|
|
1259
|
+
|
|
1260
|
+
- **New semantic surface elevation tokens** (both themes), lowest → highest: `--color-surface-base` (page), `--color-surface-raised` (cards, panels, sidebars), `--color-surface-elevated` (nested cards, hover/active fills), `--color-surface-overlay` (modals, sheets, menus, toasts), plus `--color-surface-overlay-hover` (row/icon hover inside overlays) and `--color-border-overlay` (1px overlay edge: transparent in light, visible in dark). Utilities: `bg-surface-base`, `bg-surface-raised`, `bg-surface-elevated`, `bg-surface-overlay`, `bg-surface-overlay-hover`, `border-border-overlay`.
|
|
1261
|
+
- **Dark mode elevation no longer relies on shadows**: levels step up +6 OKLCH lightness each (L 21 → 27 → 33 → 39) on the neutral base hue. Light mode is visually unchanged.
|
|
1262
|
+
- **Existing tokens are now aliases** (no renames): `background` → `surface-base`; `card` → `surface-raised`; `surface-1` → `surface-raised`; `surface-2` → `surface-elevated`; `card-elevated` → `surface-overlay` (light) / `surface-elevated` (dark); in dark, `card-hover` → `surface-elevated`, `card-active` and `surface-3` → `surface-overlay`. Dark values of `card`, `card-hover`, `card-active`, `surface-1`–`surface-3` shift slightly to land on the new levels.
|
|
1263
|
+
- **Floating components use the overlay level**: `Modal`, `SlideUp`, `Dropdown` (and `Select`'s menu), `NavigationMenu` panels, `Toaster` toasts and the `Toast` atom now paint `bg-surface-overlay` (previously `bg-card` / `bg-surface-1`, which in dark matched or undercut the card they floated over). Hover fills inside them use `bg-surface-overlay-hover` so they read above the overlay. `Modal`/`SlideUp` get a `border-border-overlay` edge. `Card variant="elevated"` uses `bg-card-elevated`.
|
|
1264
|
+
- **Dark `--color-description`** moves one step lighter (`base-650`) so description text keeps ≥ 4.5:1 contrast on every surface level, including overlays.
|
|
1265
|
+
- **New guard**: `scripts/check-surface-elevation.js` (run by `validate-theme.js` during `build:css`) fails the build if dark levels aren't strictly increasing, any step is outside 5–8 OKLCH L points, overlay hover/tooltip fills are darker than the overlay, or a floating component paints a surface below overlay.
|
|
1266
|
+
|
|
1267
|
+
### Theme fixes
|
|
1268
|
+
|
|
1269
|
+
- **`max-w-*`, `w-*` and `min-w-*` with `xs`–`2xl` now use Tailwind's container sizes again** (`max-w-lg` = 32rem). Tailwind v4 resolves these utilities through `--spacing-*` before `--container-*`, so the theme's named spacing tokens made `max-w-lg` resolve to 1.5rem for anyone importing the theme. New `--max-width-*`, `--width-*` and `--min-width-*` tokens (xs 20rem … 2xl 42rem) are checked first. Spacing utilities such as `p-lg` / `gap-md` are unchanged.
|
|
1270
|
+
- **`--color-caption` meets WCAG AA on every surface level**: light `base-500` → `base-550` (was 4.40:1 on the page background, now ≥ 5.58:1); dark `base-500` → `base-650` (was 2.00:1 on overlays, now ≥ 5.00:1). To keep caption visibly softer than description in dark mode, dark `--color-description` moves from `base-650` to `base-700` (≥ 6.53:1).
|
|
1271
|
+
- **New guard**: `scripts/check-surface-elevation.js` also fails the build if `headline`, `body`, `label`, `description` or `caption` drop below 4.5:1 on any surface level in either theme.
|
|
1272
|
+
- **`Modal`**: the title and close button line up with the body and footer. The inner `Card`'s own padding stacked on the header's padding and indented the title 24px past the content.
|
|
1273
|
+
|
|
1274
|
+
### Packaging
|
|
1275
|
+
|
|
1276
|
+
- **Removed `zabi-components/react`**: the export pointed at `dist/react`, which was never built, and the wrappers were out of sync with the Svelte components. The optional `react` peer dependency and `@types/react` are gone too.
|
|
1277
|
+
- **No more `postinstall` in consumer installs**: `scripts/fix-lucide-svelte-icon-dts.js` only ever patched this repo's own `node_modules` (from a consumer install it resolved to a non-existent nested path and did nothing). It now runs as a dev-only `prepare` script and is no longer shipped in the tarball. Installs with `--ignore-scripts` (pnpm/Bun defaults) are unaffected.
|
|
1278
|
+
- **`zabi-components/lib/ssr-safe` and `zabi-components/lib/variant-utils` now resolve to compiled `.js` + `.d.ts`** (`dist/util/ssr-safe.*`, `dist/routes/lib/variant-utils.*`) instead of raw `.ts` sources. Import paths are unchanged.
|
|
1279
|
+
- **`generateId` / `createId` from the package root** now use the shared `util/ssr-safe` implementation; the previous root version used `Date.now()` on the server, so two ids created in the same millisecond collided.
|
|
1280
|
+
- **`CodeBlock`**: window-chrome dots use semantic tokens (`bg-error` / `bg-warning` / `bg-success`) instead of raw Tailwind palette classes; props are now typed.
|
|
1281
|
+
- **`check:tokens`** also flags raw Tailwind palette utilities (e.g. `bg-red-500`) in `src/components`.
|
|
1282
|
+
- **Repo**: `dist/`, `.svelte-kit/` and `storybook-static/` are no longer tracked in git; unused Storybook template assets and `src/_tmp-lucide-import.ts` removed.
|
|
1283
|
+
- **CI**: Node 24; the smoke workflow now also runs `check`, unit tests, theme tests and the token scan; publish runs `check` and unit tests before building.
|
|
1284
|
+
|
|
1285
|
+
## [7.0.2] - 2026-04-20
|
|
1286
|
+
|
|
1287
|
+
### Changed
|
|
1288
|
+
|
|
1289
|
+
- **`Tooltip`**: Tooltip surface styling now uses theme tokens **`--color-tooltip-bg`** / **`--color-tooltip-fg`** (with **`bg-tooltip-bg`** / **`text-tooltip-fg`** utilities) and **`z-tooltip`**, keeping layout/arrow logic in scoped CSS while aligning colors with the design system.
|
|
1290
|
+
- **`Tooltip` layout**: Tooltip panel uses **`width: max-content`** with a responsive **`max-width`** cap (`min(24rem, calc(100vw - 2rem))`) so typical copy stays on one line until the limit; arrow fill follows **`--color-tooltip-bg`**.
|
|
1291
|
+
- **Theme (`src/app.css`)**: Defines **`--color-tooltip-bg`** / **`--color-tooltip-fg`** (defaults to base-800 / base-50); **`prefers-contrast: high`** maps them to **`CanvasText`** / **`Canvas`** for stronger contrast. Regenerated **`zabi-components*.css`** picks up the new utilities.
|
|
1292
|
+
|
|
1293
|
+
## [7.0.1] - 2026-04-19
|
|
1294
|
+
|
|
1295
|
+
### Fixed
|
|
1296
|
+
|
|
1297
|
+
- **`postinstall`**: The Lucide typings patch script is **included in the published package** (`scripts/fix-lucide-svelte-icon-dts.js` via `package.json` `files`), so `npm install zabi-components` no longer fails when lifecycle scripts run.
|
|
1298
|
+
- **Theme CSS (Tailwind v4)**: Replaced `theme(colors.*)` inside `@theme` / `.dark` with `var(--color-*)` for Zabi scales and stable hex literals for Tailwind default ramps (e.g. red/amber) and white, so consumers processing `theme-only` / `colors` bundles do not hit unresolved `theme()` during Tailwind passes.
|
|
1299
|
+
- **Props typings**: **`Button`**, **`IconButton`**, and **`Tooltip`** prop types now intersect native element attributes (`HTMLButtonAttributes` / `HTMLAttributes<HTMLDivElement>`) so passthrough attributes (`class`, ARIA, `data-*`, etc.) type-check when spread with `...restProps`.
|
|
1300
|
+
|
|
1301
|
+
### Added
|
|
1302
|
+
|
|
1303
|
+
- **`docs/lucide-icons.md`**: Lists Lucide icons imported by published components for teams using a custom `@lucide/svelte` barrel.
|
|
1304
|
+
|
|
1305
|
+
### Changed
|
|
1306
|
+
|
|
1307
|
+
- **`scripts/sync-theme-tokens.js`**: End marker after the mirrored base ramp now matches the **`Brand Color Scale - Dark mode variants`** comment (including the “no theme() forward refs” note).
|
|
1308
|
+
|
|
1309
|
+
### Migration / discovery
|
|
1310
|
+
|
|
1311
|
+
- **`Navbar` → `TopNavbar`**: The older **`Navbar`** export was removed in **6.0.0**; use **`TopNavbar`**. See the **6.0.0** migration notes in this file.
|
|
1312
|
+
|
|
1313
|
+
## [7.0.0] - 2026-04-19
|
|
1314
|
+
|
|
1315
|
+
### Breaking changes
|
|
1316
|
+
|
|
1317
|
+
- **`Page`**: Removed the built-in `max-w-4xl` cap. Width is now fully controlled by the parent, or pass e.g. `className="max-w-4xl"` (or use **`Container`** / **`Section`** with `maxWidth` per the layout-width policy).
|
|
1318
|
+
- **`EmptyState`**: Removed the built-in `max-w-lg` cap. Add `className="max-w-lg"` (or another token) if you still want a narrow empty state.
|
|
1319
|
+
|
|
1320
|
+
### Added
|
|
1321
|
+
|
|
1322
|
+
- **`postinstall`**: After `npm install`, runs `scripts/fix-lucide-svelte-icon-dts.js` to strip a duplicate `type X = ReturnType<typeof X>` line from each `@lucide/svelte` per-icon `dist/icons/*.svelte.d.ts` file. Some TypeScript language services treat the unpatched file as a non-module, which breaks types for deep icon imports and root re-exports.
|
|
1323
|
+
- **`fixedSidebarFlyout`**: New Svelte **action** in the built package (`util/fixed-sidebar-flyout.js`) used by **`SidebarFooter`**. It pins flyouts with `position: fixed` to `[data-sidebar-flyout-anchor]` inside `[data-sidebar-flyout-root]`, teleports the panel to `document.body` while open to avoid scroll clipping, and updates position on scroll/resize. Options: `open`, `align` (`"top"` | `"bottom"`), `gap`, `anchorRole` (`"search"` | `"profile"`).
|
|
1324
|
+
|
|
1325
|
+
### Changed
|
|
1326
|
+
|
|
1327
|
+
- **`CodeBlock`**: Replaced the ad-hoc copy **`<button>`** with **`IconButton`** (ghost, sm). Copy / copied states use Lucide **`Check`** and **`Copy`** from **`@lucide/svelte/icons/*`**. Wrapper uses **`bg-surface-1`**, **`border-border`**, header **`bg-surface-2`**; code uses **`text-base-900`** on light surfaces instead of **`bg-base-900`** / **`bg-base-*`** chrome with **`text-base-100`** body text.
|
|
1328
|
+
- **`SidebarFooter`**: Profile panel markup restructured with **`data-sidebar-flyout-root`** on the footer host; **`data-sidebar-flyout-anchor="profile"`** on the launcher; the profile snippet is positioned with **`fixedSidebarFlyout`** (`align: "bottom"`, viewport-limited **`max-height`**, **`overflow-y-auto`**, **`overscroll-contain`**). Drops the previous **`absolute bottom-0 left-full`** positioning that misbehaved beside fixed sidebars / scroll ancestors.
|
|
1329
|
+
- **`SidebarNavigation`**: Search trigger wrapped with **`data-sidebar-flyout-anchor="search"`** (collapsed and expanded) so flyouts share the same anchor protocol as the footer profile panel.
|
|
1330
|
+
- **Lucide usage in source**: Prefer **`import Icon from "@lucide/svelte/icons/name"`** over the package barrel for clearer tree-shaking and alignment with the postinstall typings fix. Showcase demos replace **`CircleHelp`** with **`CircleQuestionMark`** (current Lucide export name).
|
|
1331
|
+
- **`zabi-components.css` (generated bundle)**: Picks up additional theme utilities used by the showcase and organisms—e.g. **`--spacing-xs`**, **`text-base-900`**, **`max-h-[min(32rem,calc(100dvh-6rem))]`**, **`overscroll-contain`**, prose/fit/max/min width utilities, responsive **`md:z-10`**, **`md:w-80`**, **`md:flex-nowrap`**, **`md:items-stretch`**, **`overflow-auto`**, **`align-top`**. Some rarely-used utilities were dropped from the scanned set where no longer referenced (e.g. **`left-full`**, base **`items-stretch`** in favor of **`md:items-stretch`**, **`bg-gray-50`**).
|
|
1332
|
+
|
|
1333
|
+
### Fixed
|
|
1334
|
+
|
|
1335
|
+
- **Lucide + TypeScript**: Consumers importing per-icon modules get consistent typings after install thanks to the **`postinstall`** patch for **`@lucide/svelte`** ~**0.544.x** declaration output.
|
|
1336
|
+
|
|
1337
|
+
### Migration (from 6.x)
|
|
1338
|
+
|
|
1339
|
+
1. **`Page` / `EmptyState`**: If layout relied on the old max widths, add them explicitly:
|
|
1340
|
+
- `<Page className="max-w-4xl">` or wrap content in **`Container`** / **`Section`**.
|
|
1341
|
+
- `<EmptyState className="max-w-lg" …>` (or another **`max-w-*`** from your layout policy).
|
|
1342
|
+
2. **`npm install`**: Keep **`postinstall`** enabled so Lucide icon **`.d.ts`** files are patched; if you use **`npm install --ignore-scripts`**, run `node scripts/fix-lucide-svelte-icon-dts.js` manually or rely on root **`@lucide/svelte`** barrel imports only (may bundle more code).
|
|
1343
|
+
3. **Demos copying Lucide icon names**: Replace **`CircleHelp`** with **`CircleQuestionMark`** when upgrading Lucide past names that removed **`CircleHelp`**.
|
|
1344
|
+
|
|
1345
|
+
## [6.0.0] - 2026-04-12
|
|
1346
|
+
|
|
1347
|
+
### Breaking changes
|
|
1348
|
+
|
|
1349
|
+
- Removed the `Navigation` organism; use `TopNavbar` with `items`, optional `embedded`, or a `nav` snippet instead.
|
|
1350
|
+
- Renamed `SidebarProjectPanel` to `SidebarPanel` (including the `SidebarPanelItem` type).
|
|
1351
|
+
|
|
1352
|
+
### Added
|
|
1353
|
+
|
|
1354
|
+
- Package subpath exports `zabi-components/colors` and `zabi-components/css` for theme and full bundle CSS.
|
|
1355
|
+
- Published theme documentation files in the package (`THEME.md`, `THEMING.md`, `docs/theme-imports.md`).
|
|
1356
|
+
|
|
1357
|
+
### Changed
|
|
1358
|
+
|
|
1359
|
+
- Theme build runs `sync:tokens` before CSS generation; added theme output tests (`test:themes`).
|
|
1360
|
+
- Refined default border styling on several components.
|
|
1361
|
+
|
|
1362
|
+
### Migration
|
|
1363
|
+
|
|
1364
|
+
- Replace `import { Navigation } from 'zabi-components'` (or organisms path) with `TopNavbar` and the documented props/snippets.
|
|
1365
|
+
- Replace `SidebarProjectPanel` imports with `SidebarPanel` and align prop/type names to `SidebarPanelItem` where applicable.
|
|
1366
|
+
|
|
1367
|
+
## Pending deprecations
|
|
1368
|
+
|
|
1369
|
+
- Legacy deep CSS import paths under `zabi-components/dist/*.css` are still supported for compatibility but documented as legacy. Prefer short exports such as `zabi-components/theme-only`, `zabi-components/theme-dark-only`, `zabi-components/colors`, and `zabi-components/css`.
|
|
1370
|
+
|
|
1371
|
+
## [5.0.22] - 2026-03-16
|
|
1372
|
+
|
|
1373
|
+
### ✨ **Features**
|
|
1374
|
+
|
|
1375
|
+
- **List Components**: Added new `List` and `ListItem` components with documentation and usage examples
|
|
1376
|
+
- **Sidebar**: Added `SidebarNavigation` and `SidebarPanel` with improved navigation behaviors
|
|
1377
|
+
|
|
1378
|
+
### 🔧 **Improvements**
|
|
1379
|
+
|
|
1380
|
+
- **Card Components**: Refactored `Card`, `CardHeader`, and `CardContent` and simplified composition patterns
|
|
1381
|
+
- **Documentation**: Updated Storybook setup and improved component documentation quality
|
|
1382
|
+
- **Config & Environment**: Refined environment variable definitions and release metadata handling
|
|
1383
|
+
|
|
1384
|
+
### ✅ **No Breaking Changes**
|
|
1385
|
+
|
|
1386
|
+
This is a backward-compatible update.
|
|
1387
|
+
|
|
1388
|
+
---
|
|
1389
|
+
|
|
1390
|
+
## [5.0.21] - 2026-01-17
|
|
1391
|
+
|
|
1392
|
+
### ✨ **Features**
|
|
1393
|
+
|
|
1394
|
+
- **IconButton**: Added a new icon-only button with variants, sizes, and accessible labels
|
|
1395
|
+
- **Exports & Storybook**: Included IconButton in atom exports and Storybook registry
|
|
1396
|
+
- **Docs**: Added IconButton examples to the docs and components showcase
|
|
1397
|
+
|
|
1398
|
+
### 🔧 **Improvements**
|
|
1399
|
+
|
|
1400
|
+
- **Navigation**: Header navigation now stacks for mobile-friendly layouts
|
|
1401
|
+
- **Select**: Improved selected label truncation and helper message layout
|
|
1402
|
+
- **Button**: Standardized disabled behavior without pointer-events override
|
|
1403
|
+
|
|
1404
|
+
### ✅ **No Breaking Changes**
|
|
1405
|
+
|
|
1406
|
+
This is a backward-compatible update.
|
|
1407
|
+
|
|
1408
|
+
---
|
|
1409
|
+
|
|
1410
|
+
## [5.0.20] - 2025-01-27
|
|
1411
|
+
|
|
1412
|
+
### 🔧 **Patch Release**
|
|
1413
|
+
|
|
1414
|
+
- **Maintenance**: General improvements and stability updates
|
|
1415
|
+
- **Build**: Enhanced build process and type definitions
|
|
1416
|
+
- **Documentation**: Minor documentation updates
|
|
1417
|
+
|
|
1418
|
+
### ✅ **No Breaking Changes**
|
|
1419
|
+
|
|
1420
|
+
This is a backward-compatible patch release.
|
|
1421
|
+
|
|
1422
|
+
---
|
|
1423
|
+
|
|
1424
|
+
## [5.0.19] - 2025-01-27
|
|
1425
|
+
|
|
1426
|
+
### 🐛 **Bug Fixes**
|
|
1427
|
+
|
|
1428
|
+
#### **Modal Component**
|
|
1429
|
+
- **Fixed SSR Import Error**: Resolved issue where Modal component was trying to import `focus-utils.js` from a relative path that didn't exist in consuming projects
|
|
1430
|
+
- **Internal Focus Utilities**: Moved focus management utilities (`focus-utils.ts`) to be properly included in the build output
|
|
1431
|
+
- **Build Process**: Added post-build script to fix import paths in built components
|
|
1432
|
+
- This fixes the "Failed to load url ../../routes/lib/focus-utils.js" error that occurred during SSR
|
|
1433
|
+
|
|
1434
|
+
## [5.0.18] - 2025-01-27
|
|
1435
|
+
|
|
1436
|
+
### ✨ **Component Enhancements & Documentation**
|
|
1437
|
+
|
|
1438
|
+
#### **Card Component Improvements**
|
|
1439
|
+
- **Compound Components**: Added CardHeader, CardContent, CardFooter, CardTitle, and CardDescription sub-components
|
|
1440
|
+
- Enables flexible card composition with better structure
|
|
1441
|
+
- Maintains backward compatibility with existing title/description/image props
|
|
1442
|
+
- Supports both old and new API patterns
|
|
1443
|
+
- **Enhanced Variants**: Improved elevated, outlined, and flat variants with better hover states
|
|
1444
|
+
- **Accessibility**: Added keyboard navigation (Enter/Space) and proper ARIA attributes for interactive cards
|
|
1445
|
+
- **Size Variants**: Enhanced size support (sm, md, lg) with responsive padding and typography
|
|
1446
|
+
|
|
1447
|
+
#### **Button Component Enhancements**
|
|
1448
|
+
- **New Variants**: Added ghost, outline, and link variants for more design flexibility
|
|
1449
|
+
- **Improved Focus States**: Enhanced focus ring styles for better accessibility
|
|
1450
|
+
- **Size Refinements**: Improved size variants with better typography and spacing
|
|
1451
|
+
- **Better Disabled States**: Enhanced disabled state styling and interaction
|
|
1452
|
+
|
|
1453
|
+
#### **Badge Component Updates**
|
|
1454
|
+
- **Icon Support**: Added optional icon display for success, warning, error, and info variants
|
|
1455
|
+
- **Size Variants**: Enhanced size support with responsive icon sizing
|
|
1456
|
+
- **Visual Consistency**: Improved color system integration
|
|
1457
|
+
|
|
1458
|
+
#### **Component Accessibility Improvements**
|
|
1459
|
+
- **Modal Component**: Enhanced focus management and keyboard navigation
|
|
1460
|
+
- **Dropdown Component**: Improved ARIA attributes and keyboard navigation
|
|
1461
|
+
- **Card Component**: Added proper roles and keyboard handlers for interactive cards
|
|
1462
|
+
- **Focus Utilities**: New focus-utils.ts for better focus management across components
|
|
1463
|
+
|
|
1464
|
+
#### **Documentation Additions**
|
|
1465
|
+
- **ACCESSIBILITY.md**: Comprehensive accessibility documentation with component-by-component analysis
|
|
1466
|
+
- **KEYBOARD_NAVIGATION.md**: Detailed keyboard navigation patterns and best practices
|
|
1467
|
+
- **VARIANTS.md**: Complete variant system documentation
|
|
1468
|
+
- **THEMING.md**: Enhanced theming guide with customization examples
|
|
1469
|
+
- **THEME_QUICK_REFERENCE.md**: Quick reference for theme customization
|
|
1470
|
+
- **SHADCN_INSPIRATION.md**: Design system inspiration and improvement roadmap
|
|
1471
|
+
|
|
1472
|
+
#### **Theme & Styling Improvements**
|
|
1473
|
+
- **Color System**: Enhanced color variable resolution and consistency
|
|
1474
|
+
- **Theme Files**: Updated theme CSS files with improved structure
|
|
1475
|
+
- **Build Process**: Enhanced CSS build scripts for better theme processing
|
|
1476
|
+
|
|
1477
|
+
#### **Build & Development**
|
|
1478
|
+
- **Type Definitions**: Updated TypeScript definitions for new components
|
|
1479
|
+
- **Component Exports**: Added exports for new Card compound components
|
|
1480
|
+
- **Storybook**: Enhanced Card stories with compound component examples
|
|
1481
|
+
|
|
1482
|
+
### ✅ **No Breaking Changes**
|
|
1483
|
+
|
|
1484
|
+
This is a backward-compatible update. Existing component usage continues to work, with new features available as optional enhancements.
|
|
1485
|
+
|
|
1486
|
+
---
|
|
1487
|
+
|
|
1488
|
+
## [5.0.17] - 2025-01-27
|
|
1489
|
+
|
|
1490
|
+
### 🔧 **Patch Release**
|
|
1491
|
+
|
|
1492
|
+
- **Documentation**: Updated changelog with version 5.0.16 release notes
|
|
1493
|
+
- **Build Improvements**: Adjusted theme validation script to be less strict for dark theme structure differences
|
|
1494
|
+
- **Version Bump**: Patch release for improved stability
|
|
1495
|
+
|
|
1496
|
+
---
|
|
1497
|
+
|
|
1498
|
+
## [5.0.16] - 2025-01-27
|
|
1499
|
+
|
|
1500
|
+
### 🎨 **Color System & Component Styling Updates**
|
|
1501
|
+
|
|
1502
|
+
#### **Color System Refactoring**
|
|
1503
|
+
- **Enhanced Color Variables**: Improved color system with better CSS variable resolution
|
|
1504
|
+
- **Theme Updates**: Updated theme files with improved color consistency
|
|
1505
|
+
- **Build Process**: Enhanced CSS build scripts for better color processing
|
|
1506
|
+
|
|
1507
|
+
#### **Component Style Improvements**
|
|
1508
|
+
- **Checkbox Component**: Updated styling to match M3 design system with improved visual consistency
|
|
1509
|
+
- **Input Component**: Enhanced color system integration and styling updates
|
|
1510
|
+
- **Multiple Components**: Updated Button, CodeBlock, ColorPicker, FeatureCard, Progress, Skeleton, Textarea, Toast, Toggle, and more with consistent styling
|
|
1511
|
+
- **Molecule Components**: Updated Alert, ComponentDemo, Dropdown, ImageUpload, Modal, SlideUp, and Tabs with improved styling
|
|
1512
|
+
|
|
1513
|
+
#### **Storybook Configuration**
|
|
1514
|
+
- **Story Organization**: Reorganized stories into atoms/, molecules/, and organisms/ folders for better structure
|
|
1515
|
+
- **Enhanced Stories**: Updated component stories with improved examples and controls
|
|
1516
|
+
- **Configuration Updates**: Enhanced Storybook configuration for better development experience
|
|
1517
|
+
|
|
1518
|
+
#### **Build & Dependency Updates**
|
|
1519
|
+
- **Dependency Updates**: Updated package dependencies for improved compatibility
|
|
1520
|
+
- **Build Scripts**: Enhanced CSS build and cleanup scripts
|
|
1521
|
+
- **Type Definitions**: Updated TypeScript definitions for better type safety
|
|
1522
|
+
|
|
1523
|
+
### ✅ **No Breaking Changes**
|
|
1524
|
+
|
|
1525
|
+
This is a backward-compatible update focusing on styling improvements and build enhancements.
|
|
1526
|
+
|
|
1527
|
+
---
|
|
1528
|
+
|
|
1529
|
+
## [5.0.15] - 2025-01-27
|
|
1530
|
+
|
|
1531
|
+
### 🎨 **Color System Improvements**
|
|
1532
|
+
|
|
1533
|
+
#### **Standalone Colors File**
|
|
1534
|
+
- **New Export**: Added `zabi-components-colors.css` as a separate export for consumer apps
|
|
1535
|
+
- Contains resolved CSS custom properties that work without Tailwind CSS processing
|
|
1536
|
+
- Perfect for apps not using Tailwind CSS v4
|
|
1537
|
+
- Includes all color variables with resolved hex values
|
|
1538
|
+
- Supports both light and dark mode variants
|
|
1539
|
+
- **Package Export**: Added `./dist/zabi-components-colors.css` to package.json exports
|
|
1540
|
+
- **Usage**: Consumer apps can now import colors independently:
|
|
1541
|
+
```css
|
|
1542
|
+
/* For Tailwind v4 users */
|
|
1543
|
+
@import "zabi-components/dist/zabi-components.css";
|
|
1544
|
+
|
|
1545
|
+
/* For non-Tailwind users */
|
|
1546
|
+
@import "zabi-components/dist/zabi-components-colors.css";
|
|
1547
|
+
```
|
|
1548
|
+
|
|
1549
|
+
#### **Component Cleanup**
|
|
1550
|
+
- **Badge Component**: Removed all arbitrary Tailwind values (`[var(--color-*)]`)
|
|
1551
|
+
- Now uses proper utility classes: `bg-success border-success` instead of `bg-success border-[var(--color-success)]`
|
|
1552
|
+
- More maintainable and consistent code
|
|
1553
|
+
- **Added Background Utilities**: Created explicit background utility classes to match border utilities
|
|
1554
|
+
- Added: `bg-success`, `bg-warning`, `bg-error`, `bg-info`, `bg-neutral`, `bg-energetic`, `bg-secondary`
|
|
1555
|
+
- Ensures consistent styling across all components
|
|
1556
|
+
|
|
1557
|
+
#### **Build Improvements**
|
|
1558
|
+
- **Simplified Build Script**: Cleaned up CSS build process
|
|
1559
|
+
- **Better Separation**: Colors file is now separate from main CSS, making it easier to import independently
|
|
1560
|
+
|
|
1561
|
+
### 🎯 **Technical Details**
|
|
1562
|
+
|
|
1563
|
+
- **Color System**: Colors now work correctly in consumer apps regardless of Tailwind CSS setup
|
|
1564
|
+
- **Code Quality**: Removed all arbitrary value syntax for better maintainability
|
|
1565
|
+
- **Consistency**: All color utilities now follow the same pattern
|
|
1566
|
+
|
|
1567
|
+
### ✅ **No Breaking Changes**
|
|
1568
|
+
|
|
1569
|
+
This is a backward-compatible update. Existing usage continues to work, with improved color system support.
|
|
1570
|
+
|
|
1571
|
+
---
|
|
1572
|
+
|
|
1573
|
+
## [5.0.14] - 2025-01-27
|
|
1574
|
+
|
|
1575
|
+
### ✨ **Component Improvements**
|
|
1576
|
+
|
|
1577
|
+
#### **Textarea Component**
|
|
1578
|
+
- **Visual Consistency**: Updated Textarea styling to match Input component exactly
|
|
1579
|
+
- Changed background to `bg-brand-100` (matching Input)
|
|
1580
|
+
- Updated border radius from `rounded-md` to `rounded-lg`
|
|
1581
|
+
- Removed border for default variant (matches Input's borderless design)
|
|
1582
|
+
- Updated focus states to use `focus:ring-2 focus:ring-brand-500` with `focus:ring-offset-0`
|
|
1583
|
+
- Standardized placeholder and text colors to match Input
|
|
1584
|
+
- **Simplified API**: Removed `size` prop - Textarea now uses a fixed size (matching Input's default "md" size)
|
|
1585
|
+
- **Enhanced Features**: Added message support with icons for success, warning, and error variants (matching Input component)
|
|
1586
|
+
|
|
1587
|
+
#### **Storybook Updates**
|
|
1588
|
+
- **Storybook 10 Compatibility**: Standardized `argTypes` controls across all stories
|
|
1589
|
+
- Updated Alert, Badge, Toggle, Tooltip, and SlideUp stories to use string shorthand for controls
|
|
1590
|
+
- Improved consistency and cleaner code
|
|
1591
|
+
- **Textarea Stories**: Added message examples for success, warning, and error variants
|
|
1592
|
+
- **Removed Deprecated Stories**: Removed Small and Large Textarea stories (size prop removed)
|
|
1593
|
+
|
|
1594
|
+
### 🎯 **Technical Details**
|
|
1595
|
+
|
|
1596
|
+
- **Styling Alignment**: Textarea now visually matches Input component for consistent form design
|
|
1597
|
+
- **API Simplification**: Removed unnecessary size variations from Textarea
|
|
1598
|
+
- **Storybook Modernization**: All stories now use Storybook 10 best practices
|
|
1599
|
+
|
|
1600
|
+
### ✅ **No Breaking Changes**
|
|
1601
|
+
|
|
1602
|
+
This is a backward-compatible update. Existing Textarea usage continues to work, with improved visual consistency.
|
|
1603
|
+
|
|
1604
|
+
---
|
|
1605
|
+
|
|
1606
|
+
## [4.0.1] - 2025-01-27
|
|
1607
|
+
|
|
1608
|
+
### 🛡️ **SSR Safety Overhaul - "Zero Runtime Errors" Edition**
|
|
1609
|
+
|
|
1610
|
+
#### ✅ **Critical SSR Issues Resolved**
|
|
1611
|
+
|
|
1612
|
+
This release completely eliminates the "Cannot read properties of null (reading 'f')" error and ensures 100% SSR safety across all components.
|
|
1613
|
+
|
|
1614
|
+
#### 🔧 **SSR Safety Improvements**
|
|
1615
|
+
|
|
1616
|
+
##### **Route Navigation Fixed**
|
|
1617
|
+
- **Replaced `window.location.href`**: All route pages now use SvelteKit's `goto()` from `$app/navigation`
|
|
1618
|
+
- **SSR-Safe Navigation**: No more direct window access during server-side rendering
|
|
1619
|
+
- **Files Updated**: `src/routes/+page.svelte`, `src/routes/docs/+page.svelte`
|
|
1620
|
+
|
|
1621
|
+
##### **Component Lifecycle Fixed**
|
|
1622
|
+
- **ThemeToggle Component**: Replaced `$effect` with `onMount` for initial setup
|
|
1623
|
+
- **SSRSafe Component**: Simplified checks to rely solely on `mounted` state
|
|
1624
|
+
- **Eliminated Race Conditions**: Fixed timing issues during hydration
|
|
1625
|
+
|
|
1626
|
+
##### **ID Generation Fixed**
|
|
1627
|
+
- **Input Component**: Moved ID generation to `onMount` to prevent hydration mismatches
|
|
1628
|
+
- **Checkbox Component**: Moved ID generation to `onMount` for consistency
|
|
1629
|
+
- **SSR-Safe IDs**: No more SSR/client ID mismatches
|
|
1630
|
+
|
|
1631
|
+
##### **Enhanced SSR Utilities**
|
|
1632
|
+
- **New `ssr-safe.ts`**: Comprehensive SSR-safe utility functions
|
|
1633
|
+
- **SvelteKit Integration**: Uses `browser` from `$app/environment`
|
|
1634
|
+
- **Safe API Access**: `safeLocalStorage()`, `safeDocument()`, `safeWindow()`
|
|
1635
|
+
- **ID Generation**: `generateId()` function for consistent SSR-safe IDs
|
|
1636
|
+
|
|
1637
|
+
#### 🎯 **Technical Improvements**
|
|
1638
|
+
|
|
1639
|
+
##### **Consistent SSR Pattern**
|
|
1640
|
+
```svelte
|
|
1641
|
+
import { safeLocalStorage, safeDocument } from "../../lib/ssr-safe";
|
|
1642
|
+
|
|
1643
|
+
onMount(() => {
|
|
1644
|
+
mounted = true;
|
|
1645
|
+
const storage = safeLocalStorage();
|
|
1646
|
+
if (storage) {
|
|
1647
|
+
// Safe browser API access
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1650
|
+
```
|
|
1651
|
+
|
|
1652
|
+
##### **SvelteKit Best Practices**
|
|
1653
|
+
- Uses `browser` from `$app/environment` instead of manual checks
|
|
1654
|
+
- Uses `goto()` from `$app/navigation` for routing
|
|
1655
|
+
- Follows SvelteKit SSR patterns throughout
|
|
1656
|
+
|
|
1657
|
+
##### **Hydration Safety**
|
|
1658
|
+
- No more SSR/client mismatches
|
|
1659
|
+
- Consistent ID generation across server and client
|
|
1660
|
+
- Proper lifecycle management
|
|
1661
|
+
|
|
1662
|
+
#### ✅ **Results Achieved**
|
|
1663
|
+
|
|
1664
|
+
##### **Build Status**
|
|
1665
|
+
- ✅ **Successful Builds**: All builds complete without errors
|
|
1666
|
+
- ✅ **Type Safety**: 0 TypeScript errors in zabi-components code
|
|
1667
|
+
- ✅ **SSR Compatible**: Full server-side rendering support
|
|
1668
|
+
|
|
1669
|
+
##### **Runtime Safety**
|
|
1670
|
+
- ✅ **Zero Runtime Errors**: Eliminated all "Cannot read properties of null" errors
|
|
1671
|
+
- ✅ **Hydration Safe**: No more client/server mismatches
|
|
1672
|
+
- ✅ **Theme Toggle**: Works correctly on first load
|
|
1673
|
+
- ✅ **Form Inputs**: Stable IDs across SSR/hydration
|
|
1674
|
+
|
|
1675
|
+
##### **Cross-Environment Compatibility**
|
|
1676
|
+
- ✅ **SvelteKit SSR**: Full compatibility with server-side rendering
|
|
1677
|
+
- ✅ **Client Hydration**: Smooth hydration without errors
|
|
1678
|
+
- ✅ **Production Ready**: Works correctly in production environments
|
|
1679
|
+
|
|
1680
|
+
#### 📋 **Files Modified**
|
|
1681
|
+
|
|
1682
|
+
1. `src/routes/+page.svelte` - Navigation fixes (6 instances)
|
|
1683
|
+
2. `src/routes/docs/+page.svelte` - Navigation fixes (2 instances)
|
|
1684
|
+
3. `src/components/atoms/ThemeToggle.svelte` - SSR safety improvements
|
|
1685
|
+
4. `src/components/atoms/Input.svelte` - ID generation fixes
|
|
1686
|
+
5. `src/components/atoms/Checkbox.svelte` - ID generation fixes
|
|
1687
|
+
6. `src/components/SSRSafe.svelte` - Simplified checks
|
|
1688
|
+
7. `src/lib/ssr-safe.ts` - Enhanced SSR utilities
|
|
1689
|
+
|
|
1690
|
+
#### 🚀 **Performance Benefits**
|
|
1691
|
+
|
|
1692
|
+
- **Faster Builds**: Reduced build time and complexity
|
|
1693
|
+
- **Better Reliability**: No more runtime crashes
|
|
1694
|
+
- **Improved DX**: Better developer experience with consistent patterns
|
|
1695
|
+
- **Production Ready**: Fully tested in production environments
|
|
1696
|
+
|
|
1697
|
+
#### 🔄 **Migration Notes**
|
|
1698
|
+
|
|
1699
|
+
**No Breaking Changes**: This is a backward-compatible update that fixes SSR issues without changing component APIs.
|
|
1700
|
+
|
|
1701
|
+
**For Developers**: All existing component usage remains the same - the fixes are internal and transparent to users.
|
|
1702
|
+
|
|
1703
|
+
---
|
|
1704
|
+
|
|
1705
|
+
## [2.1.0] - 2025-01-27
|
|
1706
|
+
|
|
1707
|
+
### 🚀 Major Event Handling Refactor - "Cross-Framework Compatible" Edition
|
|
1708
|
+
|
|
1709
|
+
#### ⚠️ **BREAKING CHANGES** - Event Handling Overhaul
|
|
1710
|
+
|
|
1711
|
+
This is a **major breaking change** that affects how all components handle events. The refactoring eliminates SSR/production errors and makes components compatible with React, Vue, and vanilla JavaScript applications.
|
|
1712
|
+
|
|
1713
|
+
#### 🔧 **Event System Refactoring**
|
|
1714
|
+
- **Removed `createEventDispatcher`**: Eliminated all `createEventDispatcher` usage across 18 components
|
|
1715
|
+
- **Implemented Event Forwarding**: All components now use `{...$$restProps}` for native DOM event forwarding
|
|
1716
|
+
- **SSR-Safe**: Fixed hydration mismatches and production build errors
|
|
1717
|
+
- **Cross-Framework Compatible**: Components now work in React, Vue, Svelte, and vanilla JS
|
|
1718
|
+
|
|
1719
|
+
#### 🎯 **Components Refactored**
|
|
1720
|
+
|
|
1721
|
+
##### **Form Components**
|
|
1722
|
+
- **Input**: Replaced `bind:value` with `value` prop + `on:input` handler
|
|
1723
|
+
- **Textarea**: Replaced `bind:value` with `value` prop + `on:input` handler
|
|
1724
|
+
- **Select**: Replaced `bind:value` with `value` prop + `on:change` handler
|
|
1725
|
+
- **Checkbox**: Replaced `bind:checked` with `checked` prop + `on:change` handler
|
|
1726
|
+
|
|
1727
|
+
##### **Interactive Components**
|
|
1728
|
+
- **Button**: Added `{...$$restProps}` for native event forwarding
|
|
1729
|
+
- **Toggle**: Added event handler + `{...$$restProps}` for cross-framework compatibility
|
|
1730
|
+
- **ThemeToggle**: Added `{...$$restProps}` for event forwarding
|
|
1731
|
+
|
|
1732
|
+
##### **Complex Components**
|
|
1733
|
+
- **Modal**: Removed `createEventDispatcher`, simplified event handling
|
|
1734
|
+
- **Tabs**: Removed `createEventDispatcher`, uses direct state management
|
|
1735
|
+
- **ImageUpload**: Removed `createEventDispatcher`, simplified file handling
|
|
1736
|
+
- **ContactForm**: Updated to use new event patterns
|
|
1737
|
+
- **Form**: Removed `createEventDispatcher`, added `{...$$restProps}`
|
|
1738
|
+
- **Alert**: Removed `createEventDispatcher`, added `{...$$restProps}`
|
|
1739
|
+
- **Toast**: Removed `createEventDispatcher`, simplified close handling
|
|
1740
|
+
- **Badge**: Removed `createEventDispatcher`, added `{...$$restProps}`
|
|
1741
|
+
- **SlideUp**: Removed `createEventDispatcher`, simplified modal handling
|
|
1742
|
+
- **TopNavbar**: Removed `createEventDispatcher`, added `{...$$restProps}`
|
|
1743
|
+
- **Navigation**: Removed `createEventDispatcher`, simplified navigation
|
|
1744
|
+
|
|
1745
|
+
#### ✅ **Benefits Achieved**
|
|
1746
|
+
|
|
1747
|
+
##### **SSR/Production Safe**
|
|
1748
|
+
- ✅ **No more hydration errors** in SSR environments
|
|
1749
|
+
- ✅ **Production builds work correctly** without runtime errors
|
|
1750
|
+
- ✅ **Eliminated `createEventDispatcher` errors** in SvelteKit applications
|
|
1751
|
+
|
|
1752
|
+
##### **Cross-Framework Compatible**
|
|
1753
|
+
- ✅ **React Applications**: Components work with standard React event handlers
|
|
1754
|
+
- ✅ **Vue Applications**: Compatible with Vue's event system
|
|
1755
|
+
- ✅ **Vanilla JavaScript**: Full compatibility with native DOM events
|
|
1756
|
+
- ✅ **Svelte/SvelteKit**: Maintains full compatibility with improved reliability
|
|
1757
|
+
|
|
1758
|
+
##### **Standards Compliant**
|
|
1759
|
+
- ✅ **Native DOM Events**: Uses web standards instead of framework-specific events
|
|
1760
|
+
- ✅ **Better Performance**: Reduced JavaScript overhead and bundle size
|
|
1761
|
+
- ✅ **More Predictable**: Event handling follows web standards
|
|
1762
|
+
|
|
1763
|
+
#### 🔄 **Migration Required**
|
|
1764
|
+
|
|
1765
|
+
**Before (Old Pattern):**
|
|
1766
|
+
```svelte
|
|
1767
|
+
<!-- This would cause SSR errors -->
|
|
1768
|
+
<Button on:click={handleClick}>Click me</Button>
|
|
1769
|
+
<Input bind:value={inputValue} />
|
|
1770
|
+
<Modal bind:isOpen onclick={handleClose} />
|
|
1771
|
+
```
|
|
1772
|
+
|
|
1773
|
+
**After (New Pattern):**
|
|
1774
|
+
```svelte
|
|
1775
|
+
<!-- Now works in all environments -->
|
|
1776
|
+
<Button on:click={handleClick}>Click me</Button>
|
|
1777
|
+
<Input value={inputValue} on:input={(e) => inputValue = e.target.value} />
|
|
1778
|
+
<Modal bind:isOpen on:click={handleClose} />
|
|
1779
|
+
```
|
|
1780
|
+
|
|
1781
|
+
#### 📋 **Breaking Changes Summary**
|
|
1782
|
+
|
|
1783
|
+
1. **Form Components**: Must use `value` prop + event handlers instead of `bind:value`
|
|
1784
|
+
2. **Event Names**: Some custom events replaced with native DOM events
|
|
1785
|
+
3. **Event Structure**: Simplified event structures across all components
|
|
1786
|
+
4. **SSR Compatibility**: Components now work correctly in SSR environments
|
|
1787
|
+
|
|
1788
|
+
#### 🛠️ **Technical Details**
|
|
1789
|
+
|
|
1790
|
+
- **Event Forwarding**: All interactive elements now use `{...$$restProps}`
|
|
1791
|
+
- **Native Events**: Replaced custom events with standard DOM events where possible
|
|
1792
|
+
- **State Management**: Simplified internal state management
|
|
1793
|
+
- **Type Safety**: Maintained full TypeScript support with updated types
|
|
1794
|
+
|
|
1795
|
+
#### 🎯 **Migration Guide**
|
|
1796
|
+
|
|
1797
|
+
See the updated README.md for detailed migration examples and new usage patterns.
|
|
1798
|
+
|
|
1799
|
+
---
|
|
1800
|
+
|
|
1801
|
+
## [2.0.2] - 2025-01-27
|
|
1802
|
+
|
|
1803
|
+
### 🎯 Major Simplification - "Less is More" Edition
|
|
1804
|
+
|
|
1805
|
+
#### ✨ Simplified Components
|
|
1806
|
+
- **Massive Code Reduction**: Reduced component complexity by 60-80% across the board
|
|
1807
|
+
- **Modern CSS-First Approach**: Replaced complex JavaScript with modern CSS-only solutions
|
|
1808
|
+
- **Unified Form Component**: Consolidated 4 form components into 1 simple Form component
|
|
1809
|
+
- **Removed Complex Managers**: Eliminated NotificationManager, ToastManager, and other complex utilities
|
|
1810
|
+
|
|
1811
|
+
#### 🔧 Atom Components Simplified
|
|
1812
|
+
- **Badge**: Removed complex variants, icons, and animations - now just basic color variants
|
|
1813
|
+
- **Button**: Simplified to 3 variants (primary, secondary, danger) with minimal props
|
|
1814
|
+
- **Card**: Merged ModernCard into Card, removed complex styling and animations
|
|
1815
|
+
- **Checkbox**: Removed complex validation, sizing, and custom CSS
|
|
1816
|
+
- **Input**: Simplified to essential props only, removed complex validation system
|
|
1817
|
+
- **Heading**: Clean heading component with just level and text props
|
|
1818
|
+
- **Progress**: Basic progress bar with simple percentage display
|
|
1819
|
+
- **Select**: Simplified dropdown with basic options support
|
|
1820
|
+
- **Textarea**: Essential textarea with minimal configuration
|
|
1821
|
+
- **Toggle**: Fixed-size toggle with simple on/off state
|
|
1822
|
+
- **ThemeToggle**: Emoji-based theme switcher, removed complex styling
|
|
1823
|
+
- **Toast**: Fixed-position toast with basic message display
|
|
1824
|
+
- **Tooltip**: CSS-only positioning with data-placement attributes
|
|
1825
|
+
- **Skeleton**: Simple loading placeholder with basic styling
|
|
1826
|
+
- **TextAlignment**: Emoji-based alignment selector
|
|
1827
|
+
- **OptimizedImage**: Basic image component with lazy loading
|
|
1828
|
+
- **ColorPicker**: Simple color grid without complex dropdown logic
|
|
1829
|
+
|
|
1830
|
+
#### 🧩 Molecule Components Simplified
|
|
1831
|
+
- **Dropdown**: CSS-only positioning, removed complex JavaScript calculations
|
|
1832
|
+
- **ImageUpload**: Direct file selection, removed modal and progress bars
|
|
1833
|
+
- **Form**: Unified form component, removed complex validation and field management
|
|
1834
|
+
- **Modal**: Simple modal with basic backdrop and close functionality
|
|
1835
|
+
- **SlideUp**: CSS-only slide-up panel, removed complex state management
|
|
1836
|
+
- **Tabs**: Basic tab navigation with keyboard support, removed complex utilities
|
|
1837
|
+
|
|
1838
|
+
#### 🏗️ Organism Components Simplified
|
|
1839
|
+
- **TopNavbar**: Responsive top navbar with mobile menu, removed complex variants
|
|
1840
|
+
- **Navigation**: Simple navigation list with active state
|
|
1841
|
+
- **Removed Complex Components**: Deleted NavigationItem, NotificationManager, ToastManager
|
|
1842
|
+
|
|
1843
|
+
#### 🗑️ Removed Complexity
|
|
1844
|
+
- **External Utility Functions**: Removed complex tabs-utils, navigation-utils
|
|
1845
|
+
- **Complex Type Definitions**: Simplified event types and interfaces
|
|
1846
|
+
- **Svelte Transitions**: Replaced with CSS-only animations
|
|
1847
|
+
- **Custom CSS Variables**: Replaced with standard Tailwind classes
|
|
1848
|
+
- **Complex State Management**: Simplified to essential state only
|
|
1849
|
+
- **Redundant Folders**: Removed simple/ folder with duplicate exports
|
|
1850
|
+
|
|
1851
|
+
#### 🎨 Modern CSS Features
|
|
1852
|
+
- **CSS-only Positioning**: Using `position: absolute` and `transform` for tooltips and dropdowns
|
|
1853
|
+
- **CSS Pseudo-elements**: Using `::before` for arrows and indicators
|
|
1854
|
+
- **CSS Data Attributes**: Using `data-placement` for component positioning
|
|
1855
|
+
- **CSS Transitions**: Smooth animations handled entirely by CSS
|
|
1856
|
+
- **Tailwind-First**: All styling using Tailwind utility classes
|
|
1857
|
+
|
|
1858
|
+
#### 📦 Package Improvements
|
|
1859
|
+
- **Cleaner Structure**: Removed unnecessary abstraction layers
|
|
1860
|
+
- **Direct Imports**: Components available from their natural locations
|
|
1861
|
+
- **Simplified APIs**: Just the essential props needed for functionality
|
|
1862
|
+
- **Better Performance**: Reduced JavaScript overhead and complexity
|
|
1863
|
+
|
|
1864
|
+
#### 🐛 Bug Fixes
|
|
1865
|
+
- **Accessibility**: Fixed label associations and ARIA attributes
|
|
1866
|
+
- **TypeScript**: Resolved all type errors and warnings
|
|
1867
|
+
- **Linting**: Fixed all ESLint and Svelte warnings
|
|
1868
|
+
- **Deprecation**: Replaced deprecated Lucide icons with emoji alternatives
|
|
1869
|
+
|
|
1870
|
+
### Breaking Changes
|
|
1871
|
+
- **Removed Components**: KeyValueForm, ContactForm, ModernForm, NotificationManager, ToastManager
|
|
1872
|
+
- **Simplified Props**: Many components now have fewer configuration options
|
|
1873
|
+
- **Removed Variants**: Reduced complex variant systems to essential options only
|
|
1874
|
+
- **Event Simplification**: Simplified event structures across all components
|
|
1875
|
+
|
|
1876
|
+
### Migration Guide
|
|
1877
|
+
- **Form Components**: Use the unified `Form` component instead of multiple form variants
|
|
1878
|
+
- **Complex Features**: Many complex features have been removed in favor of simplicity
|
|
1879
|
+
- **Styling**: Components now use standard Tailwind classes instead of custom CSS variables
|
|
1880
|
+
- **Imports**: Import components directly from atoms/, molecules/, organisms/ folders
|
|
1881
|
+
|
|
1882
|
+
## [1.1.1] - 2025-10-06
|
|
1883
|
+
|
|
1884
|
+
### Fixed
|
|
1885
|
+
- Updated generated TypeScript definitions for better type safety
|
|
1886
|
+
- Enhanced package structure with improved type exports
|
|
1887
|
+
- Fixed build consistency and versioning
|
|
1888
|
+
|
|
1889
|
+
### Changed
|
|
1890
|
+
- Updated internal build files and generated assets
|
|
1891
|
+
- Improved TypeScript declaration file generation
|
|
1892
|
+
- Enhanced package.json structure and exports
|
|
1893
|
+
|
|
1894
|
+
## [1.1.0] - 2025-10-05
|
|
1895
|
+
|
|
1896
|
+
### Added
|
|
1897
|
+
- Comprehensive TypeScript definitions built into the library
|
|
1898
|
+
- Self-contained type system - no custom type definitions needed in consumer apps
|
|
1899
|
+
- Enhanced component type safety with proper event handling
|
|
1900
|
+
- SSR-safe utilities for better SvelteKit compatibility
|
|
1901
|
+
|
|
1902
|
+
### Changed
|
|
1903
|
+
- Updated build configuration for proper SvelteKit compatibility
|
|
1904
|
+
- Removed SSR mode from library build (libraries should be client-side)
|
|
1905
|
+
- Enhanced package.json exports for better module resolution
|
|
1906
|
+
- Updated peer dependencies for Svelte 5 compatibility
|
|
1907
|
+
|
|
1908
|
+
### Fixed
|
|
1909
|
+
- Fixed runtime errors with `Cannot read properties of null (reading 'f')`
|
|
1910
|
+
- Fixed event handling for Svelte 5 compatibility
|
|
1911
|
+
- Fixed TypeScript type inference for component props and events
|
|
1912
|
+
- Fixed package exports for proper module resolution
|
|
1913
|
+
|
|
1914
|
+
### Removed
|
|
1915
|
+
- Removed need for custom type definitions in consumer applications
|
|
1916
|
+
- Removed unnecessary client-only component duplicates
|
|
1917
|
+
- Removed SSR wrapper components (not needed)
|
|
1918
|
+
|
|
1919
|
+
### Breaking Changes
|
|
1920
|
+
- None - this is a backward-compatible update
|
|
1921
|
+
|
|
1922
|
+
## [1.0.11] - Previous version
|
|
1923
|
+
- Initial release with basic components
|
|
1924
|
+
|