@sableui/react 0.6.2 → 0.7.1
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/AGENTS.md +2 -2
- package/CHANGELOG.md +315 -0
- package/CONTRIBUTING.md +16 -10
- package/DESIGN.md +3 -3
- package/README.md +3 -3
- package/bin/sable-lint.mjs +18 -0
- package/bin/sable.mjs +470 -62
- package/dist/blocks/api-keys-block.js +44 -0
- package/dist/blocks/api-keys-block.meta.js +21 -0
- package/dist/blocks/chat-thread-block.js +17 -0
- package/dist/blocks/chat-thread-block.meta.js +20 -0
- package/dist/blocks/command-palette-block.js +24 -0
- package/dist/blocks/command-palette-block.meta.js +21 -0
- package/dist/blocks/cta-band-block.js +27 -0
- package/dist/blocks/cta-band-block.meta.js +20 -0
- package/dist/blocks/danger-zone-block.js +39 -0
- package/dist/blocks/danger-zone-block.meta.js +21 -0
- package/dist/blocks/device-comparison-block.js +69 -0
- package/dist/blocks/device-comparison-block.meta.js +21 -0
- package/dist/blocks/event-card-block.js +40 -0
- package/dist/blocks/event-card-block.meta.js +21 -0
- package/dist/blocks/faq-block.js +38 -0
- package/dist/blocks/faq-block.meta.js +20 -0
- package/dist/blocks/feature-grid-block.js +38 -0
- package/dist/blocks/feature-grid-block.meta.js +21 -0
- package/dist/blocks/feedback-form-block.js +30 -0
- package/dist/blocks/feedback-form-block.meta.js +21 -0
- package/dist/blocks/goal-progress-block.js +24 -0
- package/dist/blocks/goal-progress-block.meta.js +21 -0
- package/dist/blocks/hero-block.js +33 -0
- package/dist/blocks/hero-block.meta.js +21 -0
- package/dist/blocks/index.js +162 -1964
- package/dist/blocks/invite-team-block.js +36 -0
- package/dist/blocks/invite-team-block.meta.js +20 -0
- package/dist/blocks/invoice-history-block.js +41 -0
- package/dist/blocks/invoice-history-block.meta.js +21 -0
- package/dist/blocks/kpi-sparkline-block.js +29 -0
- package/dist/blocks/kpi-sparkline-block.meta.js +20 -0
- package/dist/blocks/media-player-block.js +68 -0
- package/dist/blocks/media-player-block.meta.js +21 -0
- package/dist/blocks/newsletter-block.js +35 -0
- package/dist/blocks/newsletter-block.meta.js +21 -0
- package/dist/blocks/not-found-block.js +27 -0
- package/dist/blocks/not-found-block.meta.js +20 -0
- package/dist/blocks/notification-list-block.js +32 -0
- package/dist/blocks/notification-list-block.meta.js +21 -0
- package/dist/blocks/notification-settings-block.js +25 -0
- package/dist/blocks/notification-settings-block.meta.js +20 -0
- package/dist/blocks/order-tracking-block.js +26 -0
- package/dist/blocks/order-tracking-block.meta.js +20 -0
- package/dist/blocks/pricing-card-block.js +46 -0
- package/dist/blocks/pricing-card-block.meta.js +21 -0
- package/dist/blocks/pricing-table-block.js +88 -0
- package/dist/blocks/pricing-table-block.meta.js +20 -0
- package/dist/blocks/product-card-block.js +26 -0
- package/dist/blocks/product-card-block.meta.js +21 -0
- package/dist/blocks/profile-card-block.js +58 -0
- package/dist/blocks/profile-card-block.meta.js +21 -0
- package/dist/blocks/profile-form-block.js +70 -0
- package/dist/blocks/profile-form-block.meta.js +21 -0
- package/dist/blocks/revenue-breakdown-block.js +38 -0
- package/dist/blocks/revenue-breakdown-block.meta.js +21 -0
- package/dist/blocks/sign-in-block.js +38 -0
- package/dist/blocks/sign-in-block.meta.js +21 -0
- package/dist/blocks/sign-up-block.js +48 -0
- package/dist/blocks/sign-up-block.meta.js +21 -0
- package/dist/blocks/site-footer-block.js +54 -0
- package/dist/blocks/site-footer-block.meta.js +20 -0
- package/dist/blocks/stat-row-block.js +28 -0
- package/dist/blocks/stat-row-block.meta.js +21 -0
- package/dist/blocks/steps-block.js +37 -0
- package/dist/blocks/steps-block.meta.js +20 -0
- package/dist/blocks/task-list-block.js +52 -0
- package/dist/blocks/task-list-block.meta.js +21 -0
- package/dist/blocks/team-members-block.js +49 -0
- package/dist/blocks/team-members-block.meta.js +21 -0
- package/dist/blocks/testimonial-block.js +49 -0
- package/dist/blocks/testimonial-block.meta.js +20 -0
- package/dist/blocks/traffic-sources-block.js +39 -0
- package/dist/blocks/traffic-sources-block.meta.js +21 -0
- package/dist/blocks/traffic-trend-block.js +61 -0
- package/dist/blocks/traffic-trend-block.meta.js +21 -0
- package/dist/blocks/upload-list-block.js +39 -0
- package/dist/blocks/upload-list-block.meta.js +21 -0
- package/dist/blocks/verify-code-block.js +28 -0
- package/dist/blocks/verify-code-block.meta.js +21 -0
- package/dist/blocks/wallet-block.js +48 -0
- package/dist/blocks/wallet-block.meta.js +21 -0
- package/dist/components/accordion/accordion.js +63 -0
- package/dist/components/accordion/accordion.meta.js +155 -0
- package/dist/components/accordion/index.js +15 -0
- package/dist/components/app-header/app-header.js +30 -0
- package/dist/components/app-header/app-header.meta.js +88 -0
- package/dist/components/app-header/index.js +7 -0
- package/dist/components/app-shell/app-shell-dock.js +37 -0
- package/dist/components/app-shell/app-shell.js +67 -0
- package/dist/components/app-shell/app-shell.meta.js +176 -0
- package/dist/components/app-shell/index.js +7 -0
- package/dist/components/area-chart/area-chart.js +10 -0
- package/dist/components/area-chart/area-chart.meta.js +218 -0
- package/dist/components/area-chart/index.js +7 -0
- package/dist/components/attachment/attachment.js +176 -0
- package/dist/components/attachment/attachment.meta.js +179 -0
- package/dist/components/attachment/attachment.variants.js +47 -0
- package/dist/components/attachment/index.js +7 -0
- package/dist/components/avatar/avatar.js +192 -0
- package/dist/components/avatar/avatar.meta.js +233 -0
- package/dist/components/avatar/avatar.variants.js +44 -0
- package/dist/components/avatar/index.js +22 -0
- package/dist/components/badge/badge.js +26 -0
- package/dist/components/badge/badge.meta.js +171 -0
- package/dist/components/badge/badge.variants.js +103 -0
- package/dist/components/badge/index.js +11 -0
- package/dist/components/banner/banner.js +76 -0
- package/dist/components/banner/banner.meta.js +172 -0
- package/dist/components/banner/banner.variants.js +44 -0
- package/dist/components/banner/index.js +9 -0
- package/dist/components/bar-chart/bar-chart.js +10 -0
- package/dist/components/bar-chart/bar-chart.meta.js +245 -0
- package/dist/components/bar-chart/index.js +7 -0
- package/dist/components/bottom-nav/bottom-nav.js +63 -0
- package/dist/components/bottom-nav/bottom-nav.meta.js +95 -0
- package/dist/components/bottom-nav/index.js +11 -0
- package/dist/components/breadcrumbs/breadcrumbs.js +90 -0
- package/dist/components/breadcrumbs/breadcrumbs.meta.js +111 -0
- package/dist/components/breadcrumbs/index.js +17 -0
- package/dist/components/browser/browser.js +39 -0
- package/dist/components/browser/browser.meta.js +80 -0
- package/dist/components/browser/index.js +7 -0
- package/dist/components/bubble/bubble.js +110 -0
- package/dist/components/bubble/bubble.meta.js +158 -0
- package/dist/components/bubble/bubble.variants.js +90 -0
- package/dist/components/bubble/index.js +13 -0
- package/dist/components/button/button.js +87 -0
- package/dist/components/button/button.meta.js +302 -0
- package/dist/components/button/button.variants.js +137 -0
- package/dist/components/button/index.js +9 -0
- package/dist/components/calendar/calendar.js +89 -0
- package/dist/components/calendar/calendar.meta.js +136 -0
- package/dist/components/calendar/index.js +7 -0
- package/dist/components/card/card.js +203 -0
- package/dist/components/card/card.meta.js +212 -0
- package/dist/components/card/index.js +23 -0
- package/dist/components/carousel/carousel.js +428 -0
- package/dist/components/carousel/carousel.meta.js +100 -0
- package/dist/components/carousel/index.js +7 -0
- package/dist/components/chat-composer/chat-composer.js +174 -0
- package/dist/components/chat-composer/chat-composer.meta.js +241 -0
- package/dist/components/chat-composer/chat-composer.variants.js +17 -0
- package/dist/components/chat-composer/index.js +7 -0
- package/dist/components/checkbox/checkbox-mark.js +28 -0
- package/dist/components/checkbox/checkbox.js +30 -0
- package/dist/components/checkbox/checkbox.meta.js +127 -0
- package/dist/components/checkbox/index.js +7 -0
- package/dist/components/checkbox-card/checkbox-card.js +94 -0
- package/dist/components/checkbox-card/checkbox-card.meta.js +154 -0
- package/dist/components/checkbox-card/checkbox-card.variants.js +43 -0
- package/dist/components/checkbox-card/index.js +11 -0
- package/dist/components/code/code.js +19 -0
- package/dist/components/code/code.meta.js +71 -0
- package/dist/components/code/index.js +7 -0
- package/dist/components/code-block/code-block.js +84 -0
- package/dist/components/code-block/code-block.meta.js +103 -0
- package/dist/components/code-block/index.js +8 -0
- package/dist/components/collapsible/collapsible.js +38 -0
- package/dist/components/collapsible/collapsible.meta.js +117 -0
- package/dist/components/collapsible/index.js +15 -0
- package/dist/components/color-field/color-field.js +293 -0
- package/dist/components/color-field/color-field.meta.js +142 -0
- package/dist/components/color-field/color.js +69 -0
- package/dist/components/color-field/index.js +15 -0
- package/dist/components/combobox/combobox.js +486 -0
- package/dist/components/combobox/combobox.meta.js +217 -0
- package/dist/components/combobox/index.js +37 -0
- package/dist/components/command-menu/command-menu-trigger.js +122 -0
- package/dist/components/command-menu/command-menu.js +433 -0
- package/dist/components/command-menu/command-menu.meta.js +279 -0
- package/dist/components/command-menu/index.js +21 -0
- package/dist/components/context-card/context-card.js +62 -0
- package/dist/components/context-card/context-card.meta.js +106 -0
- package/dist/components/context-card/index.js +17 -0
- package/dist/components/context-menu/context-menu.js +92 -0
- package/dist/components/context-menu/context-menu.meta.js +138 -0
- package/dist/components/context-menu/index.js +23 -0
- package/dist/components/data-table/data-table.js +142 -0
- package/dist/components/data-table/data-table.meta.js +146 -0
- package/dist/components/data-table/index.js +9 -0
- package/dist/components/date-picker/date-picker.js +196 -0
- package/dist/components/date-picker/date-picker.meta.js +164 -0
- package/dist/components/date-picker/index.js +7 -0
- package/dist/components/date-tile/date-tile.js +29 -0
- package/dist/components/date-tile/date-tile.meta.js +72 -0
- package/dist/components/date-tile/index.js +7 -0
- package/dist/components/destructive-action-modal/destructive-action-modal.js +130 -0
- package/dist/components/destructive-action-modal/destructive-action-modal.meta.js +136 -0
- package/dist/components/destructive-action-modal/index.js +9 -0
- package/dist/components/dialog/dialog.js +207 -0
- package/dist/components/dialog/dialog.meta.js +239 -0
- package/dist/components/dialog/index.js +45 -0
- package/dist/components/disclosure-picker/disclosure-picker.js +417 -0
- package/dist/components/disclosure-picker/disclosure-picker.meta.js +213 -0
- package/dist/components/disclosure-picker/index.js +21 -0
- package/dist/components/empty-state/empty-state.js +52 -0
- package/dist/components/empty-state/empty-state.meta.js +158 -0
- package/dist/components/empty-state/index.js +7 -0
- package/dist/components/entity/entity.js +70 -0
- package/dist/components/entity/entity.meta.js +108 -0
- package/dist/components/entity/entity.variants.js +16 -0
- package/dist/components/entity/index.js +7 -0
- package/dist/components/feedback/feedback.js +93 -0
- package/dist/components/feedback/feedback.meta.js +109 -0
- package/dist/components/feedback/index.js +7 -0
- package/dist/components/fieldset/fieldset.js +80 -0
- package/dist/components/fieldset/fieldset.meta.js +160 -0
- package/dist/components/fieldset/index.js +17 -0
- package/dist/components/file-dropzone/file-dropzone.js +193 -0
- package/dist/components/file-dropzone/file-dropzone.meta.js +210 -0
- package/dist/components/file-dropzone/file-dropzone.variants.js +26 -0
- package/dist/components/file-dropzone/index.js +13 -0
- package/dist/components/font-picker/font-picker.js +77 -0
- package/dist/components/font-picker/font-picker.meta.js +119 -0
- package/dist/components/font-picker/index.js +7 -0
- package/dist/components/form/form.js +145 -0
- package/dist/components/form/form.meta.js +142 -0
- package/dist/components/form/index.js +11 -0
- package/dist/components/gliding-highlight/gliding-highlight.js +50 -0
- package/dist/components/gliding-highlight/gliding-highlight.meta.js +135 -0
- package/dist/components/gliding-highlight/index.js +9 -0
- package/dist/components/gradient-art/gradient-art.js +168 -0
- package/dist/components/gradient-art/gradient-art.meta.js +81 -0
- package/dist/components/gradient-art/index.js +7 -0
- package/dist/components/gradient-art/renderer.js +336 -0
- package/dist/components/inline-status/index.js +7 -0
- package/dist/components/inline-status/inline-status.js +33 -0
- package/dist/components/inline-status/inline-status.meta.js +83 -0
- package/dist/components/inline-status/inline-status.variants.js +18 -0
- package/dist/components/input/index.js +7 -0
- package/dist/components/input/input.js +33 -0
- package/dist/components/input/input.meta.js +155 -0
- package/dist/components/kbd/index.js +8 -0
- package/dist/components/kbd/kbd.js +54 -0
- package/dist/components/kbd/kbd.meta.js +95 -0
- package/dist/components/label/index.js +7 -0
- package/dist/components/label/label.js +51 -0
- package/dist/components/label/label.meta.js +98 -0
- package/dist/components/line-chart/index.js +7 -0
- package/dist/components/line-chart/line-chart.js +10 -0
- package/dist/components/line-chart/line-chart.meta.js +222 -0
- package/dist/components/marker/index.js +7 -0
- package/dist/components/marker/marker.js +39 -0
- package/dist/components/marker/marker.meta.js +132 -0
- package/dist/components/marker/marker.variants.js +25 -0
- package/dist/components/marquee/index.js +7 -0
- package/dist/components/marquee/marquee.js +146 -0
- package/dist/components/marquee/marquee.meta.js +106 -0
- package/dist/components/media-image/index.js +7 -0
- package/dist/components/media-image/media-image.js +56 -0
- package/dist/components/media-image/media-image.meta.js +92 -0
- package/dist/components/media-tile/index.js +7 -0
- package/dist/components/media-tile/media-tile.js +135 -0
- package/dist/components/media-tile/media-tile.meta.js +133 -0
- package/dist/components/media-tile/media-tile.variants.js +48 -0
- package/dist/components/menu/index.js +33 -0
- package/dist/components/menu/menu.js +168 -0
- package/dist/components/menu/menu.meta.js +176 -0
- package/dist/components/message/index.js +7 -0
- package/dist/components/message/message.js +83 -0
- package/dist/components/message/message.meta.js +150 -0
- package/dist/components/message/message.variants.js +41 -0
- package/dist/components/message-scroller/index.js +9 -0
- package/dist/components/message-scroller/message-scroller.js +300 -0
- package/dist/components/message-scroller/message-scroller.meta.js +172 -0
- package/dist/components/meter/index.js +20 -0
- package/dist/components/meter/meter.js +112 -0
- package/dist/components/meter/meter.meta.js +227 -0
- package/dist/components/meter/meter.variants.js +61 -0
- package/dist/components/middle-truncate/index.js +7 -0
- package/dist/components/middle-truncate/middle-truncate.js +28 -0
- package/dist/components/middle-truncate/middle-truncate.meta.js +77 -0
- package/dist/components/note/index.js +9 -0
- package/dist/components/note/note.js +73 -0
- package/dist/components/note/note.meta.js +112 -0
- package/dist/components/note/note.variants.js +22 -0
- package/dist/components/notification-badge/index.js +15 -0
- package/dist/components/notification-badge/notification-badge.js +48 -0
- package/dist/components/notification-badge/notification-badge.meta.js +107 -0
- package/dist/components/notification-badge/notification-badge.variants.js +57 -0
- package/dist/components/number-field/index.js +7 -0
- package/dist/components/number-field/number-field.js +70 -0
- package/dist/components/number-field/number-field.meta.js +109 -0
- package/dist/components/otp-field/index.js +7 -0
- package/dist/components/otp-field/otp-field.js +70 -0
- package/dist/components/otp-field/otp-field.meta.js +197 -0
- package/dist/components/overflow-menu/index.js +7 -0
- package/dist/components/overflow-menu/overflow-menu.js +26 -0
- package/dist/components/overflow-menu/overflow-menu.meta.js +100 -0
- package/dist/components/page-header/index.js +7 -0
- package/dist/components/page-header/page-header.js +34 -0
- package/dist/components/page-header/page-header.meta.js +104 -0
- package/dist/components/page-loader/index.js +7 -0
- package/dist/components/page-loader/page-loader.js +37 -0
- package/dist/components/page-loader/page-loader.meta.js +85 -0
- package/dist/components/pagination/index.js +7 -0
- package/dist/components/pagination/pagination.js +105 -0
- package/dist/components/pagination/pagination.meta.js +141 -0
- package/dist/components/panel-group/index.js +10 -0
- package/dist/components/panel-group/panel-group.js +168 -0
- package/dist/components/panel-group/panel-group.meta.js +153 -0
- package/dist/components/panel-section/index.js +9 -0
- package/dist/components/panel-section/panel-section.js +115 -0
- package/dist/components/panel-section/panel-section.meta.js +156 -0
- package/dist/components/phone/index.js +7 -0
- package/dist/components/phone/phone.js +50 -0
- package/dist/{chunk-KFZ43RKU.js → components/phone/phone.meta.js} +3 -50
- package/dist/{chunk-NEQNXI2G.js → components/phone/status-icons.js} +4 -4
- package/dist/components/pie-chart/index.js +7 -0
- package/dist/components/pie-chart/pie-chart.js +191 -0
- package/dist/components/pie-chart/pie-chart.meta.js +210 -0
- package/dist/components/pill/index.js +9 -0
- package/dist/components/pill/pill.js +24 -0
- package/dist/components/pill/pill.meta.js +108 -0
- package/dist/components/pill/pill.variants.js +65 -0
- package/dist/components/popover/index.js +23 -0
- package/dist/components/popover/popover.js +100 -0
- package/dist/components/popover/popover.meta.js +136 -0
- package/dist/components/progress/index.js +19 -0
- package/dist/components/progress/progress.js +70 -0
- package/dist/components/progress/progress.meta.js +171 -0
- package/dist/components/progress/progress.variants.js +17 -0
- package/dist/components/radar-chart/index.js +7 -0
- package/dist/components/radar-chart/radar-chart.js +161 -0
- package/dist/components/radar-chart/radar-chart.meta.js +204 -0
- package/dist/components/radial-chart/index.js +7 -0
- package/dist/components/radial-chart/radial-chart.js +175 -0
- package/dist/components/radial-chart/radial-chart.meta.js +216 -0
- package/dist/components/radio/index.js +8 -0
- package/dist/components/radio/radio.js +39 -0
- package/dist/components/radio/radio.meta.js +104 -0
- package/dist/components/radio-card/index.js +7 -0
- package/dist/components/radio-card/radio-card.js +81 -0
- package/dist/components/radio-card/radio-card.meta.js +144 -0
- package/dist/components/radio-card/radio-card.variants.js +43 -0
- package/dist/components/relative-time/index.js +7 -0
- package/dist/components/relative-time/relative-time.js +47 -0
- package/dist/components/relative-time/relative-time.meta.js +77 -0
- package/dist/components/scroller/index.js +7 -0
- package/dist/components/scroller/scroller.js +111 -0
- package/dist/components/scroller/scroller.meta.js +118 -0
- package/dist/components/scrub-field/index.js +7 -0
- package/dist/components/scrub-field/scrub-field.js +124 -0
- package/dist/components/scrub-field/scrub-field.meta.js +139 -0
- package/dist/components/search-input/index.js +7 -0
- package/dist/components/search-input/search-input.js +91 -0
- package/dist/components/search-input/search-input.meta.js +129 -0
- package/dist/components/section-header/index.js +7 -0
- package/dist/components/section-header/section-header.js +39 -0
- package/dist/components/section-header/section-header.meta.js +174 -0
- package/dist/components/segmented-bar/index.js +14 -0
- package/dist/components/segmented-bar/segmented-bar.js +26 -0
- package/dist/components/segmented-bar/segmented-bar.meta.js +108 -0
- package/dist/components/segmented-bar/segmented-bar.variants.js +39 -0
- package/dist/components/select/index.js +25 -0
- package/dist/components/select/select.js +191 -0
- package/dist/components/select/select.meta.js +189 -0
- package/dist/components/separator/index.js +7 -0
- package/dist/components/separator/separator.js +42 -0
- package/dist/components/separator/separator.meta.js +84 -0
- package/dist/components/settings-menu/index.js +11 -0
- package/dist/components/settings-menu/settings-menu.js +101 -0
- package/dist/components/settings-menu/settings-menu.meta.js +177 -0
- package/dist/components/settings-menu/settings-menu.variants.js +15 -0
- package/dist/components/sheet/index.js +29 -0
- package/dist/components/sheet/sheet.js +121 -0
- package/dist/components/sheet/sheet.meta.js +167 -0
- package/dist/components/sheet/sheet.variants.js +21 -0
- package/dist/components/show-more/index.js +7 -0
- package/dist/components/show-more/show-more.js +87 -0
- package/dist/components/show-more/show-more.meta.js +79 -0
- package/dist/components/side-nav/index.js +13 -0
- package/dist/components/side-nav/side-nav.js +75 -0
- package/dist/components/side-nav/side-nav.meta.js +139 -0
- package/dist/components/side-nav/side-nav.variants.js +34 -0
- package/dist/components/sidebar/index.js +21 -0
- package/dist/components/sidebar/sidebar.js +196 -0
- package/dist/components/sidebar/sidebar.meta.js +118 -0
- package/dist/components/skeleton/index.js +7 -0
- package/dist/components/skeleton/skeleton.js +16 -0
- package/dist/components/skeleton/skeleton.meta.js +90 -0
- package/dist/components/slider/index.js +21 -0
- package/dist/components/slider/slider.js +115 -0
- package/dist/components/slider/slider.meta.js +214 -0
- package/dist/components/sparkline/index.js +7 -0
- package/dist/components/sparkline/sparkline.js +84 -0
- package/dist/components/sparkline/sparkline.meta.js +147 -0
- package/dist/components/spinner/index.js +7 -0
- package/dist/components/spinner/spinner.js +59 -0
- package/dist/components/spinner/spinner.meta.js +118 -0
- package/dist/components/status-bar/index.js +9 -0
- package/dist/components/status-bar/status-bar.js +40 -0
- package/dist/components/status-bar/status-bar.meta.js +117 -0
- package/dist/components/status-dot/index.js +9 -0
- package/dist/components/status-dot/status-dot.js +40 -0
- package/dist/components/status-dot/status-dot.meta.js +113 -0
- package/dist/components/status-dot/status-dot.variants.js +28 -0
- package/dist/components/stepper/index.js +17 -0
- package/dist/components/stepper/stepper.js +161 -0
- package/dist/components/stepper/stepper.meta.js +190 -0
- package/dist/components/stepper/stepper.variants.js +72 -0
- package/dist/components/streaming-text/index.js +7 -0
- package/dist/components/streaming-text/streaming-text.js +55 -0
- package/dist/components/streaming-text/streaming-text.meta.js +147 -0
- package/dist/components/switch/index.js +7 -0
- package/dist/components/switch/switch.js +26 -0
- package/dist/components/switch/switch.meta.js +133 -0
- package/dist/components/table/index.js +23 -0
- package/dist/components/table/table.js +117 -0
- package/dist/components/table/table.meta.js +143 -0
- package/dist/components/tabs/index.js +17 -0
- package/dist/components/tabs/tabs.js +58 -0
- package/dist/components/tabs/tabs.meta.js +120 -0
- package/dist/components/textarea/index.js +7 -0
- package/dist/components/textarea/textarea.js +38 -0
- package/dist/components/textarea/textarea.meta.js +117 -0
- package/dist/components/theme-selector/index.js +11 -0
- package/dist/components/theme-selector/theme-selector.js +145 -0
- package/dist/components/theme-selector/theme-selector.meta.js +181 -0
- package/dist/components/theme-switcher/index.js +7 -0
- package/dist/components/theme-switcher/theme-switcher.js +106 -0
- package/dist/components/theme-switcher/theme-switcher.meta.js +99 -0
- package/dist/components/thinking-orb/index.js +9 -0
- package/dist/components/thinking-orb/thinking-orb.js +126 -0
- package/dist/components/thinking-orb/thinking-orb.meta.js +142 -0
- package/dist/components/thinking-orb/thinking-orb.render.js +252 -0
- package/dist/components/toast/index.js +29 -0
- package/dist/components/toast/toast.js +254 -0
- package/dist/components/toast/toast.meta.js +261 -0
- package/dist/components/toggle/index.js +13 -0
- package/dist/components/toggle/toggle.js +51 -0
- package/dist/components/toggle/toggle.meta.js +129 -0
- package/dist/components/toolbar/index.js +27 -0
- package/dist/components/toolbar/toolbar.js +80 -0
- package/dist/components/toolbar/toolbar.meta.js +272 -0
- package/dist/components/toolbar/toolbar.variants.js +16 -0
- package/dist/components/tooltip/index.js +21 -0
- package/dist/components/tooltip/tooltip.js +74 -0
- package/dist/components/tooltip/tooltip.meta.js +156 -0
- package/dist/components/tree/index.js +12 -0
- package/dist/components/tree/tree.js +364 -0
- package/dist/components/tree/tree.meta.js +334 -0
- package/dist/components/video/index.js +7 -0
- package/dist/components/video/video.js +59 -0
- package/dist/components/video/video.meta.js +81 -0
- package/dist/components/workspace/index.js +9 -0
- package/dist/components/workspace/workspace.js +79 -0
- package/dist/components/workspace/workspace.meta.js +183 -0
- package/dist/foundations/accessibility.js +107 -0
- package/dist/foundations/index.js +25 -792
- package/dist/foundations/motion.js +135 -0
- package/dist/foundations/scrolling.js +62 -0
- package/dist/foundations/shape.js +113 -0
- package/dist/foundations/spacing.js +121 -0
- package/dist/foundations/surfaces.js +107 -0
- package/dist/foundations/types.js +0 -0
- package/dist/foundations/typography.js +152 -0
- package/dist/icon/Icon.js +30 -0
- package/dist/icon/IconProvider.js +27 -0
- package/dist/icon/compose.js +13 -0
- package/dist/icon/index.d.ts +13 -5
- package/dist/icon/index.js +13 -2
- package/dist/icon/registry.js +0 -0
- package/dist/icon-sets/brands.generated.js +123 -0
- package/dist/icon-sets/brands.js +10 -123
- package/dist/icon-sets/reicon.d.ts +3 -1
- package/dist/icon-sets/reicon.js +209 -1
- package/dist/index.d.ts +618 -34
- package/dist/index.js +211 -11351
- package/dist/ios/chrome.js +36 -0
- package/dist/ios/index.js +19 -315
- package/dist/ios/liquid-glass.js +103 -0
- package/dist/ios/native-app.js +46 -0
- package/dist/ios/navigation.js +63 -0
- package/dist/ios/phone-frame.js +64 -0
- package/dist/ios/preview-theme.js +83 -0
- package/dist/ios/preview.js +10 -137
- package/dist/ios/sheet.js +85 -0
- package/dist/ios/theme.js +19 -0
- package/dist/layout/AspectBox.js +27 -0
- package/dist/layout/Bleed.js +15 -0
- package/dist/layout/Center.js +21 -0
- package/dist/layout/Columns.js +70 -0
- package/dist/layout/Grid.js +50 -0
- package/dist/layout/GridOverlay.js +41 -0
- package/dist/layout/Page.js +11 -0
- package/dist/layout/Region.js +39 -0
- package/dist/layout/Row.js +45 -0
- package/dist/layout/SafeArea.js +26 -0
- package/dist/layout/Spacer.js +9 -0
- package/dist/layout/SplitPane.js +55 -0
- package/dist/layout/Stack.js +42 -0
- package/dist/layout/_responsive.js +16 -0
- package/dist/layout/_safeArea.js +18 -0
- package/dist/layout/index.d.ts +30 -8
- package/dist/layout/index.js +14 -2
- package/dist/lib/anchored-scroll.js +7 -0
- package/dist/lib/bleed.js +7 -0
- package/dist/lib/chart/cartesian.js +365 -0
- package/dist/lib/chart/chart-frame.js +163 -0
- package/dist/lib/chart/chart-legend.js +13 -0
- package/dist/lib/chart/chart-tooltip.js +38 -0
- package/dist/lib/chart/format.js +24 -0
- package/dist/lib/chart/geometry.js +114 -0
- package/dist/lib/chart/motion.js +34 -0
- package/dist/lib/chart/scale.js +72 -0
- package/dist/lib/chart/types.js +45 -0
- package/dist/lib/chart/use-arrival.js +41 -0
- package/dist/{chunk-SRQWQUTW.js → lib/cn.js} +5 -5
- package/dist/lib/component-guidance.js +215 -0
- package/dist/lib/density.js +54 -0
- package/dist/lib/field-shell.js +11 -0
- package/dist/lib/glyphs.js +9 -0
- package/dist/lib/heading-row.js +36 -0
- package/dist/lib/highlight-glide.js +84 -0
- package/dist/lib/hit-target.js +5 -0
- package/dist/lib/meta.js +0 -0
- package/dist/lib/portal-theme-scope.js +74 -0
- package/dist/{chunk-FKZXAFIY.js → lib/presentation.js} +8 -3
- package/dist/lib/refs.js +14 -0
- package/dist/lib/scrub-gesture.js +50 -0
- package/dist/lib/segmented.js +13 -0
- package/dist/lib/selection-card.js +41 -0
- package/dist/lib/status-tone.js +13 -0
- package/dist/lib/taxonomy.js +214 -0
- package/dist/lib/trim-label.js +20 -0
- package/dist/lib/use-copy.js +27 -0
- package/dist/lib/use-edge-fade.js +32 -0
- package/dist/lib/use-isomorphic-layout-effect.js +8 -0
- package/dist/lib/use-resize-observer.js +18 -0
- package/dist/lib/use-scroll-activity.js +52 -0
- package/dist/lib/use-wheel-to-horizontal.js +65 -0
- package/dist/motion/MotionProvider.js +75 -0
- package/dist/motion/Presence.js +112 -0
- package/dist/motion/Reveal.js +42 -0
- package/dist/motion/TextReveal.js +61 -0
- package/dist/motion/index.js +24 -0
- package/dist/motion/useInView.js +36 -0
- package/dist/motion/useReducedMotion.js +25 -0
- package/dist/motion/viewTransition.js +47 -0
- package/dist/patterns/auth-page.js +90 -0
- package/dist/patterns/auth-page.meta.js +27 -0
- package/dist/patterns/brand-landing-page.js +341 -0
- package/dist/patterns/brand-landing-page.meta.js +28 -0
- package/dist/patterns/chat-panel.js +185 -0
- package/dist/patterns/chat-panel.meta.js +31 -0
- package/dist/patterns/dashboard-page.js +332 -0
- package/dist/patterns/dashboard-page.meta.js +29 -0
- package/dist/patterns/feed-page.js +205 -0
- package/dist/patterns/feed-page.meta.js +25 -0
- package/dist/patterns/index.js +34 -2140
- package/dist/patterns/landing-page.js +356 -0
- package/dist/patterns/landing-page.meta.js +26 -0
- package/dist/patterns/list-detail-page.js +373 -0
- package/dist/patterns/list-detail-page.meta.js +24 -0
- package/dist/patterns/settings-page.js +268 -0
- package/dist/patterns/settings-page.meta.js +27 -0
- package/dist/presets/index.d.ts +7 -1
- package/dist/presets/index.js +53 -185
- package/dist/styles.css +118 -3
- package/dist/{theme-provider-kQszHYji.d.ts → theme-provider-S0NVJs2Y.d.ts} +6 -1
- package/dist/{chunk-NETUFI34.js → themes/_contract.js} +14 -2
- package/dist/themes/atelier/index.js +17 -1
- package/dist/themes/contrast.js +288 -0
- package/dist/themes/ember/ember.css +6 -6
- package/dist/themes/ember/index.js +16 -1
- package/dist/themes/index.d.ts +1 -1
- package/dist/themes/index.js +3 -8
- package/dist/themes/meridian/index.js +16 -1
- package/dist/themes/poppy/index.js +17 -1
- package/dist/themes/prism/index.js +17 -1
- package/dist/themes/prism/prism.css +2 -2
- package/dist/themes/registry.js +59 -0
- package/dist/themes/sable/index.js +16 -1
- package/dist/{chunk-EFLC4NA3.js → themes/theme-provider.js} +38 -54
- package/dist/themes/verdant/index.js +17 -1
- package/llms/blocks/media-player-block.md +20 -7
- package/llms/blocks/product-card-block.md +6 -4
- package/llms/blocks/task-list-block.md +1 -1
- package/llms/blocks/traffic-sources-block.md +1 -1
- package/llms/blocks/traffic-trend-block.md +5 -3
- package/llms/blocks/verify-code-block.md +1 -1
- package/llms/components/app-shell.md +5 -2
- package/llms/components/avatar.md +25 -3
- package/llms/components/button.md +20 -1
- package/llms/components/card.md +1 -1
- package/llms/components/disclosure-picker.md +168 -0
- package/llms/components/font-picker.md +104 -0
- package/llms/components/media-image.md +98 -0
- package/llms/components/media-tile.md +143 -0
- package/llms/components/page-header.md +6 -5
- package/llms/components/pie-chart.md +2 -2
- package/llms/components/radial-chart.md +1 -1
- package/llms/components/section-header.md +144 -0
- package/llms/components/sheet.md +10 -0
- package/llms/components/sidebar.md +4 -3
- package/llms/components/slider.md +14 -2
- package/llms/components/theme-selector.md +44 -4
- package/llms/components/tree.md +3 -3
- package/llms/layout/grid.md +24 -3
- package/llms/layout/region.md +16 -3
- package/llms/patterns/dashboard-page.md +3 -6
- package/llms/patterns/feed-page.md +4 -4
- package/llms/patterns/list-detail-page.md +1 -0
- package/llms/patterns/settings-page.md +2 -5
- package/llms/reference/utilities.md +84 -4
- package/llms/tasks/building-a-page.md +34 -3
- package/llms/tasks/converting-an-app.md +14 -4
- package/llms/tasks/creating-a-preset.md +4 -1
- package/llms/tasks/reviewing-a-design.md +19 -0
- package/llms/tasks/using-presets.md +19 -0
- package/llms-full.txt +1066 -78
- package/llms.txt +26 -9
- package/package.json +1 -1
- package/registry.json +2342 -164
- package/scripts/agent-setup.mjs +90 -0
- package/scripts/design-presets.mjs +59 -6
- package/scripts/grade.mjs +195 -29
- package/scripts/lint-rules.mjs +486 -10
- package/scripts/review-evidence.mjs +28 -1
- package/skills/sable/SKILL.md +67 -24
- package/targets/ios.json +45 -0
- package/dist/chunk-3IWZYBGH.js +0 -398
- package/dist/chunk-7MG4SHYX.js +0 -15
- package/dist/chunk-7QSDYBXO.js +0 -7421
- package/dist/chunk-ADLLDWLW.js +0 -15
- package/dist/chunk-C4Z3CF54.js +0 -15
- package/dist/chunk-EDXLM7YL.js +0 -5078
- package/dist/chunk-EHH4MHO6.js +0 -14
- package/dist/chunk-K7RUHUGC.js +0 -14
- package/dist/chunk-KO3NAEBF.js +0 -15
- package/dist/chunk-L4IEIQQA.js +0 -14
- package/dist/chunk-QJ6FWTUG.js +0 -40
- package/dist/chunk-VVW4UCDI.js +0 -4908
- package/dist/chunk-XAZGMY73.js +0 -187
|
@@ -1,798 +1,31 @@
|
|
|
1
|
-
// src/foundations/accessibility.ts
|
|
2
|
-
var accessibility = {
|
|
3
|
-
slug: "accessibility",
|
|
4
|
-
title: "Accessibility",
|
|
5
|
-
summary: "Sable's components ship accessible behavior from Base UI. These rules cover what an app composing them still has to get right.",
|
|
6
|
-
headline: "WCAG AA \xB7 44px targets \xB7 visible focus \xB7 reduced motion \xB7 logical properties",
|
|
7
|
-
vocabularies: [
|
|
8
|
-
{
|
|
9
|
-
title: "Floors",
|
|
10
|
-
terms: [
|
|
11
|
-
{ name: "Contrast", value: "4.5:1 text \xB7 3:1 UI and large text", use: "Every semantic text/surface pair is checked per theme by contrast.test.ts." },
|
|
12
|
-
{ name: "Hit area", value: "44\xD744px", use: "hit-target keeps a small control at the floor without moving layout." },
|
|
13
|
-
{ name: "Focus", value: "ring-2 ring-ring, full opacity", use: "Visible on every interactive element, in light, dark and forced-colors." },
|
|
14
|
-
{ name: "Mobile input text", value: "\u2265 16px", use: "iOS Safari zooms the page on focus below it." },
|
|
15
|
-
{ name: "Reduced motion", value: "--motion-scale: 0", use: "Transitions resolve instantly; loaders hold their first frame." }
|
|
16
|
-
]
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
rules: [
|
|
20
|
-
{
|
|
21
|
-
rule: "Use the component, not a div with a click handler.",
|
|
22
|
-
why: "Base UI supplies roles, keyboard handling and focus management. A clickable div has none of them."
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
rule: "Every control has an accessible name.",
|
|
26
|
-
why: "Icon-only buttons take aria-label. Fields take a <Label> through <Field>, which also wires aria-describedby and aria-invalid."
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
rule: "A live region exists before it has anything to say.",
|
|
30
|
-
why: 'Render role="status" empty from the start and swap its text. A region inserted together with its content is announced inconsistently.'
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
rule: "On client-side navigation, update document.title and move focus to the new view's h1.",
|
|
34
|
-
why: "Route changes are silent to assistive technology unless the app announces them. AppShell supplies the skip link and landmarks; the app does the rest."
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
rule: "Keep submit enabled; validate on submit and focus the first invalid field.",
|
|
38
|
-
why: "A disabled submit hides why it is disabled. Focus is the only way a screen-reader user finds the error."
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
rule: "Use logical properties: ms-/me-/ps-/pe-/text-start/inset-inline-*.",
|
|
42
|
-
why: 'Flex and grid mirror for free under dir="rtl"; hand-positioned elements do not.',
|
|
43
|
-
enforcedBy: "design-lint: physical-direction (ratchet)"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
rule: "Decorative layers over content take pointer-events-none and aria-hidden.",
|
|
47
|
-
why: "Scrims, glows and fades otherwise swallow clicks on the controls beneath."
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
rule: "Check focus and disabled states under forced-colors: active.",
|
|
51
|
-
why: "Windows High Contrast overrides ring colors and erases opacity-based disabled styling."
|
|
52
|
-
}
|
|
53
|
-
],
|
|
54
|
-
noGos: [
|
|
55
|
-
{
|
|
56
|
-
never: "Color as the only signal (a red border with no message).",
|
|
57
|
-
instead: "Color plus text or an icon.",
|
|
58
|
-
why: "Roughly 1 in 12 men cannot tell red from green."
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
never: "Opacity on the focus ring.",
|
|
62
|
-
instead: "ring-ring at full opacity; tune the token in the theme.",
|
|
63
|
-
why: "A faded ring loses the contrast the theme tested.",
|
|
64
|
-
enforcedBy: "sable-lint: faded-focus-ring"
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
never: "A brand or status fill retuned under its old label \u2014 dark ink on a bright red that passes 4.5:1 at APCA Lc 42.",
|
|
68
|
-
instead: "A label that clears 4.5:1 and APCA Lc 60 in both modes. Move the fill's lightness, not its hue, keeping the ink its colour takes. A status colour on a surface is its own token (`--destructive-text`), so the fill can move freely.",
|
|
69
|
-
why: "The ratio alone passes labels that read muddy. A deliberate brand pair is allowed with a recorded reason (lint disable or preset exceptions).",
|
|
70
|
-
enforcedBy: "sable-lint: label-contrast"
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
never: 'Blocking paste, or type="number" for codes and card numbers.',
|
|
74
|
-
instead: 'type="text" inputMode="numeric" with the right autocomplete.',
|
|
75
|
-
why: "Blocking paste breaks password managers; number inputs scroll-change identifiers."
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
never: "forced-color-adjust: none to 'fix' high-contrast mode.",
|
|
79
|
-
instead: "Let the system colors apply; verify the result.",
|
|
80
|
-
why: "It removes the user's chosen colors exactly where they need them."
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
never: "outline: none without a replacement.",
|
|
84
|
-
instead: "focus-visible:ring-2 ring-ring.",
|
|
85
|
-
why: "Keyboard users lose their place."
|
|
86
|
-
}
|
|
87
|
-
],
|
|
88
|
-
details: [
|
|
89
|
-
{
|
|
90
|
-
title: "An icon-only button",
|
|
91
|
-
before: '<Button size="icon"><Icon name="xmark" /></Button>',
|
|
92
|
-
after: '<Button size="icon" aria-label="Close"><Icon name="xmark" /></Button>',
|
|
93
|
-
why: "Without a name, a screen reader announces only 'button'."
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
title: "A status that announces",
|
|
97
|
-
before: '{saved && <p role="status">Saved</p>}',
|
|
98
|
-
after: '<p role="status">{saved ? "Saved" : ""}</p>',
|
|
99
|
-
why: "The region is in the accessibility tree before its text changes, so the change is announced."
|
|
100
|
-
}
|
|
101
|
-
],
|
|
102
|
-
related: ["typography", "motion", "sable component <Name> --a11y"]
|
|
103
|
-
};
|
|
104
|
-
var accessibility_default = accessibility;
|
|
105
|
-
|
|
106
|
-
// src/foundations/motion.ts
|
|
107
|
-
var motion = {
|
|
108
|
-
slug: "motion",
|
|
109
|
-
title: "Motion",
|
|
110
|
-
summary: "Motion explains a change: where something came from, what responded, and that work is happening. It is subtle by default, built from named recipes, and one switch turns it off.",
|
|
111
|
-
headline: "4 slots \xB7 3 speeds \xB7 enter-only overshoot \xB7 1 kill switch",
|
|
112
|
-
vocabularies: [
|
|
113
|
-
{
|
|
114
|
-
title: "Three speeds",
|
|
115
|
-
note: "Match duration to the distance and size of what moves. Each value is multiplied by --motion-scale. Two edge steps sit outside the three: --motion-duration-instant (75ms) for a checkbox tick or tab swap, and --motion-duration-slower (500ms) for progress and value changes.",
|
|
116
|
-
terms: [
|
|
117
|
-
{ name: "fast --motion-duration-fast", value: "150ms", use: "Hover, press, focus, tooltips, small toggles. Also every exit." },
|
|
118
|
-
{ name: "base --motion-duration-base", value: "200ms", use: "Menus, popovers, tabs, disclosure. The default enter." },
|
|
119
|
-
{ name: "slow --motion-duration-slow", value: "300ms", use: "Dialogs, sheets, page-level transitions." }
|
|
120
|
-
]
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
title: "Recipes",
|
|
124
|
-
note: "Reach for one named class before writing a transition.",
|
|
125
|
-
terms: [
|
|
126
|
-
{ name: "motion-interactive", value: "fast \xB7 standard", use: "Hover/focus/press chrome on anything clickable. Replaces transition-colors." },
|
|
127
|
-
{ name: "motion-press", value: "fast \xB7 scale settle", use: "Tactile press. Always paired with motion-interactive." },
|
|
128
|
-
{ name: "motion-fade", value: "enter base / exit fast", use: "Scrims and backdrops." },
|
|
129
|
-
{ name: "motion-pop", value: "enter base \xB7 spring", use: "Anchored or centred surfaces: popovers, menus, dialogs." },
|
|
130
|
-
{ name: "motion-slide-{up,down,left,right}", value: "enter base / exit fast", use: "Directional surfaces: sheets, toasts." },
|
|
131
|
-
{ name: "motion-disclosure", value: "base \xB7 standard", use: "Height changes: accordion, collapsible." },
|
|
132
|
-
{ name: "motion-icon-swap", value: "enter \xB7 emphasized cross-fade", use: "Two icons of one control trading places: play/pause, outline/fill." },
|
|
133
|
-
{ name: "motion-draw", value: "slower \xB7 decelerate", use: 'SVG line art drawing on when a <Reveal> scrolls in. The path needs pathLength="1"; stagger siblings with --motion-draw-delay.' },
|
|
134
|
-
{ name: "motion-spin \xB7 pulse \xB7 shimmer \xB7 dot-bounce \xB7 ping", value: "loops", use: "Activity. Scale-aware, so reduced motion freezes them." }
|
|
135
|
-
]
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
title: "Easings",
|
|
139
|
-
terms: [
|
|
140
|
-
{ name: "ease-standard", value: "cubic-bezier(0.4, 0, 0.2, 1)", use: "The default for state changes." },
|
|
141
|
-
{ name: "ease-decelerate", value: "cubic-bezier(0, 0, 0.2, 1)", use: "Entrances: arrive fast, settle slowly." },
|
|
142
|
-
{ name: "ease-emphasized", value: "cubic-bezier(0.2, 0, 0, 1)", use: "Large surfaces covering a lot of distance." },
|
|
143
|
-
{ name: "ease-spring", value: "~4% overshoot", use: "A gentle pop on enter. Never on exit." },
|
|
144
|
-
{ name: "ease-bounce", value: "~10% overshoot", use: "A playful pop, opt-in only. Enter only, never on exit." }
|
|
145
|
-
]
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
title: "The four slots",
|
|
149
|
-
note: "Every interactive component answers all four, by filling the slot or declining it with a reason in meta.motion.",
|
|
150
|
-
terms: [
|
|
151
|
-
{ name: "arrival", value: "fade \xB7 slide \xB7 pop \xB7 Reveal", use: "How does it enter?" },
|
|
152
|
-
{ name: "response", value: "motion-interactive \xB7 motion-press", use: "What happens under the pointer?" },
|
|
153
|
-
{ name: "transition", value: "motion-disclosure \xB7 eased transform", use: "How does it move between its own states?" },
|
|
154
|
-
{ name: "activity", value: "spin \xB7 pulse \xB7 shimmer", use: "How do you tell working from frozen?" }
|
|
155
|
-
]
|
|
156
|
-
}
|
|
157
|
-
],
|
|
158
|
-
rules: [
|
|
159
|
-
{
|
|
160
|
-
rule: "Every motion has a reason: orientation, feedback, continuity or activity.",
|
|
161
|
-
why: "Motion with no reason is latency. If you cannot say which of the four it serves, remove it."
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
rule: "Reach for a recipe first; drop to tokens only when none fits.",
|
|
165
|
-
why: "Recipes are reviewed once and respect the kill switch. A bespoke transition string is reviewed never.",
|
|
166
|
-
enforcedBy: "sable-lint: transition-colors, transition-all"
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
rule: "Exits are one step faster than entrances, and never overshoot.",
|
|
170
|
-
why: "Something leaving should get out of the way. A bounce on exit draws the eye to what the user just dismissed."
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
rule: "Animate opacity and transform only.",
|
|
174
|
-
why: "They run on the compositor. Width, height, top and left cause layout on every frame. motion-disclosure is the one sanctioned exception."
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
rule: "Durations come from tokens, so --motion-scale can stop them.",
|
|
178
|
-
why: "prefers-reduced-motion and <MotionProvider> off both set --motion-scale to 0. A raw duration ignores that.",
|
|
179
|
-
enforcedBy: "sable-lint: raw-duration, builtin-animate"
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
rule: "Interactive elements always respond. Something styled for hover with no motion recipe snaps.",
|
|
183
|
-
why: "A snap between states reads as broken. The four prohibitive rules could not catch missing motion, so this one does.",
|
|
184
|
-
enforcedBy: "sable-lint: static-interactive"
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
rule: "JS motion reads useMotion().enabled and does nothing when it is false.",
|
|
188
|
-
why: "Reduced motion is an accessibility setting, not a style preference."
|
|
189
|
-
}
|
|
190
|
-
],
|
|
191
|
-
noGos: [
|
|
192
|
-
{
|
|
193
|
-
never: "Tailwind's animate-spin / animate-pulse / animate-ping / animate-bounce.",
|
|
194
|
-
instead: "motion-spin, motion-pulse, motion-ping, motion-dot-bounce.",
|
|
195
|
-
why: "The built-ins ignore --motion-scale, so reduced motion cannot freeze them.",
|
|
196
|
-
enforcedBy: "sable-lint: builtin-animate"
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
never: "duration-150, duration-300, or any raw duration utility.",
|
|
200
|
-
instead: "duration-(--motion-duration-fast) and siblings.",
|
|
201
|
-
why: "Raw durations bypass the kill switch.",
|
|
202
|
-
enforcedBy: "sable-lint: raw-duration"
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
never: "transition-all, or transition-colors on its own.",
|
|
206
|
-
instead: "motion-interactive, or name the properties you animate.",
|
|
207
|
-
why: "transition-all animates unrelated layout changes and defeats the browser's pre-computation.",
|
|
208
|
-
enforcedBy: "sable-lint: transition-all, transition-colors"
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
never: "Motion on first paint of static content, or staggered entrances on every list.",
|
|
212
|
-
instead: "Reveal for one deliberate moment per view.",
|
|
213
|
-
why: "Entrance choreography repeated everywhere becomes a loading delay."
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
never: "The motion library in the core package.",
|
|
217
|
-
instead: "src/motion (WAAPI, IntersectionObserver, View Transitions).",
|
|
218
|
-
why: "Sable's motion layer is zero-dependency; drag, FLIP and velocity springs belong in an optional adapter."
|
|
219
|
-
}
|
|
220
|
-
],
|
|
221
|
-
details: [
|
|
222
|
-
{
|
|
223
|
-
title: "Hover that respects the kill switch",
|
|
224
|
-
before: '<button className="transition-colors duration-150 sable-surface-interactive">',
|
|
225
|
-
after: '<button className="motion-interactive sable-surface-interactive">',
|
|
226
|
-
why: "Same feel, but reduced motion now resolves it instantly."
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
title: "Faster exits",
|
|
230
|
-
before: "transition: opacity 200ms ease-in-out; /* in and out */",
|
|
231
|
-
after: "data-[starting-style]: enter 200ms decelerate \xB7 data-[ending-style]: exit 150ms standard",
|
|
232
|
-
why: "motion-pop and motion-slide-* already do this; dismissing feels responsive instead of sluggish."
|
|
233
|
-
}
|
|
234
|
-
],
|
|
235
|
-
related: ["accessibility", "sable docs reviewing-motion", "sable token motion"]
|
|
236
|
-
};
|
|
237
|
-
var motion_default = motion;
|
|
238
|
-
|
|
239
|
-
// src/foundations/scrolling.ts
|
|
240
|
-
var scrolling = {
|
|
241
|
-
slug: "scrolling",
|
|
242
|
-
title: "Scrolling",
|
|
243
|
-
summary: "Every scroll container declares a policy. Vertical regions get a quiet bar that appears only while you are in them; horizontal strips get edge fades instead of a rail.",
|
|
244
|
-
headline: "2 policies \xB7 vertical gets a bar \xB7 horizontal gets a fade",
|
|
245
|
-
vocabularies: [
|
|
246
|
-
{
|
|
247
|
-
title: "Policies",
|
|
248
|
-
terms: [
|
|
249
|
-
{ name: "scrollbar-quiet", value: "4px thumb \xB7 hidden at rest \u2192 12% active \u2192 16% hovered \u2192 20% dragging", use: "The default for vertical scrolling. Grows to 6px under the pointer." },
|
|
250
|
-
{ name: "scrollbar-hidden", value: "no bar", use: "Only when something else carries the affordance \u2014 and you can name it (an edge fade, paging controls)." },
|
|
251
|
-
{ name: "<Scroller>", value: "edge fades + wheel mapping", use: "Horizontal strips: media rows, chip rows, tab overflow." },
|
|
252
|
-
{ name: "useScrollActivity", value: "data-scrolling for ~600ms", use: "Lets the bar respond to touch drags and programmatic scrolls, which hover cannot see." }
|
|
253
|
-
]
|
|
254
|
-
}
|
|
255
|
-
],
|
|
256
|
-
rules: [
|
|
257
|
-
{
|
|
258
|
-
rule: "Every overflow-*-auto|scroll declares scrollbar-quiet or scrollbar-hidden.",
|
|
259
|
-
why: "Left bare, the platform draws its own: on Windows, stepper arrows hard against the padding edge that poke out of rounded corners.",
|
|
260
|
-
enforcedBy: "sable-lint: unmanaged-overflow (also checked over every registry example)"
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
rule: "Vertical regions get a bar; horizontal regions get an edge fade.",
|
|
264
|
-
why: "A vertical list needs position feedback. A horizontal strip needs 'there is more this way', which a fade says better than a 4px rail."
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
rule: "A horizontal region must work without a horizontal input.",
|
|
268
|
-
why: "Most mice have no horizontal wheel. Scroller maps the vertical wheel sideways and handles arrow keys, Home and End."
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
rule: "Reveal on scrolling, not only on hover.",
|
|
272
|
-
why: "Touch drags and programmatic scrolls (a chat following a reply) hover nothing, and those are the moments a reader needs to see where they are."
|
|
273
|
-
}
|
|
274
|
-
],
|
|
275
|
-
noGos: [
|
|
276
|
-
{
|
|
277
|
-
never: "scrollbar-hidden on a vertical list with no fade or paging.",
|
|
278
|
-
instead: "scrollbar-quiet.",
|
|
279
|
-
why: "It leaves the reader with no sense of position and nothing to discover."
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
never: "A styled ::-webkit-scrollbar written per component.",
|
|
283
|
-
instead: "The scrollbar-quiet utility.",
|
|
284
|
-
why: "Chromium ignores ::-webkit-scrollbar once scrollbar-width is set; the utility handles both engines."
|
|
285
|
-
}
|
|
286
|
-
],
|
|
287
|
-
details: [
|
|
288
|
-
{
|
|
289
|
-
title: "A scrolling panel",
|
|
290
|
-
before: '<div className="overflow-y-auto">',
|
|
291
|
-
after: '<div className="overflow-y-auto scrollbar-quiet" ref={useScrollActivity()}>',
|
|
292
|
-
why: "No bar at rest; it appears while scrolling, hovered or focused, and fades a beat after. Content cut off at the edge is what says the region scrolls."
|
|
293
|
-
}
|
|
294
|
-
],
|
|
295
|
-
related: ["surfaces", "accessibility", "sable component Scroller"]
|
|
296
|
-
};
|
|
297
|
-
var scrolling_default = scrolling;
|
|
298
|
-
|
|
299
|
-
// src/foundations/shape.ts
|
|
300
|
-
var shape = {
|
|
301
|
-
slug: "shape",
|
|
302
|
-
title: "Shape",
|
|
303
|
-
summary: "Radius signals what something is: tight on containers, full on actions. Nested corners stay concentric, and one edge treatment marks a boundary.",
|
|
304
|
-
headline: "7 radius steps \xB7 concentric nesting \xB7 1 edge treatment per boundary",
|
|
305
|
-
vocabularies: [
|
|
306
|
-
{
|
|
307
|
-
title: "The radius ladder",
|
|
308
|
-
note: "Each step is 4px apart, so common insets land on a real step. Themes and the customizer retune every step together.",
|
|
309
|
-
terms: [
|
|
310
|
-
{ name: "rounded-none", value: "0", use: "Full-bleed media." },
|
|
311
|
-
{ name: "rounded-xs", value: "4px", use: "Tags, kbd, small inline marks." },
|
|
312
|
-
{ name: "rounded-sm", value: "8px", use: "Inputs, menu items, list rows." },
|
|
313
|
-
{ name: "rounded-md", value: "12px", use: "Popovers, menus, toasts." },
|
|
314
|
-
{ name: "rounded-lg", value: "16px", use: "Cards, dialogs, sheets." },
|
|
315
|
-
{ name: "rounded-xl", value: "20px", use: "Hero surfaces." },
|
|
316
|
-
{ name: "rounded-full", value: "pill", use: "Buttons (via --button-radius), avatars, pills, dots." }
|
|
317
|
-
]
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
title: "Edges",
|
|
321
|
-
terms: [
|
|
322
|
-
{ name: "border-border", value: "hairline", use: "The default edge: inputs, cards, badges." },
|
|
323
|
-
{ name: "border-border-strong", value: "hairline, higher contrast", use: "Structural rules: table grids, section separators." },
|
|
324
|
-
{ name: "border-border-floating", value: "faint, alpha", use: "Every floating surface: popovers, menus, selects, tooltips, dialogs, sheets, toasts. Lighter than border; the shadow carries the elevation." },
|
|
325
|
-
{ name: "ring-2 ring-ring", value: "2px focus ring", use: "Focus only. Never a thick border." }
|
|
326
|
-
]
|
|
327
|
-
}
|
|
328
|
-
],
|
|
329
|
-
rules: [
|
|
330
|
-
{
|
|
331
|
-
rule: "Nested corners are concentric: inner radius = outer radius \u2212 inset. Write it as rounded-concentric-<outer>/<inset>, not a hardcoded step.",
|
|
332
|
-
why: "A 12px popover with 4px padding takes 8px items. The utility computes the subtraction from the live tokens, so the pair stays concentric when a theme or the customizer rescales the radius ladder; a hardcoded rounded-sm drifts."
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
rule: "A list of rows inside a rounded popup uses popup-inset on the popup, rounded-popup-item on the rows and popup-bleed on a separator.",
|
|
336
|
-
why: "All three derive from the popup's radius: the inset grows to a third of it (never below --spacing(1), so density moves it too), the rows take what is left after the inset and the 1px border, and a separator bleeds by exactly the inset. A rounder look gets more room instead of rows that round into pills; the default ladder lands on a 4px inset and 7px rows.",
|
|
337
|
-
enforcedBy: "sable-lint: popup-separator-bleed"
|
|
338
|
-
},
|
|
339
|
-
{
|
|
340
|
-
rule: "A Button, Pill or Badge gets its look from a variant, never from override classes.",
|
|
341
|
-
why: `border, bg, rounded, ring and shadow classes on a component fight the variant's own hover, selected and focus states, and give one call site a look no other site can ask for. Use the variant that exists (Button variant="floating" over content, "on-color" on a fill; Pill appearance="outline"; Badge appearance and tone) or add one to the component.`,
|
|
342
|
-
enforcedBy: "sable-lint: component-restyle"
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
rule: "The outer radius is always at least the inner radius.",
|
|
346
|
-
why: "If the inset is larger than the outer radius, square the inner element or step the container up."
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
rule: "Never mix radii within one component.",
|
|
350
|
-
why: "A component reads as one object when its corners agree."
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
rule: "Actions are full; containers are tight.",
|
|
354
|
-
why: "The contrast itself says 'this is a control'. A theme flattens buttons through --button-radius, not by editing each one."
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
rule: "High-contrast outlines (selection, emphasis) are inset rings; hairline chrome is a border.",
|
|
358
|
-
why: "Rings anti-alias on rounded corners at any display scale; a 1px light border on dark snaps to the pixel grid and looks jagged."
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
rule: "One edge treatment per boundary: a border, a ring or a shadow.",
|
|
362
|
-
why: "Stacking all three makes an edge heavier than the content it holds."
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
rule: "Prefer a tonal step or space over a divider line.",
|
|
366
|
-
why: "The flattest thing that reads is best."
|
|
367
|
-
}
|
|
368
|
-
],
|
|
369
|
-
noGos: [
|
|
370
|
-
{
|
|
371
|
-
never: "rounded-[10px] or any off-ladder radius.",
|
|
372
|
-
instead: "The ladder step for the role.",
|
|
373
|
-
why: "Off-ladder radii cannot follow a theme or the customizer's radius control."
|
|
374
|
-
},
|
|
375
|
-
{
|
|
376
|
-
never: "A card inside a card with the same radius.",
|
|
377
|
-
instead: "Flatten the inner one, or use rounded-concentric-*.",
|
|
378
|
-
why: "Nested surfaces with equal corners look misaligned at every corner."
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
never: '<Button className="rounded-full">, <Pill className="border bg-card">, a -mx-1 separator in a popup.',
|
|
382
|
-
instead: "The component's variant (add one if it is missing); popup-bleed for the separator.",
|
|
383
|
-
why: "An override fights the component's own states and does not follow the radius ladder, the density or a theme.",
|
|
384
|
-
enforcedBy: "sable-lint: component-restyle, popup-separator-bleed"
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
never: "border-2 for emphasis or focus.",
|
|
388
|
-
instead: "ring-2 ring-ring for focus; a tonal fill for emphasis.",
|
|
389
|
-
why: "A thick border shifts layout and competes with the focus ring."
|
|
390
|
-
}
|
|
391
|
-
],
|
|
392
|
-
details: [
|
|
393
|
-
{
|
|
394
|
-
title: "Crisp selected outlines on dark surfaces",
|
|
395
|
-
before: '<button className="rounded-lg border border-foreground">',
|
|
396
|
-
after: '<button className="rounded-lg ring-[1.5px] ring-foreground ring-inset">',
|
|
397
|
-
why: "A 1px high-contrast border on a rounded corner snaps unevenly at fractional device-pixel ratios (Windows 125%/150%) and reads as jagged, worst light-on-dark. A box-shadow ring is anti-aliased instead, and inset keeps it from changing the element's size. Keep hairline borders for low-contrast chrome, where the snapping is invisible."
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
title: "Concentric menu items",
|
|
401
|
-
before: '<div className="rounded-md p-1"><div className="rounded-md">Item</div></div>',
|
|
402
|
-
after: '<div className="rounded-md popup-inset"><div className="rounded-popup-item">Item</div></div>',
|
|
403
|
-
why: "12px outer \u2212 4px inset = 8px inner at the default ladder, and the utilities keep the subtraction right when a preset or the customizer rescales the radius or the density."
|
|
404
|
-
}
|
|
405
|
-
],
|
|
406
|
-
related: ["surfaces", "spacing", "sable token radii"]
|
|
407
|
-
};
|
|
408
|
-
var shape_default = shape;
|
|
409
|
-
|
|
410
|
-
// src/foundations/spacing.ts
|
|
411
|
-
var spacing = {
|
|
412
|
-
slug: "spacing",
|
|
413
|
-
title: "Spacing & sizing",
|
|
414
|
-
summary: "One 4px scale governs every gap, inset and size, and space is assigned by role. Controls read a height ladder that a density scope can retune all at once.",
|
|
415
|
-
headline: "4px base \xB7 8px rhythm \xB7 3 roles \xB7 1 control ladder",
|
|
416
|
-
vocabularies: [
|
|
417
|
-
{
|
|
418
|
-
title: "Spacing by role",
|
|
419
|
-
note: "Steps below 2 (8px) are optical correction only.",
|
|
420
|
-
terms: [
|
|
421
|
-
{ name: "Inset p-4", value: "16px", use: "Padding inside a surface. p-3 for a dense card, px-3 py-2 for a row." },
|
|
422
|
-
{ name: "Stack, within a group gap-1.5 \u2013 gap-2", value: "6\u20138px", use: "Label to field, title to description." },
|
|
423
|
-
{ name: "Stack, between groups gap-4 \u2013 gap-6", value: "16\u201324px", use: "Field to field, card to card." },
|
|
424
|
-
{ name: "Stack, between sections gap-12+", value: "48px+", use: "Major regions of a page." },
|
|
425
|
-
{ name: "Inline gap-2 \u2013 gap-3", value: "8\u201312px", use: "Icon to label, control to control." }
|
|
426
|
-
]
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
title: "The control ladder",
|
|
430
|
-
note: 'Comfortable values; data-density="compact" (or <Density>) redefines every step for a region.',
|
|
431
|
-
terms: [
|
|
432
|
-
{ name: "h-(--control-xs)", value: "28px \xB7 compact 24", use: "Inline chips, tree rows." },
|
|
433
|
-
{ name: "h-(--control-sm)", value: "32px \xB7 compact 28", use: "Compact buttons, toolbar controls." },
|
|
434
|
-
{ name: "h-(--control-md)", value: "40px \xB7 compact 32", use: "The default: inputs, buttons, selects, menu triggers." },
|
|
435
|
-
{ name: "h-(--control-cta)", value: "44px \xB7 compact 36", use: "The primary call to action; clears the touch floor on its own." },
|
|
436
|
-
{ name: "h-(--control-lg)", value: "48px \xB7 compact 40", use: "Prominent or touch-first actions." }
|
|
437
|
-
]
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
title: "Icons and touch",
|
|
441
|
-
terms: [
|
|
442
|
-
{ name: "text-base (16px)", value: "sm", use: "Inline with label-md / body-sm." },
|
|
443
|
-
{ name: "text-xl (20px)", value: "md \xB7 default", use: "The default UI glyph beside label-lg." },
|
|
444
|
-
{ name: "hit-target", value: "44\xD744px", use: "Keeps a visually small control's hit area at the WCAG floor without moving layout." }
|
|
445
|
-
]
|
|
446
|
-
}
|
|
447
|
-
],
|
|
448
|
-
rules: [
|
|
449
|
-
{
|
|
450
|
-
rule: "Every margin, padding and gap is a step on the 4px scale.",
|
|
451
|
-
why: "Off-scale values break the rhythm that lets adjacent components line up without coordination."
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
rule: "Padding, gaps and insets in a component sit on the spacing scale: a step (p-4, gap-2) or --spacing(n) inside an arbitrary value (p-[var(--card-p,--spacing(6))]).",
|
|
455
|
-
why: 'The density (Character) control works by retuning --spacing, so anything on the scale moves with it. A literal p-[1.5rem] or padding: "12px" stays put while everything around it tightens. Keep fixed rem/px for what must not scale: a 1px hairline, a ring width.',
|
|
456
|
-
enforcedBy: "design-lint: fixed-spacing (library source)"
|
|
457
|
-
},
|
|
458
|
-
{
|
|
459
|
-
rule: "Related things sit closer than unrelated things.",
|
|
460
|
-
why: "Proximity groups content without a divider line. Use space first; reach for a border only when space is not enough."
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
rule: "Space lives on the container (gap), not on children (margins).",
|
|
464
|
-
why: "gap survives reordering, conditional children and RTL; per-child margins do not."
|
|
465
|
-
},
|
|
466
|
-
{
|
|
467
|
-
rule: "Control heights read the --control-* ladder.",
|
|
468
|
-
why: "A density scope retunes every control at once. A hardcoded height has to be found and overridden by hand.",
|
|
469
|
-
enforcedBy: "sable-lint: raw-control-height"
|
|
470
|
-
},
|
|
471
|
-
{
|
|
472
|
-
rule: "Single-column content inside a card stacks with flex flex-col and a gap, not CSS grid.",
|
|
473
|
-
why: "A grid track sizes to its widest min-content child, so one long unbreakable string (a URL, a code line, a table) blows the column out past the card. A flex column lets children shrink to the card."
|
|
474
|
-
},
|
|
475
|
-
{
|
|
476
|
-
rule: "A compact surface drops one whole step; a spacious one adds one.",
|
|
477
|
-
why: "Moving in whole steps keeps density changes proportional."
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
rule: "Every interactive element clears a 44\xD744px hit area.",
|
|
481
|
-
why: "WCAG 2.5.5. Use padding or hit-target to grow the hit area; never a bigger glyph."
|
|
482
|
-
},
|
|
483
|
-
{
|
|
484
|
-
rule: "Borderless controls get roughly twice the gap of bordered ones.",
|
|
485
|
-
why: "A border declares where a control ends. Two ghost buttons 12px apart read as one target."
|
|
486
|
-
}
|
|
487
|
-
],
|
|
488
|
-
noGos: [
|
|
489
|
-
{
|
|
490
|
-
never: "Arbitrary values: p-[15px], gap-[7px], mt-[13px].",
|
|
491
|
-
instead: "The nearest scale step.",
|
|
492
|
-
why: "Arbitrary values cannot follow density or a customized scale."
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
never: "h-[40px] on a control.",
|
|
496
|
-
instead: "h-(--control-md).",
|
|
497
|
-
why: "It will not follow <Density>.",
|
|
498
|
-
enforcedBy: "sable-lint: raw-control-height"
|
|
499
|
-
},
|
|
500
|
-
{
|
|
501
|
-
never: "Icons sized with type utilities (text-body-md on an <Icon>).",
|
|
502
|
-
instead: "text-{size} scale classes.",
|
|
503
|
-
why: "Type utilities carry line-height and tracking that misalign glyphs."
|
|
504
|
-
},
|
|
505
|
-
{
|
|
506
|
-
never: "A fixed width sized to an English label.",
|
|
507
|
-
instead: "padding-inline and max-width with wrapping.",
|
|
508
|
-
why: "German and Finnish run 30\u201350% longer."
|
|
509
|
-
}
|
|
510
|
-
],
|
|
511
|
-
details: [
|
|
512
|
-
{
|
|
513
|
-
title: "A dense region in one attribute",
|
|
514
|
-
before: '<div className="[&_button]:h-8 [&_input]:h-8">\u2026</div>',
|
|
515
|
-
after: '<Density value="compact">\u2026</Density>',
|
|
516
|
-
why: "Every control, menu and popup inside retunes, including portaled overlays."
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
title: "Grouping by space",
|
|
520
|
-
before: "<Separator /> between every field",
|
|
521
|
-
after: "<Stack gap={3}> between groups (24px), <Stack gap={1}> inside a group (8px)",
|
|
522
|
-
why: "Two spacing steps do the work of every divider, with less ink. Stack's numeric gap counts --rhythm-unit \u2014 two spacing steps \u2014 so it follows the density control like every other spacing value."
|
|
523
|
-
}
|
|
524
|
-
],
|
|
525
|
-
related: ["shape", "typography", "sable docs building-a-tool", "sable token layout"]
|
|
526
|
-
};
|
|
527
|
-
var spacing_default = spacing;
|
|
528
|
-
|
|
529
|
-
// src/foundations/surfaces.ts
|
|
530
|
-
var surfaces = {
|
|
531
|
-
slug: "surfaces",
|
|
532
|
-
title: "Surfaces & elevation",
|
|
533
|
-
summary: "Depth is a step up the surface ladder, chosen by how transient a surface is. Dark mode lifts with lighter tones and edge light; light mode adds soft shadows.",
|
|
534
|
-
headline: "4 surface tokens \xB7 5 elevation roles \xB7 1 shadow per surface \xB7 1 layer scale",
|
|
535
|
-
vocabularies: [
|
|
536
|
-
{
|
|
537
|
-
title: "The surface ladder",
|
|
538
|
-
terms: [
|
|
539
|
-
{ name: "bg-background", value: "the page", use: "The document body; the furthest-back surface." },
|
|
540
|
-
{ name: "bg-card", value: "+1 step", use: "Resting containers: cards, panels, dialogs." },
|
|
541
|
-
{ name: "bg-muted", value: "+1 step, quieter", use: "Wells, code blocks, inactive fills inside a card." },
|
|
542
|
-
{ name: "bg-popover", value: "+2 steps", use: "Anything that floats: menus, popovers, selects, tooltips. Always distinct from card, so a menu in a dialog never melts into it." },
|
|
543
|
-
{ name: "bg-overlay", value: "scrim", use: "Behind modals, and pills over imagery." }
|
|
544
|
-
]
|
|
545
|
-
},
|
|
546
|
-
{
|
|
547
|
-
title: "Elevation by role",
|
|
548
|
-
note: "Raise by transience \u2014 how much a surface floats over content \u2014 not by importance.",
|
|
549
|
-
terms: [
|
|
550
|
-
{ name: "Resting", value: "no shadow", use: "Page content and flat cards. A hairline border if it needs an edge." },
|
|
551
|
-
{ name: "Raised shadow-sm", value: "subtle", use: "A draggable card, a sticky header over scrolled content." },
|
|
552
|
-
{ name: "Floating shadow-md", value: "anchored", use: "Menus, popovers, selects, dropdowns." },
|
|
553
|
-
{ name: "Modal shadow-lg", value: "with scrim", use: "Dialogs, sheets, the command menu." },
|
|
554
|
-
{ name: "Top shadow-xl", value: "rare", use: "The toast stack." }
|
|
555
|
-
]
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
title: "Layers",
|
|
559
|
-
terms: [
|
|
560
|
-
{ name: "z-(--z-sticky)", value: "30", use: "Sticky in-content chrome: table headers." },
|
|
561
|
-
{ name: "z-(--z-nav)", value: "40", use: "App chrome: header, sidebar, bottom nav." },
|
|
562
|
-
{ name: "z-(--z-overlay)", value: "50", use: "Every portaled transient surface." },
|
|
563
|
-
{ name: "z-(--z-toast)", value: "60", use: "Toasts, above modals." },
|
|
564
|
-
{ name: "z-(--z-skip-link)", value: "100", use: "The skip link, above everything." }
|
|
565
|
-
]
|
|
566
|
-
}
|
|
567
|
-
],
|
|
568
|
-
rules: [
|
|
569
|
-
{
|
|
570
|
-
rule: "Pick the elevation role, not a shadow value.",
|
|
571
|
-
why: "Elevation is a five-step system. A custom box-shadow cannot follow a theme or the light/dark split."
|
|
572
|
-
},
|
|
573
|
-
{
|
|
574
|
-
rule: "Floating surfaces use bg-popover, resting ones bg-card. Anything with shadow-md or above floats: menus, popovers, selects, tooltips, dialogs, sheets, toasts, a floating panel.",
|
|
575
|
-
why: "In dark mode popover is guaranteed lighter than card, so a menu opened inside a dialog always reads as on top of it. In light mode several presets make background, card and popover the same colour, so the fill alone never carries elevation.",
|
|
576
|
-
enforcedBy: "themes.test.ts + sable-lint: floating-surface"
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
rule: "A floating surface's edge is border-border-floating (or ring-border-floating), never the full border-border. The shadow carries the elevation.",
|
|
580
|
-
why: "A full hairline plus a shadow stacks into a heavy dark outline. border-floating is a faint alpha edge: it defines the shape against a same-coloured page in light mode and reads as a lit edge in dark mode, where shadows barely show.",
|
|
581
|
-
enforcedBy: "sable-lint: floating-surface"
|
|
582
|
-
},
|
|
583
|
-
{
|
|
584
|
-
rule: "A recessed area inside a surface (a segmented-control track, a pad, a preview well) is sable-well, and it takes no outline. Whatever sits on the well takes the surface's own colour back (bg-popover inside a popover).",
|
|
585
|
-
why: "sable-well mixes a fixed amount of ink into the surface it sits in, so it is the same visible step on the page, in a card or in a floating panel, in every theme and mode. A fixed bg-muted can nearly vanish inside a popover in dark mode, and adding an outline to every well to compensate stacks a panel into boxes."
|
|
586
|
-
},
|
|
587
|
-
{
|
|
588
|
-
rule: "One shadow per surface; never a heavy shadow with a heavy border.",
|
|
589
|
-
why: "Stacked depth cues read as decoration rather than position."
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
rule: "Do not elevate a static section to make it stand out.",
|
|
593
|
-
why: "Elevation means 'this floats'. Use a tonal step or space for emphasis."
|
|
594
|
-
},
|
|
595
|
-
{
|
|
596
|
-
rule: "For modals, the scrim is the primary depth cue.",
|
|
597
|
-
why: "The scrim is what tells the user the page behind is inert; the shadow is secondary."
|
|
598
|
-
},
|
|
599
|
-
{
|
|
600
|
-
rule: "Stack with the named layer scale.",
|
|
601
|
-
why: "Overlays portal to the end of body and order themselves by open order; the layer only has to clear in-flow chrome."
|
|
602
|
-
}
|
|
603
|
-
],
|
|
604
|
-
noGos: [
|
|
605
|
-
{
|
|
606
|
-
never: "z-[9999] or any raw z-index.",
|
|
607
|
-
instead: "z-(--z-overlay) and the other named layers.",
|
|
608
|
-
why: "Raw z-indexes become a bidding war that a future overlay always loses.",
|
|
609
|
-
enforcedBy: "sable-lint: raw-z-index"
|
|
610
|
-
},
|
|
611
|
-
{
|
|
612
|
-
never: "shadow-[0_8px_30px_rgba(0,0,0,.2)] or any custom shadow.",
|
|
613
|
-
instead: "shadow-sm \u2026 shadow-xl by role.",
|
|
614
|
-
why: "Custom shadows ignore the theme's light/dark elevation recipes."
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
never: "bg-card on a popover, or bg-popover on a resting card.",
|
|
618
|
-
instead: "Match the token to the role.",
|
|
619
|
-
why: "Swapping them is how floating surfaces melt into the surface beneath."
|
|
620
|
-
}
|
|
621
|
-
],
|
|
622
|
-
details: [
|
|
623
|
-
{
|
|
624
|
-
title: "A menu inside a dialog",
|
|
625
|
-
before: "Dialog bg-card \xB7 Menu bg-card \u2014 identical in dark mode",
|
|
626
|
-
after: "Dialog bg-card \xB7 Menu bg-popover \u2014 one step lighter, plus edge light",
|
|
627
|
-
why: "The menu reads as above the dialog without any prop."
|
|
628
|
-
}
|
|
629
|
-
],
|
|
630
|
-
related: ["shape", "sable docs token-intent"]
|
|
631
|
-
};
|
|
632
|
-
var surfaces_default = surfaces;
|
|
633
|
-
|
|
634
|
-
// src/foundations/typography.ts
|
|
635
|
-
var typography = {
|
|
636
|
-
slug: "typography",
|
|
637
|
-
title: "Typography",
|
|
638
|
-
summary: "Type is a closed vocabulary of paired utilities. Each one bundles size, weight, line-height and tracking, and is picked by role rather than by size.",
|
|
639
|
-
headline: "6 families \xB7 8 everyday tiers \xB7 2 text colors \xB7 1 utility per text node",
|
|
640
|
-
vocabularies: [
|
|
641
|
-
{
|
|
642
|
-
title: "The everyday set",
|
|
643
|
-
note: "Product UI is built almost entirely from these eight. Reach outside them on purpose.",
|
|
644
|
-
terms: [
|
|
645
|
-
{ name: "text-heading-1", value: "36px \xB7 600", use: "The page title. Exactly one per route." },
|
|
646
|
-
{ name: "text-heading-2", value: "28px \xB7 600", use: "A major section of the page." },
|
|
647
|
-
{ name: "text-title-lg", value: "16px \xB7 600", use: "Names a component: card, dialog, sheet, list row." },
|
|
648
|
-
{ name: "text-title-sm", value: "14px \xB7 600", use: "Names a dense component: compact card, table section." },
|
|
649
|
-
{ name: "text-body-md", value: "15px \xB7 400", use: "The default paragraph, description and help text." },
|
|
650
|
-
{ name: "text-body-sm", value: "13px \xB7 400", use: "Secondary copy and table cells." },
|
|
651
|
-
{ name: "text-label-lg", value: "14px \xB7 600", use: "Buttons, field labels, tabs." },
|
|
652
|
-
{ name: "text-caption", value: "12px \xB7 400", use: "Timestamps, footnotes, meta rows." }
|
|
653
|
-
]
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
title: "The six families",
|
|
657
|
-
note: "Every tier belongs to one family, and the family is the decision. Size follows from it.",
|
|
658
|
-
terms: [
|
|
659
|
-
{ name: "Display display-lg\u20264xl", value: "48\u2013120px \xB7 700\u2013800", use: "Type as image: landing heroes, campaign numbers. Never in product chrome; never two per view." },
|
|
660
|
-
{ name: "Heading heading-1\u20264", value: "36 / 28 / 22 / 18px \xB7 600", use: "The document outline (h1\u2013h4). Keep the order; never skip a level to get a size." },
|
|
661
|
-
{ name: "Title title-lg \xB7 title-sm", value: "16 / 14px \xB7 600", use: "Names a UI part without claiming a document heading." },
|
|
662
|
-
{ name: "Body body-lg/md/sm/xs", value: "17 / 15 / 13 / 12px \xB7 400", use: "Reading text. -lg is the lede (carries text-wrap: pretty); -xs is a dense-UI floor, not a prose size." },
|
|
663
|
-
{ name: "Label label-lg/md/sm \xB7 caption", value: "14 / 12 / 11px \xB7 600; caption 12 \xB7 400", use: "Functional, scannable text of one to three words. Caption is also the group-label tier: nav sections, menu groups." },
|
|
664
|
-
{ name: "Mono mono-xl/lg/md/caption/sm", value: "28 / 20 / 13 / 12 / 11px \xB7 tabular", use: "Numbers that are the content, IDs, hashes, code, aligned figures." }
|
|
665
|
-
]
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
title: "Text color",
|
|
669
|
-
terms: [
|
|
670
|
-
{ name: "text-foreground", value: "full contrast", use: "Primary text: headings, body, values." },
|
|
671
|
-
{ name: "text-muted-foreground", value: "secondary contrast (AA on every surface)", use: "Supporting text: descriptions, captions, meta, placeholders." }
|
|
672
|
-
]
|
|
673
|
-
}
|
|
674
|
-
],
|
|
675
|
-
rules: [
|
|
676
|
-
{
|
|
677
|
-
rule: "One paired utility per text node. Compose it with a color and margin only.",
|
|
678
|
-
why: "Each utility is a tuned set of size, weight, leading and tracking. Splitting it into text-* + font-* + tracking-* breaks the tuning and every theme override of it.",
|
|
679
|
-
enforcedBy: "sable-lint: decomposed-type"
|
|
680
|
-
},
|
|
681
|
-
{
|
|
682
|
-
rule: "Pick the tier by role, not by the size it renders at.",
|
|
683
|
-
why: "A card title is title-lg even when body-lg is the same size: role fixes the weight (600 vs 400) and the rhythm around it."
|
|
684
|
-
},
|
|
685
|
-
{
|
|
686
|
-
rule: "Two text colors: foreground and muted-foreground.",
|
|
687
|
-
why: "Both are contrast-tested against every surface in every theme. A third grey, or an opacity step, is untested contrast."
|
|
688
|
-
},
|
|
689
|
-
{
|
|
690
|
-
rule: "One heading-1 per route; headings descend without skipping levels.",
|
|
691
|
-
why: "The heading outline is how screen-reader users navigate a page. Restyle with a tier rather than jumping a level for its size."
|
|
692
|
-
},
|
|
693
|
-
{
|
|
694
|
-
rule: "Hierarchy comes from a clear jump (heading \u2192 title \u2192 body), then weight and space, before size.",
|
|
695
|
-
why: "Two sizes one step apart read as the same level. Space and weight separate levels more cheaply than another size."
|
|
696
|
-
},
|
|
697
|
-
{
|
|
698
|
-
rule: "Cap reading text at --measure-prose (65ch).",
|
|
699
|
-
why: "Past roughly 75 characters the eye loses the line return. A wide region can still hold a 65ch column."
|
|
700
|
-
},
|
|
701
|
-
{
|
|
702
|
-
rule: "Numbers that must align use a mono tier (tabular figures).",
|
|
703
|
-
why: "Proportional digits change width as values update, so totals and columns shimmy."
|
|
704
|
-
},
|
|
705
|
-
{
|
|
706
|
-
rule: "text-wrap: balance on headings, pretty on ledes, neither on long prose.",
|
|
707
|
-
why: "balance evens short headlines; pretty only refuses a one-word last line. Both cost layout work that long prose does not repay."
|
|
708
|
-
},
|
|
709
|
-
{
|
|
710
|
-
rule: "A mark attached to a line of type (a dot, a bullet, a timeline marker, a leading icon) sits in a box one line tall (h-[1lh] items-center) and is sized in em.",
|
|
711
|
-
why: "1lh is the line-height of the text beside it, so the mark centres on that line in every tier, density and theme. A px size with a mt-[3px] nudge is right for one font at one size and wrong everywhere else."
|
|
712
|
-
}
|
|
713
|
-
],
|
|
714
|
-
noGos: [
|
|
715
|
-
{
|
|
716
|
-
never: "Opacity on text (text-foreground/60, opacity-70 on a paragraph).",
|
|
717
|
-
instead: "text-muted-foreground.",
|
|
718
|
-
why: "An alpha step over an unknown surface has unknown contrast, and it shifts per theme.",
|
|
719
|
-
enforcedBy: "sable-lint: text-opacity"
|
|
720
|
-
},
|
|
721
|
-
{
|
|
722
|
-
never: "A hand-picked size, weight or tracking (text-[15px], font-medium, tracking-wide).",
|
|
723
|
-
instead: "The paired utility for the role.",
|
|
724
|
-
why: "Off-vocabulary type cannot be retuned by a theme or the customizer, so it drifts the moment either changes.",
|
|
725
|
-
enforcedBy: "sable-lint: decomposed-type"
|
|
726
|
-
},
|
|
727
|
-
{
|
|
728
|
-
never: "Uppercase in product chrome: nav and menu group labels, page eyebrows, table headers, section titles.",
|
|
729
|
-
instead: "Sentence-case text-caption or text-label-md in muted-foreground; hierarchy from size, weight and space.",
|
|
730
|
-
why: "Tracked capitals spread across a UI read as shouting and slow word recognition. The eyebrow tier (label-xs / overline) is an editorial accent: at most one per marketing view."
|
|
731
|
-
},
|
|
732
|
-
{
|
|
733
|
-
never: "Display tiers inside product UI, or two display sizes in one view.",
|
|
734
|
-
instead: "heading-1 for a page title; mono-xl for a metric.",
|
|
735
|
-
why: "Display is type as image. In chrome it competes with the content it is supposed to frame."
|
|
736
|
-
},
|
|
737
|
-
{
|
|
738
|
-
never: "Justified text, or reading copy below body-sm.",
|
|
739
|
-
instead: "Ragged-right at body-md; body-xs only for dense data.",
|
|
740
|
-
why: "Justification opens rivers on the web, which has no hyphenation control; 12px prose fails people with low vision."
|
|
741
|
-
},
|
|
742
|
-
{
|
|
743
|
-
never: "A new font family, or raw font-feature-settings / font-variation-settings.",
|
|
744
|
-
instead: "The theme's sans, mono and optional display; font-variant-numeric and font-weight.",
|
|
745
|
-
why: "Raw axis tags silently do nothing on a fallback font, and every theme declares a fallback stack."
|
|
746
|
-
}
|
|
747
|
-
],
|
|
748
|
-
details: [
|
|
749
|
-
{
|
|
750
|
-
title: "Steady numbers",
|
|
751
|
-
before: '<span className="text-body-md">{price}</span>',
|
|
752
|
-
after: '<span className="text-mono-md">{price}</span>',
|
|
753
|
-
why: "Mono tiers set tabular-nums, so a ticking value stops nudging its neighbors sideways."
|
|
754
|
-
},
|
|
755
|
-
{
|
|
756
|
-
title: "Optically centred labels",
|
|
757
|
-
before: '<button className="h-(--control-md) px-4 items-center">Save</button>',
|
|
758
|
-
after: "<Button>Save</Button> {/* label wrapped in .sable-label-trim */}",
|
|
759
|
-
why: "text-box: trim-both cap alphabetic centres the cap-height band the eye reads, not the line box. Measured: Helvetica otherwise sits a full 1px high on a 40px control."
|
|
760
|
-
},
|
|
761
|
-
{
|
|
762
|
-
title: "A marker centred on its line",
|
|
763
|
-
before: '<span className="mt-[5px] size-2 rounded-full bg-primary" />',
|
|
764
|
-
after: '<span className="flex h-[1lh] items-center"><span className="size-[0.5em] rounded-full bg-primary" /></span>',
|
|
765
|
-
why: 'The wrapper is exactly one line of the adjacent text tall, so the dot centres on the first line whatever the tier; em sizing keeps it in proportion when the type scales. Stepper indicator="dot" is built this way.'
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
title: "Balanced headings",
|
|
769
|
-
before: '<h2 className="text-heading-2">Ship faster with fewer review rounds</h2>',
|
|
770
|
-
after: '<h2 className="text-heading-2 text-balance">Ship faster with fewer review rounds</h2>',
|
|
771
|
-
why: "Evens line lengths so a two-line heading does not end on a single orphaned word."
|
|
772
|
-
},
|
|
773
|
-
{
|
|
774
|
-
title: "Secondary text without opacity",
|
|
775
|
-
before: '<p className="text-foreground/60">Updated 2m ago</p>',
|
|
776
|
-
after: '<p className="text-caption text-muted-foreground">Updated 2m ago</p>',
|
|
777
|
-
why: "muted-foreground is contrast-tested on every surface; a 60% alpha is not."
|
|
778
|
-
}
|
|
779
|
-
],
|
|
780
|
-
related: ["spacing", "accessibility", "sable token typography", "sable token typographyOverrides"]
|
|
781
|
-
};
|
|
782
|
-
var typography_default = typography;
|
|
783
|
-
|
|
784
1
|
// src/foundations/index.ts
|
|
2
|
+
import accessibility from "./accessibility.js";
|
|
3
|
+
import motion from "./motion.js";
|
|
4
|
+
import scrolling from "./scrolling.js";
|
|
5
|
+
import shape from "./shape.js";
|
|
6
|
+
import spacing from "./spacing.js";
|
|
7
|
+
import surfaces from "./surfaces.js";
|
|
8
|
+
import typography from "./typography.js";
|
|
785
9
|
var FOUNDATIONS = [
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
10
|
+
typography,
|
|
11
|
+
spacing,
|
|
12
|
+
shape,
|
|
13
|
+
surfaces,
|
|
14
|
+
motion,
|
|
15
|
+
scrolling,
|
|
16
|
+
accessibility
|
|
793
17
|
];
|
|
794
18
|
var FOUNDATIONS_BY_SLUG = Object.fromEntries(
|
|
795
19
|
FOUNDATIONS.map((foundation) => [foundation.slug, foundation])
|
|
796
20
|
);
|
|
797
|
-
|
|
798
|
-
|
|
21
|
+
export {
|
|
22
|
+
FOUNDATIONS,
|
|
23
|
+
FOUNDATIONS_BY_SLUG,
|
|
24
|
+
accessibility,
|
|
25
|
+
motion,
|
|
26
|
+
scrolling,
|
|
27
|
+
shape,
|
|
28
|
+
spacing,
|
|
29
|
+
surfaces,
|
|
30
|
+
typography
|
|
31
|
+
};
|