@crewlethq/ui 0.2.0 → 0.4.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/README.md +1714 -14
- package/dist/Accordion/Accordion.d.ts +54 -0
- package/dist/Accordion/index.d.ts +4 -0
- package/dist/Accordion/index.js +6 -0
- package/dist/Accordion-ZI2WLD5C.css +87 -0
- package/dist/AddPill/AddPill.d.ts +114 -0
- package/dist/AddPill/index.d.ts +3 -0
- package/dist/AddPill/index.js +3 -0
- package/dist/AddPill-DFWXYLIQ.css +434 -0
- package/dist/Announcer/Announcer.d.ts +15 -0
- package/dist/Announcer/index.d.ts +2 -0
- package/dist/Announcer/index.js +3 -0
- package/dist/AppShell/AppShell.d.ts +193 -0
- package/dist/AppShell/index.d.ts +3 -0
- package/dist/AppShell/index.js +6 -0
- package/dist/AppShell-NVE6LHNI.css +395 -0
- package/dist/Avatar/Avatar.d.ts +104 -0
- package/dist/Avatar/index.d.ts +3 -0
- package/dist/Avatar/index.js +2 -0
- package/dist/Avatar-P2KRQM2B.css +156 -0
- package/dist/BrandLockup/BrandLockup.d.ts +41 -0
- package/dist/BrandLockup/index.d.ts +3 -0
- package/dist/BrandLockup/index.js +2 -0
- package/dist/BrandLockup-6ISDEHPJ.css +108 -0
- package/dist/Button/Button.d.ts +88 -0
- package/dist/Button/index.d.ts +3 -0
- package/dist/Button/index.js +3 -0
- package/dist/Button-D4J6B4MA.css +302 -0
- package/dist/CalendarGrid/CalendarGrid.d.ts +87 -0
- package/dist/CalendarGrid/index.d.ts +3 -0
- package/dist/CalendarGrid/index.js +2 -0
- package/dist/CalendarGrid-V6TQ5QKS.css +128 -0
- package/dist/Callout/Callout.d.ts +47 -0
- package/dist/Callout/index.d.ts +3 -0
- package/dist/Callout/index.js +2 -0
- package/dist/Callout-S7QDWHDL.css +111 -0
- package/dist/Canvas/Canvas.d.ts +234 -0
- package/dist/Canvas/geometry.d.ts +249 -0
- package/dist/Canvas/index.d.ts +5 -0
- package/dist/Canvas/index.js +8 -0
- package/dist/Canvas/layerNode.d.ts +20 -0
- package/dist/Canvas-Q6JETCWR.css +315 -0
- package/dist/Card/Card.d.ts +164 -0
- package/dist/Card/headerSlot.d.ts +65 -0
- package/dist/Card/index.d.ts +5 -0
- package/dist/Card/index.js +6 -0
- package/dist/Card-RLR254LD.css +450 -0
- package/dist/Charts/ActivityStrip.d.ts +45 -0
- package/dist/Charts/BarList.d.ts +51 -0
- package/dist/Charts/Legend.d.ts +32 -0
- package/dist/Charts/StackedBar.d.ts +38 -0
- package/dist/Charts/TimeSeries.d.ts +92 -0
- package/dist/Charts/dataColor.d.ts +20 -0
- package/dist/Charts/index.d.ts +12 -0
- package/dist/Charts/index.js +3 -0
- package/dist/Charts-D7G2BSXH.css +277 -0
- package/dist/Checkbox/Checkbox.d.ts +45 -0
- package/dist/Checkbox/index.d.ts +3 -0
- package/dist/Checkbox/index.js +2 -0
- package/dist/Checkbox-VGW26YLQ.css +111 -0
- package/dist/CodeBlock/CodeBlock.d.ts +119 -0
- package/dist/CodeBlock/index.d.ts +3 -0
- package/dist/CodeBlock/index.js +7 -0
- package/dist/CodeBlock-GYNJ2HI6.css +188 -0
- package/dist/Combobox/Combobox.d.ts +89 -0
- package/dist/Combobox/index.d.ts +3 -0
- package/dist/Combobox/index.js +8 -0
- package/dist/Combobox-4FUVYIE7.css +80 -0
- package/dist/CommandPalette/CommandPalette.d.ts +77 -0
- package/dist/CommandPalette/index.d.ts +3 -0
- package/dist/CommandPalette/index.js +11 -0
- package/dist/CommandPalette-CSJDUCQA.css +115 -0
- package/dist/Container/Container.d.ts +17 -0
- package/dist/Container/index.d.ts +3 -0
- package/dist/Container/index.js +2 -0
- package/dist/Container-3TSKMGNI.css +52 -0
- package/dist/Copyable/CopyButton.d.ts +64 -0
- package/dist/Copyable/Copyable.d.ts +42 -0
- package/dist/Copyable/index.d.ts +5 -0
- package/dist/Copyable/index.js +6 -0
- package/dist/Copyable-5PNRFOLS.css +125 -0
- package/dist/Count/Count.d.ts +24 -0
- package/dist/Count/index.d.ts +3 -0
- package/dist/Count/index.js +3 -0
- package/dist/Count-ZVCJ6VPD.css +20 -0
- package/dist/DataTable/CopyableCell.d.ts +12 -0
- package/dist/DataTable/DataTable.d.ts +615 -0
- package/dist/DataTable/RowActionsMenu.d.ts +7 -0
- package/dist/DataTable/index.d.ts +7 -0
- package/dist/DataTable/index.js +14 -0
- package/dist/DataTable-ITNACQ2X.css +2020 -0
- package/dist/DataView/DataView.d.ts +109 -0
- package/dist/DataView/DataViewToolbar.d.ts +87 -0
- package/dist/DataView/FilterAxisBar.d.ts +103 -0
- package/dist/DataView/columns.d.ts +35 -0
- package/dist/DataView/filters.d.ts +184 -0
- package/dist/DataView/index.d.ts +11 -0
- package/dist/DataView/index.js +25 -0
- package/dist/DataView-256YF32J.css +441 -0
- package/dist/DateTimePicker/DateTimePicker.d.ts +91 -0
- package/dist/DateTimePicker/index.d.ts +3 -0
- package/dist/DateTimePicker/index.js +9 -0
- package/dist/DateTimePicker-22VNXRWC.css +199 -0
- package/dist/DescriptionList/DescriptionList.d.ts +36 -0
- package/dist/DescriptionList/index.d.ts +3 -0
- package/dist/DescriptionList/index.js +2 -0
- package/dist/DescriptionList-YYY3V3O6.css +59 -0
- package/dist/DiffList/DiffList.d.ts +47 -0
- package/dist/DiffList/index.d.ts +3 -0
- package/dist/DiffList/index.js +3 -0
- package/dist/DiffList-3EX64AAE.css +94 -0
- package/dist/Disclosure/Disclosure.d.ts +56 -0
- package/dist/Disclosure/anatomy.d.ts +43 -0
- package/dist/Disclosure/index.d.ts +5 -0
- package/dist/Disclosure/index.js +6 -0
- package/dist/Disclosure-VKBJJIZ7.css +176 -0
- package/dist/EmptyState/EmptyState.d.ts +39 -0
- package/dist/EmptyState/index.d.ts +3 -0
- package/dist/EmptyState/index.js +3 -0
- package/dist/EmptyState-C6HRBGT7.css +73 -0
- package/dist/EmptyValue/EmptyValue.d.ts +31 -0
- package/dist/EmptyValue/index.d.ts +3 -0
- package/dist/EmptyValue/index.js +3 -0
- package/dist/EmptyValue-4B6ODXHT.css +12 -0
- package/dist/EntityChip/EntityChip.d.ts +40 -0
- package/dist/EntityChip/index.d.ts +3 -0
- package/dist/EntityChip/index.js +3 -0
- package/dist/EntityChip-LUCPZBBG.css +64 -0
- package/dist/ErrorBoundary/ErrorBoundary.d.ts +58 -0
- package/dist/ErrorBoundary/index.d.ts +3 -0
- package/dist/ErrorBoundary/index.js +4 -0
- package/dist/ErrorBoundary-ME36CQTA.css +59 -0
- package/dist/Eyebrow/Eyebrow.d.ts +18 -0
- package/dist/Eyebrow/index.d.ts +3 -0
- package/dist/Eyebrow/index.js +2 -0
- package/dist/Eyebrow-5NREGHQH.css +46 -0
- package/dist/FilterChip/FilterChip.d.ts +100 -0
- package/dist/FilterChip/index.d.ts +3 -0
- package/dist/FilterChip/index.js +3 -0
- package/dist/FilterChip-GTGLLOXE.css +107 -0
- package/dist/Fullscreen/index.d.ts +2 -0
- package/dist/Fullscreen/index.js +1 -0
- package/dist/Fullscreen/useFullscreen.d.ts +32 -0
- package/dist/IconButton/IconButton.d.ts +45 -0
- package/dist/IconButton/index.d.ts +3 -0
- package/dist/IconButton/index.js +3 -0
- package/dist/IconButton-VQGNMJDI.css +158 -0
- package/dist/ImageUpload/ImageUpload.d.ts +37 -0
- package/dist/ImageUpload/index.d.ts +3 -0
- package/dist/ImageUpload/index.js +3 -0
- package/dist/ImageUpload-HKFIZR6D.css +193 -0
- package/dist/InlineCode/InlineCode.d.ts +37 -0
- package/dist/InlineCode/index.d.ts +3 -0
- package/dist/InlineCode/index.js +2 -0
- package/dist/InlineCode-6WSR5COL.css +65 -0
- package/dist/Input/FormField.d.ts +61 -0
- package/dist/Input/FormRow.d.ts +6 -0
- package/dist/Input/FormSection.d.ts +28 -0
- package/dist/Input/Input.d.ts +98 -0
- package/dist/Input/Label.d.ts +31 -0
- package/dist/Input/ReadOnlyField.d.ts +30 -0
- package/dist/Input/Textarea.d.ts +16 -0
- package/dist/Input/index.d.ts +15 -0
- package/dist/Input/index.js +5 -0
- package/dist/Input-2PU5CPKK.css +616 -0
- package/dist/Kbd/Kbd.d.ts +40 -0
- package/dist/Kbd/index.d.ts +3 -0
- package/dist/Kbd/index.js +3 -0
- package/dist/Kbd-W66MXQI5.css +61 -0
- package/dist/Layer/LayerHost.d.ts +23 -0
- package/dist/Layer/index.d.ts +7 -0
- package/dist/Layer/index.js +3 -0
- package/dist/Layer/place.d.ts +63 -0
- package/dist/Layer/stack.d.ts +159 -0
- package/dist/Layer-NAFSGCFA.css +23 -0
- package/dist/Layout/Layout.d.ts +104 -0
- package/dist/Layout/index.d.ts +3 -0
- package/dist/Layout/index.js +2 -0
- package/dist/Layout-2ZCV3IGF.css +171 -0
- package/dist/Link/Link.d.ts +45 -0
- package/dist/Link/index.d.ts +3 -0
- package/dist/Link/index.js +3 -0
- package/dist/Link-6BEFNMN2.css +80 -0
- package/dist/List/List.d.ts +73 -0
- package/dist/List/index.d.ts +3 -0
- package/dist/List/index.js +3 -0
- package/dist/List-XTHESXLR.css +161 -0
- package/dist/ListInput/ListInput.d.ts +68 -0
- package/dist/ListInput/index.d.ts +3 -0
- package/dist/ListInput/index.js +10 -0
- package/dist/ListInput-O7LJKOIG.css +147 -0
- package/dist/Listbox/index.d.ts +5 -0
- package/dist/Listbox/index.js +2 -0
- package/dist/Listbox/useListbox.d.ts +97 -0
- package/dist/Listbox/useOptionKeys.d.ts +64 -0
- package/dist/Listbox-X2DOTSQA.css +110 -0
- package/dist/Menu/Menu.d.ts +118 -0
- package/dist/Menu/index.d.ts +3 -0
- package/dist/Menu/index.js +7 -0
- package/dist/Menu-6DE7I66I.css +222 -0
- package/dist/Meter/Meter.d.ts +126 -0
- package/dist/Meter/index.d.ts +3 -0
- package/dist/Meter/index.js +3 -0
- package/dist/Meter-SKOOXQBC.css +87 -0
- package/dist/Modal/ConfirmModal.d.ts +74 -0
- package/dist/Modal/Modal.d.ts +154 -0
- package/dist/Modal/index.d.ts +5 -0
- package/dist/Modal/index.js +8 -0
- package/dist/Modal-XYQJ4BL6.css +484 -0
- package/dist/NewItemsNotice/NewItemsNotice.d.ts +28 -0
- package/dist/NewItemsNotice/index.d.ts +3 -0
- package/dist/NewItemsNotice/index.js +4 -0
- package/dist/NewItemsNotice-T4DSSPVV.css +60 -0
- package/dist/OrgLabel/OrgLabel.d.ts +117 -0
- package/dist/OrgLabel/index.d.ts +3 -0
- package/dist/OrgLabel/index.js +2 -0
- package/dist/OrgLabel-D7U6QJZD.css +293 -0
- package/dist/OrgNode/OrgNode.d.ts +103 -0
- package/dist/OrgNode/index.d.ts +3 -0
- package/dist/OrgNode/index.js +3 -0
- package/dist/OrgNode-BT7T2Q2X.css +214 -0
- package/dist/OrgTable/OrgTable.d.ts +113 -0
- package/dist/OrgTable/index.d.ts +3 -0
- package/dist/OrgTable/index.js +11 -0
- package/dist/OrgTable-VWMFC3TB.css +360 -0
- package/dist/PageHeader/PageHeader.d.ts +35 -0
- package/dist/PageHeader/index.d.ts +3 -0
- package/dist/PageHeader/index.js +3 -0
- package/dist/PageHeader-6EYWQXRE.css +88 -0
- package/dist/Popover/Popover.d.ts +91 -0
- package/dist/Popover/index.d.ts +3 -0
- package/dist/Popover/index.js +4 -0
- package/dist/Popover-WJGCF3ZQ.css +185 -0
- package/dist/PricingCard/PricingCard.d.ts +56 -0
- package/dist/PricingCard/index.d.ts +3 -0
- package/dist/PricingCard/index.js +4 -0
- package/dist/PricingCard-JFI7RYXP.css +278 -0
- package/dist/Prose/Prose.d.ts +33 -0
- package/dist/Prose/index.d.ts +3 -0
- package/dist/Prose/index.js +2 -0
- package/dist/Prose-BTXNFT2Q.css +99 -0
- package/dist/RelativeTime/RelativeTime.d.ts +31 -0
- package/dist/RelativeTime/clock.d.ts +9 -0
- package/dist/RelativeTime/format.d.ts +38 -0
- package/dist/RelativeTime/index.d.ts +6 -0
- package/dist/RelativeTime/index.js +4 -0
- package/dist/RelativeTime-EBAQPGO2.css +10 -0
- package/dist/SearchTrigger/SearchTrigger.d.ts +29 -0
- package/dist/SearchTrigger/index.d.ts +3 -0
- package/dist/SearchTrigger/index.js +2 -0
- package/dist/SearchTrigger-QXOYAUUY.css +79 -0
- package/dist/Section/Section.d.ts +41 -0
- package/dist/Section/index.d.ts +3 -0
- package/dist/Section/index.js +3 -0
- package/dist/Section-QRUPRIQ2.css +127 -0
- package/dist/SegmentedControl/SegmentedControl.d.ts +101 -0
- package/dist/SegmentedControl/index.d.ts +4 -0
- package/dist/SegmentedControl/index.js +5 -0
- package/dist/SegmentedControl-X3BI3XU4.css +68 -0
- package/dist/Select/Select.d.ts +153 -0
- package/dist/Select/index.d.ts +3 -0
- package/dist/Select/index.js +5 -0
- package/dist/Select-2X77FSWR.css +350 -0
- package/dist/Shortcut/index.d.ts +2 -0
- package/dist/Shortcut/index.js +4 -0
- package/dist/Shortcut/useShortcut.d.ts +68 -0
- package/dist/SidebarNav/SidebarNav.d.ts +117 -0
- package/dist/SidebarNav/index.d.ts +3 -0
- package/dist/SidebarNav/index.js +3 -0
- package/dist/SidebarNav-QGSFKZYA.css +319 -0
- package/dist/Skeleton/Skeleton.d.ts +50 -0
- package/dist/Skeleton/index.d.ts +3 -0
- package/dist/Skeleton/index.js +3 -0
- package/dist/Skeleton-V735HRGM.css +421 -0
- package/dist/StatCard/StatCard.d.ts +56 -0
- package/dist/StatCard/index.d.ts +3 -0
- package/dist/StatCard/index.js +3 -0
- package/dist/StatCard-KYPKZUWX.css +111 -0
- package/dist/StatGroup/StatGroup.d.ts +20 -0
- package/dist/StatGroup/index.d.ts +3 -0
- package/dist/StatGroup/index.js +2 -0
- package/dist/StatGroup-TGB6AEKM.css +80 -0
- package/dist/StatusDot/StatusDot.d.ts +25 -0
- package/dist/StatusDot/index.d.ts +3 -0
- package/dist/StatusDot/index.js +2 -0
- package/dist/StatusDot-HA2L2FSX.css +84 -0
- package/dist/Switch/Switch.d.ts +38 -0
- package/dist/Switch/index.d.ts +3 -0
- package/dist/Switch/index.js +2 -0
- package/dist/Switch-U4OWK4CL.css +169 -0
- package/dist/Table/Table.d.ts +51 -0
- package/dist/Table/index.d.ts +3 -0
- package/dist/Table/index.js +3 -0
- package/dist/Table-MDOLVUAW.css +159 -0
- package/dist/Tabs/Tabs.d.ts +97 -0
- package/dist/Tabs/index.d.ts +3 -0
- package/dist/Tabs/index.js +4 -0
- package/dist/Tabs/roving.d.ts +67 -0
- package/dist/Tabs-VLICJFVB.css +275 -0
- package/dist/Tag/Tag.d.ts +107 -0
- package/dist/Tag/index.d.ts +3 -0
- package/dist/Tag/index.js +3 -0
- package/dist/Tag-VNZEYLB6.css +406 -0
- package/dist/TagGroup/TagGroup.d.ts +41 -0
- package/dist/TagGroup/index.d.ts +3 -0
- package/dist/TagGroup/index.js +4 -0
- package/dist/TagGroup-APC2EFH4.css +75 -0
- package/dist/TagsInput/TagsInput.d.ts +112 -0
- package/dist/TagsInput/index.d.ts +3 -0
- package/dist/TagsInput/index.js +10 -0
- package/dist/TagsInput-W4P36PRJ.css +168 -0
- package/dist/Text/Text.d.ts +67 -0
- package/dist/Text/index.d.ts +3 -0
- package/dist/Text/index.js +2 -0
- package/dist/Text-XIPYYZX2.css +154 -0
- package/dist/ThemeSwitcher/ThemeSwitcher.d.ts +47 -0
- package/dist/ThemeSwitcher/index.d.ts +4 -0
- package/dist/ThemeSwitcher/index.js +6 -0
- package/dist/ThemeSwitcher/preferences.d.ts +51 -0
- package/dist/TimeWindowPicker/TimeWindowPicker.d.ts +231 -0
- package/dist/TimeWindowPicker/index.d.ts +3 -0
- package/dist/TimeWindowPicker/index.js +13 -0
- package/dist/TimeWindowPicker/mask.d.ts +82 -0
- package/dist/TimeWindowPicker-7I7KWCMU.css +403 -0
- package/dist/Timeline/Timeline.d.ts +51 -0
- package/dist/Timeline/index.d.ts +3 -0
- package/dist/Timeline/index.js +3 -0
- package/dist/Timeline-27SYKOFX.css +144 -0
- package/dist/Toaster/Toaster.d.ts +108 -0
- package/dist/Toaster/index.d.ts +3 -0
- package/dist/Toaster/index.js +5 -0
- package/dist/Toaster-NVDCEIP5.css +306 -0
- package/dist/Toolbar/Toolbar.d.ts +62 -0
- package/dist/Toolbar/index.d.ts +3 -0
- package/dist/Toolbar/index.js +4 -0
- package/dist/Toolbar-IP6LXN45.css +48 -0
- package/dist/Tooltip/Tooltip.d.ts +51 -0
- package/dist/Tooltip/index.d.ts +3 -0
- package/dist/Tooltip/index.js +4 -0
- package/dist/Tooltip-LKQF7CE4.css +71 -0
- package/dist/Tree/index.d.ts +8 -0
- package/dist/Tree/index.js +1 -0
- package/dist/Tree/layout.d.ts +160 -0
- package/dist/Tree/model.d.ts +110 -0
- package/dist/Tree/useMeasuredSizes.d.ts +61 -0
- package/dist/Tree/useTreeState.d.ts +89 -0
- package/dist/TreeCanvas/TreeCanvas.d.ts +384 -0
- package/dist/TreeCanvas/index.d.ts +3 -0
- package/dist/TreeCanvas/index.js +10 -0
- package/dist/TreeCanvas/motion.d.ts +59 -0
- package/dist/TreeCanvas-WBNQPHC7.css +1098 -0
- package/dist/TreeGrid/TreeGrid.d.ts +142 -0
- package/dist/TreeGrid/index.d.ts +3 -0
- package/dist/TreeGrid/index.js +8 -0
- package/dist/TreeGrid-NSW7VMOX.css +132 -0
- package/dist/VisuallyHidden/VisuallyHidden.d.ts +23 -0
- package/dist/VisuallyHidden/index.d.ts +3 -0
- package/dist/VisuallyHidden/index.js +2 -0
- package/dist/VisuallyHidden-L552H7OA.css +21 -0
- package/dist/chunk-22O7LAW2.js +41 -0
- package/dist/chunk-23DHIMJA.js +38 -0
- package/dist/chunk-2EOKFKHX.js +263 -0
- package/dist/chunk-2HQR4U4D.js +143 -0
- package/dist/chunk-2PRH365R.js +74 -0
- package/dist/chunk-2ZQMA2WB.js +57 -0
- package/dist/chunk-34VGEDVV.js +907 -0
- package/dist/chunk-3CZ7XAVY.js +139 -0
- package/dist/chunk-3EYF6HKB.js +135 -0
- package/dist/chunk-3QRYCPXQ.js +116 -0
- package/dist/chunk-3ZP55QZE.js +40 -0
- package/dist/chunk-53DVJCVW.js +14 -0
- package/dist/chunk-5C5YMZQU.js +51 -0
- package/dist/chunk-5EKIWVOR.js +132 -0
- package/dist/chunk-5KPQ6D7N.js +54 -0
- package/dist/chunk-5SDBMKUO.js +297 -0
- package/dist/chunk-5VNUYO76.js +62 -0
- package/dist/chunk-5WS6L6FA.js +114 -0
- package/dist/chunk-6ZFVFA56.js +108 -0
- package/dist/chunk-7EM5T4ME.js +39 -0
- package/dist/chunk-7FZ53WDJ.js +163 -0
- package/dist/chunk-7HPRF5XN.js +525 -0
- package/dist/chunk-7RPGY3JE.js +43 -0
- package/dist/chunk-A2UXBCLO.js +48 -0
- package/dist/chunk-AV2DXAEL.js +27 -0
- package/dist/chunk-BHHIIK3M.js +17 -0
- package/dist/chunk-BZFWDIV6.js +9 -0
- package/dist/chunk-BZYDZBAN.js +123 -0
- package/dist/chunk-CMEE37QJ.js +628 -0
- package/dist/chunk-D35VTV2Z.js +106 -0
- package/dist/chunk-DD5BBGBE.js +162 -0
- package/dist/chunk-EN6K2IFA.js +256 -0
- package/dist/chunk-ESCO4XYW.js +144 -0
- package/dist/chunk-EUCC37WY.js +14 -0
- package/dist/chunk-EV6IMYJ6.js +62 -0
- package/dist/chunk-F27EPQ7D.js +68 -0
- package/dist/chunk-F4PJZ4ED.js +245 -0
- package/dist/chunk-F6OE5FH3.js +49 -0
- package/dist/chunk-FBZAKNSX.js +263 -0
- package/dist/chunk-FEFTGVMF.js +46 -0
- package/dist/chunk-GTOUGQQK.js +147 -0
- package/dist/chunk-GXQ3B46I.js +216 -0
- package/dist/chunk-H4FBTAYS.js +130 -0
- package/dist/chunk-HOXGBLEY.js +37 -0
- package/dist/chunk-HQNHV7D6.js +54 -0
- package/dist/chunk-HZ5XZNPS.js +98 -0
- package/dist/chunk-IAJLHVUA.js +28 -0
- package/dist/chunk-IJ7QXNIJ.js +1 -0
- package/dist/chunk-ITHXJHBB.js +280 -0
- package/dist/chunk-JCSA3XVH.js +32 -0
- package/dist/chunk-KLJCCMF3.js +97 -0
- package/dist/chunk-LWKJQTOM.js +77 -0
- package/dist/chunk-MNAXYY3R.js +215 -0
- package/dist/chunk-NDCATSW7.js +229 -0
- package/dist/chunk-NN62JQLR.js +29 -0
- package/dist/chunk-NPZY6YYL.js +251 -0
- package/dist/chunk-NTRZGNEX.js +529 -0
- package/dist/chunk-O6232G37.js +273 -0
- package/dist/chunk-O67VF5MS.js +58 -0
- package/dist/chunk-ODEQTPFM.js +6 -0
- package/dist/chunk-OFHBK7RI.js +498 -0
- package/dist/chunk-PNFASD6F.js +98 -0
- package/dist/chunk-PQADV6P6.js +19 -0
- package/dist/chunk-PZSWF74H.js +15 -0
- package/dist/chunk-Q4ZPPART.js +26 -0
- package/dist/chunk-QEUY3A5P.js +186 -0
- package/dist/chunk-QNCVHRZL.js +17 -0
- package/dist/chunk-QVE4GJAT.js +15 -0
- package/dist/chunk-QYOFACSP.js +36 -0
- package/dist/chunk-RA2ULG6H.js +29 -0
- package/dist/chunk-RDGD7Z7I.js +350 -0
- package/dist/chunk-RK4XPHVG.js +16 -0
- package/dist/chunk-RSAQOKH3.js +38 -0
- package/dist/chunk-S2XHHEW4.js +84 -0
- package/dist/chunk-S4GZWSKR.js +137 -0
- package/dist/chunk-SFZOKW2G.js +52 -0
- package/dist/chunk-UG46QBLT.js +325 -0
- package/dist/chunk-UOGSZKLT.js +134 -0
- package/dist/chunk-V2XMNLMS.js +52 -0
- package/dist/chunk-W4ENKCXJ.js +40 -0
- package/dist/chunk-W5J7R5EB.js +107 -0
- package/dist/chunk-WNI2WSJF.js +27 -0
- package/dist/chunk-WOYYD2YR.js +823 -0
- package/dist/chunk-WQX73TWF.js +1652 -0
- package/dist/chunk-XDTYBF2F.js +36 -0
- package/dist/chunk-XN4JBAN3.js +32 -0
- package/dist/chunk-XV6WRMBC.js +211 -0
- package/dist/chunk-YLYOWZ4V.js +33 -0
- package/dist/chunk-YODNNSZL.js +63 -0
- package/dist/chunk-ZCR6YGQ4.js +1 -0
- package/dist/chunk-ZFATEERP.js +144 -0
- package/dist/generated/index.d.ts +83 -0
- package/dist/generated/index.js +91 -0
- package/dist/index.d.ts +1 -1217
- package/dist/index.js +91 -4518
- package/dist/styles.css +14898 -4274
- package/dist/utils/cx.d.ts +10 -0
- package/dist/utils/headingLevel.d.ts +16 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/tone.d.ts +25 -0
- package/dist/utils/useClipboard.d.ts +82 -0
- package/package.json +18 -10
- package/scripts/bundle-css.mjs +25 -0
- package/scripts/check-css-literals.mjs +166 -0
- package/scripts/check-css-variables.mjs +184 -0
- package/scripts/check-css.mjs +27 -0
- package/scripts/css-source.mjs +67 -0
- package/scripts/write-barrel.mjs +60 -0
- package/dist/Accordion-NIMJQIOI.css +0 -88
- package/dist/AppShell-ZW3NSFBP.css +0 -51
- package/dist/Avatar-V6NG6Q45.css +0 -67
- package/dist/Button-XGCTXXYP.css +0 -174
- package/dist/Callout-LEGDIMAM.css +0 -52
- package/dist/Card-BOZGBML7.css +0 -86
- package/dist/Checkbox-OCUICIIZ.css +0 -58
- package/dist/CodeBlock-IEMBNIQC.css +0 -90
- package/dist/Container-LTKBA2FW.css +0 -21
- package/dist/Copyable-IB57OTHZ.css +0 -123
- package/dist/DataTable-3GALZUW2.css +0 -1593
- package/dist/DateTimePicker-NMSSGVPK.css +0 -312
- package/dist/Eyebrow-AVBORAS2.css +0 -30
- package/dist/IconButton-GB4PJDG2.css +0 -82
- package/dist/ImageUpload-3HQDF2KC.css +0 -158
- package/dist/Input-ZHUTN7CU.css +0 -210
- package/dist/Kbd-Q77CDZY7.css +0 -23
- package/dist/Modal-FJVG66S4.css +0 -138
- package/dist/Popover-FUFULUE2.css +0 -123
- package/dist/PricingCard-NKYV3JHA.css +0 -238
- package/dist/Section-665ZIPG6.css +0 -20
- package/dist/Select-GPRF7PRB.css +0 -294
- package/dist/Skeleton-3WEFLRDN.css +0 -378
- package/dist/StatCard-RTJIVTRB.css +0 -50
- package/dist/Table-AR3TRY2E.css +0 -89
- package/dist/Tabs-XZ4JIH3E.css +0 -153
- package/dist/Tag-R5QDJLYJ.css +0 -163
- package/dist/TimeWindowPicker-L3G5Q7UT.css +0 -629
- package/dist/Toaster-2O65VOR4.css +0 -264
package/README.md
CHANGED
|
@@ -5,16 +5,17 @@ Crewlet shared React components. Built on top of `@crewlethq/tokens` (CSS variab
|
|
|
5
5
|
## Use it
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
|
-
import '@crewlethq/tokens/css';
|
|
9
|
-
import '@crewlethq/tokens/css/
|
|
10
|
-
import '@crewlethq/tokens/css/
|
|
11
|
-
import '@crewlethq/
|
|
8
|
+
import '@crewlethq/tokens/css'; // the variables, once at the application entry
|
|
9
|
+
import '@crewlethq/tokens/css/themes'; // light, dark and follow-the-system
|
|
10
|
+
import '@crewlethq/tokens/css/density'; // compact and comfortable, optional
|
|
11
|
+
import '@crewlethq/tokens/css/fonts'; // self-hosted Inter and JetBrains Mono
|
|
12
|
+
import '@crewlethq/ui/styles.css'; // every component stylesheet, in one file
|
|
12
13
|
import { Button, Card } from '@crewlethq/ui';
|
|
13
|
-
import {
|
|
14
|
+
import { Book2Glyph } from '@crewlethq/icons/glyphs';
|
|
14
15
|
|
|
15
16
|
<Card>
|
|
16
17
|
<Card.Body>
|
|
17
|
-
<Button variant="primary" leadingIcon={<
|
|
18
|
+
<Button variant="primary" leadingIcon={<Book2Glyph />}>
|
|
18
19
|
Open documentation
|
|
19
20
|
</Button>
|
|
20
21
|
<Button variant="outline">Cancel</Button>
|
|
@@ -22,26 +23,55 @@ import { Icon } from '@crewlethq/icons';
|
|
|
22
23
|
</Card>
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
**Nothing here contacts a third-party host.** A glyph is an SVG that ships in
|
|
27
|
+
`@crewlethq/icons`, never a ligature of a font fetched from somewhere else: a
|
|
28
|
+
ligature renders the WORD until the font arrives, renders it for good on a
|
|
29
|
+
closed network, and is read aloud as that word by a screen reader.
|
|
30
|
+
|
|
31
|
+
**One component, one stylesheet.** `@crewlethq/ui/styles.css` is every
|
|
32
|
+
component's CSS in one file, for a consumer that wants it that way. Import the
|
|
33
|
+
folder instead and you carry that component's stylesheet and nothing else:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Button } from '@crewlethq/ui/Button'; // 8 KB of CSS, not 182 KB
|
|
37
|
+
```
|
|
26
38
|
|
|
27
39
|
## What is in here
|
|
28
40
|
|
|
29
41
|
| Area | Components |
|
|
30
42
|
| ---- | ---------- |
|
|
31
|
-
| Actions | `Button` (variants `primary`, `secondary`, `outline`, `tertiary`, `accent`, `danger`; sizes `small`, `medium`, `large`; shapes `square`, `pill`), `IconButton`, `Copyable`, `Kbd` |
|
|
43
|
+
| Actions | `Button` and `ButtonLink` (variants `primary`, `secondary`, `outline`, `tertiary`, `accent`, `danger`; sizes `small`, `medium`, `large`; shapes `square`, `pill`), `IconButton`, `Menu`, `Link`, `Copyable`, `CopyButton`, `Kbd` |
|
|
44
|
+
| Content and text | `Text` (the eight registers), `Prose`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime` |
|
|
32
45
|
| Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `ImageUpload` |
|
|
33
46
|
| Data display | `DataTable`, `CopyableCell`, `Table`, `StatCard`, `PricingCard`, `Avatar`, `CodeBlock`, `Skeleton`, `Eyebrow` |
|
|
34
|
-
|
|
|
35
|
-
|
|
|
47
|
+
| List screens | `DataView`, and its parts on their own: `DataViewToolbar`, `FilterAxisBar`, `FilterAxisChip`, plus the filter model (`filterDefsFromColumns`, `applyColumnFilters`, `filterPredicate`, `blankFilterValue`) |
|
|
48
|
+
| Feedback and overlays | `Callout`, `Toaster`, `Modal` (with `variant="sheet"`), `ConfirmModal`, `CommandPalette`, `Tooltip`, `Popover`, `Announcer` |
|
|
49
|
+
| Marks | `Count`, `StatusDot`, `EmptyValue`, `VisuallyHidden` |
|
|
50
|
+
| Hooks and seams | `useModalLayer`, `usePopupLayer`, `LayerHost`, `useListbox`, `useOptionKeys`, `useClipboard`, `useAnnouncer`, `useNow`, `formatRelative`, `HeadingLevelProvider`, `cx` |
|
|
51
|
+
| Layout | `Container`, `Section`, `Card`, `Tabs`, `Accordion`, `Disclosure` |
|
|
52
|
+
| Application shell | `AppShell` with `AppShell.Rail`, `AppShell.Topbar` and `useAppShell`, `SidebarNav` and `NavItem`, `BrandLockup`, `SearchTrigger`, `PageHeader`, `Toolbar`, `Stack`, `Inline`, `Spacer`, `AutoGrid`, `ErrorBoundary`, `useShortcut`, `useFullscreen` |
|
|
36
53
|
|
|
37
54
|
Storybook stories live under `apps/storybook/src/stories`.
|
|
38
55
|
|
|
39
|
-
Add new components under `src/<Name>/` with a `Component.tsx`,
|
|
56
|
+
Add new components under `src/<Name>/` with a `Component.tsx`, an optional
|
|
57
|
+
`Component.css`, an `index.ts` and a colocated `Component.test.tsx`. The root
|
|
58
|
+
barrel is GENERATED from the folder list by `scripts/write-barrel.mjs`, so
|
|
59
|
+
there is no shared index to edit and no conflict in it when several components
|
|
60
|
+
land at once.
|
|
40
61
|
|
|
41
62
|
## Theming hooks
|
|
42
63
|
|
|
43
|
-
- **Light and dark palettes.** Import `@crewlethq/tokens/css/themes` and
|
|
44
|
-
|
|
64
|
+
- **Light and dark palettes.** Import `@crewlethq/tokens/css/themes` and set
|
|
65
|
+
`data-theme="light"` or `data-theme="dark"` on `<html>`. With the attribute
|
|
66
|
+
absent the document follows the system. Every component reads the canonical
|
|
67
|
+
`--color-*` variables, so no provider is needed.
|
|
68
|
+
- **Density.** Import `@crewlethq/tokens/css/density` and set
|
|
69
|
+
`data-density="compact"` or `"comfortable"` on `<html>`. Every spacing and
|
|
70
|
+
size token scales with it, and the small control and row steps floor at 24px
|
|
71
|
+
so no density takes a pointer target under the size a finger can hit.
|
|
72
|
+
- **Accent colour.** The selected, active and focus states read `--color-brand-accent` and its companions from `@crewlethq/tokens`: `--color-brand-accent-rgb` (the same colour as a comma-separated `r, g, b` triple, for translucent tints), `--color-brand-accent-hover`, `--color-brand-accent-active`, `--color-brand-accent-soft` and `--color-brand-accent-soft-strong`. Rebind them together to retint the components. Every overlay portals into the nearest `LayerHost`, or into `<body>` when
|
|
73
|
+
there is none, so declare the override on `<html>` for those panels to follow
|
|
74
|
+
it.
|
|
45
75
|
|
|
46
76
|
```css
|
|
47
77
|
/* Retint every accent state to the brand slate. */
|
|
@@ -57,7 +87,1677 @@ body {
|
|
|
57
87
|
|
|
58
88
|
- **Component variables.** A few components expose their own `--crewlet-*` variables for values that are not tokens, for example `--crewlet-data-table-active-sort-color`, `--crewlet-data-table-row-hover-bg` and `--crewlet-data-table-archived-tint-rgb` on the compact `DataTable`, and `--crewlet-avatar-tint-fg` for the initials on a tinted `Avatar`. Override them from a rule that targets the component's root.
|
|
59
89
|
|
|
60
|
-
The component stylesheets read only variables that `@crewlethq/tokens` emits or
|
|
90
|
+
The component stylesheets read only variables that `@crewlethq/tokens` emits or
|
|
91
|
+
that the components declare themselves, and the same check refuses a glyph
|
|
92
|
+
drawn as a ligature, anything loaded from a host, a theme painted on a body
|
|
93
|
+
class, the decoration step used as text, and a focus rule that turns the
|
|
94
|
+
outline off without putting one back. `npm run lint` in this package runs it,
|
|
95
|
+
and `npx crewlet-css-check <folder>` runs it and the literal check over an
|
|
96
|
+
application's own stylesheets.
|
|
97
|
+
|
|
98
|
+
## Overlays
|
|
99
|
+
|
|
100
|
+
Every overlay in this package is on ONE layer stack, so a press of Escape
|
|
101
|
+
closes exactly one surface, a press outside dismisses the surface it belongs
|
|
102
|
+
to, and a modal traps Tab. A surface's z-index is its DEPTH in the stack inside
|
|
103
|
+
the token layer band, never a counter, so the tenth dialog of a session still
|
|
104
|
+
paints below a toast.
|
|
105
|
+
|
|
106
|
+
An application does not have to do anything to get this. It matters in two
|
|
107
|
+
places:
|
|
108
|
+
|
|
109
|
+
- A container that can go FULLSCREEN wraps its content in a `LayerHost`. A
|
|
110
|
+
fullscreen element renders only its own subtree, so a dialog portalled to
|
|
111
|
+
`document.body` while a canvas is fullscreen is not painted at all.
|
|
112
|
+
- A surface that pans or zooms dispatches `LAYER_REPOSITION_EVENT` on its host
|
|
113
|
+
when the content beneath it moves, and every panel anchored inside follows.
|
|
114
|
+
|
|
115
|
+
## Breaking changes in 0.3.0
|
|
116
|
+
|
|
117
|
+
Every one of these is a change a consumer can see. None of them needs an edit
|
|
118
|
+
unless it is listed under "What to change".
|
|
119
|
+
|
|
120
|
+
**The overlays.** Modal, Popover, Select and DateTimePicker each used to add
|
|
121
|
+
their own Escape listener to the document, so one press closed all of them, and
|
|
122
|
+
a backdrop press closed two layers at once. They are on one stack now: Escape
|
|
123
|
+
reaches the TOPMOST surface and a backdrop press dismisses the surface it
|
|
124
|
+
belongs to. An application that relied on one press closing a dialog and the
|
|
125
|
+
menu inside it together now needs two. A panel portals into the nearest
|
|
126
|
+
`LayerHost` rather than always into `document.body`.
|
|
127
|
+
|
|
128
|
+
**The controls wear the product's own register.** Every form control, button
|
|
129
|
+
and tab in the package is drawn the way the engine dashboard draws it, which
|
|
130
|
+
is the look the product ships. What follows is that change, component by
|
|
131
|
+
component. None of it needs an edit; all of it renders differently.
|
|
132
|
+
|
|
133
|
+
**`Button`'s hierarchy is redrawn.** `primary` is the ACCENT FILL rather than
|
|
134
|
+
the monochrome `--color-brand-primary`, because the one action on a screen has
|
|
135
|
+
to read as the answer the screen is asking for, and a white primary beside a
|
|
136
|
+
light-grey secondary was one pair of greys on the dark palette. `accent` is
|
|
137
|
+
the same recipe under the name a call site may already spell, so the two are
|
|
138
|
+
one rule and cannot drift into two blues. `danger` is no longer a red slab: it
|
|
139
|
+
is a secondary that has gone red, stating itself in the critical INK and
|
|
140
|
+
taking the hue as a tint and a boundary on hover, because it sits beside
|
|
141
|
+
Cancel and it is the button nobody should press by reflex. Labels are medium
|
|
142
|
+
rather than semibold and carry no negative tracking, and the padding ramp is
|
|
143
|
+
one scale step per size (small 8px, medium 12px, large 16px). What to change:
|
|
144
|
+
a call site that reached for `variant="danger"` to draw a filled red
|
|
145
|
+
confirmation now gets the quiet one; nothing else.
|
|
146
|
+
|
|
147
|
+
**`IconButton` has a `secondary` variant**, which is the bordered square a
|
|
148
|
+
secondary Button with no room for a label would be: a month's back and
|
|
149
|
+
forward, a step in a toolbar. Its corner is `--radius-md` like every other
|
|
150
|
+
button, rather than `--radius-sm`, and its glyph follows the label size a
|
|
151
|
+
Button of that step would draw (12px at `sm`, 14px at `md`) instead of running
|
|
152
|
+
one step heavier.
|
|
153
|
+
|
|
154
|
+
**The form row is one line, and its label is a micro-label.** `Label`,
|
|
155
|
+
`FormField`'s legend and `ReadOnlyField`'s label are 11px, medium, opened up
|
|
156
|
+
and uppercased on the tertiary step: a form is read by its values, and a label
|
|
157
|
+
set in the value's own size competes with it. A field carries its inset as
|
|
158
|
+
side padding only, so a medium one is exactly `--size-control-md` tall and
|
|
159
|
+
lines up with the button and the select beside it; it used to draw 39px
|
|
160
|
+
against a 32px button. Values are 13px rather than 14px, a `FormField`'s own
|
|
161
|
+
parts sit one scale step apart rather than two, and the affix a url field
|
|
162
|
+
wears (`InputAffix`) is the page's face rather than the code face and is
|
|
163
|
+
joined to the value it prefixes: it takes the inset a value takes and the
|
|
164
|
+
control after it takes none, so `https://` and the host read as one string
|
|
165
|
+
rather than as two fields. What to change: a screen that wrote its labels in
|
|
166
|
+
Title Case now reads them in caps; write them as sentences.
|
|
167
|
+
|
|
168
|
+
**A focused field shows ONE ring, and it is an outline.** `Input`, `Textarea`,
|
|
169
|
+
`Select` and `TagsInput` each drew two: the boundary took `--color-focus` and
|
|
170
|
+
`--shadow-focus` laid a second focus-coloured band outside it with a
|
|
171
|
+
background-coloured gap between the two, so a focused field read as an accent
|
|
172
|
+
hairline, a gap and an accent band. What is left is one 2px outline in
|
|
173
|
+
`--color-focus`, drawn INSIDE the box on `--size-focus-ring-inset-offset`, so
|
|
174
|
+
it covers the boundary rather than standing off it: a scroller cannot clip it,
|
|
175
|
+
and the field no longer grows a 4px halo when a caret lands in it. The
|
|
176
|
+
boundary keeps its own `--color-border-control` under it, a refused field
|
|
177
|
+
rings in `--color-feedback-danger` so a focus ring cannot paint out the one
|
|
178
|
+
mark saying the value cannot be saved, and the field's ring is keyed on the
|
|
179
|
+
field's own control rather than on `:focus-within`, so tabbing to a search
|
|
180
|
+
box's clear control rings the button and not the whole field around it.
|
|
181
|
+
`Combobox` and `ListInput` follow, being built from these. What to change:
|
|
182
|
+
nothing in a call site; a stylesheet of your own that keyed on
|
|
183
|
+
`.crewlet-input:focus-within` or on the focus shadow needs rewriting.
|
|
184
|
+
|
|
185
|
+
**Every dropdown is one panel.** `Select`'s list, `Combobox`'s completions and
|
|
186
|
+
`TagsInput`'s offers take the product's panel surface, hairline, corner and
|
|
187
|
+
medium shadow rather than the elevated surface and the large one, and each
|
|
188
|
+
caps at `12rem` rather than 240 or 320px. In `Select` and `TagsInput`, WHAT IS
|
|
189
|
+
CHOSEN and WHERE THE READER IS are drawn apart: the chosen row keeps its tick
|
|
190
|
+
in the accent ink and the row under the arrows or the pointer takes the hover
|
|
191
|
+
tint, which used to be the same accent tint on both. A row a caller draws
|
|
192
|
+
itself with `renderOption` carries the choice in its weight.
|
|
193
|
+
|
|
194
|
+
**`Select` takes a `width`.** `full` is the default and is what every call
|
|
195
|
+
site has today. `auto` is the toolbar picker: it sizes to its answer between
|
|
196
|
+
132px and 220px, which is what a filter bar is, rather than one question per
|
|
197
|
+
line. Its trigger is the field's own chrome at 13px, and its chevron is the
|
|
198
|
+
decoration step, as every glyph that repeats what the control already says.
|
|
199
|
+
|
|
200
|
+
**`Tabs` and `SegmentedControl` are redrawn.** The underline row scrolls
|
|
201
|
+
sideways rather than pushing the screen it is on, with its scrollbar hidden
|
|
202
|
+
and its focus ring drawn INSIDE the tab, because an outset one is clipped by
|
|
203
|
+
the scroller the row has become. The row brings its current section into its
|
|
204
|
+
own visible range when it mounts and when the section changes, moving nothing
|
|
205
|
+
but itself, and the accent bar is placed from a content offset so it stays
|
|
206
|
+
under its tab however far the row is scrolled. Its labels are 13px on the
|
|
207
|
+
tertiary step,
|
|
208
|
+
carry only the side pad that ring needs, and stand one scale step apart. The
|
|
209
|
+
pill row is a WELL WITH CHIPS IN IT: the inset surface, no boundary of its
|
|
210
|
+
own, a 2px inset, `--radius-md` on the container and `--radius-chip` on a chip,
|
|
211
|
+
12px labels, and the chip that is on lifted off the well with the panel
|
|
212
|
+
surface. A chip clears the 24px target floor in BOTH directions, where its
|
|
213
|
+
width used to be a pad around whatever it held and a one-letter mark drew a
|
|
214
|
+
target 19px wide, and a chip wider than its content by that floor centres what
|
|
215
|
+
it draws. The `sm` chip is the quiet step a rail's foot wears: 4px of side
|
|
216
|
+
padding and a glyph one type step under its label's, so a theme row and a
|
|
217
|
+
density row beside each other draw one cell each and read as one row of
|
|
218
|
+
controls. A hover brightens the label in both chromes rather than filling the
|
|
219
|
+
box behind it. `SegmentedControl`'s card rows take the framed-choice boundary
|
|
220
|
+
(no surface of their own, `--radius-lg`), which is the frame a `Checkbox`
|
|
221
|
+
draws. `ThemeSwitcher` follows, being a `SegmentedControl`.
|
|
222
|
+
|
|
223
|
+
**A focused `Switch` is ringed where a reader can see it.** The ring used to be
|
|
224
|
+
drawn on the native checkbox the track carries, which is the element that takes
|
|
225
|
+
focus and is also the element drawn at `opacity: 0` so the pointer hits a real
|
|
226
|
+
input. Opacity applies to the whole of an element's rendering, its outline
|
|
227
|
+
included, so a switch a keyboard reader tabbed onto showed no indicator at all.
|
|
228
|
+
It is drawn on the track now, keyed on the control's own `:focus-visible`, in
|
|
229
|
+
the same 2px outline every other control in the package wears. What to change:
|
|
230
|
+
nothing, unless a stylesheet of your own keyed on
|
|
231
|
+
`.crewlet-switch__control:focus-visible`.
|
|
232
|
+
|
|
233
|
+
**A framed `Checkbox` or `Switch` is a boundary, not a panel.** Neither draws
|
|
234
|
+
a surface of its own any more, both take `--radius-lg`, and a `tone="danger"`
|
|
235
|
+
framed row is no longer tinted red: what it deletes is said in the words and
|
|
236
|
+
in the colour of the tick, and a red panel puts the alarm on the frame. Their
|
|
237
|
+
descriptions are 12px, and both rows state the label's own 14px on the root
|
|
238
|
+
rather than inheriting an application's body size, because the box and the
|
|
239
|
+
track are centred on the label's first line in `em`. A screen that set a font
|
|
240
|
+
size on a `Checkbox` or `Switch` from outside now has to set it on the label
|
|
241
|
+
it means.
|
|
242
|
+
|
|
243
|
+
**The pickers take the control chrome.** `DateTimePicker` and
|
|
244
|
+
`TimeWindowPicker` are one line tall with a side inset, their glyphs are the
|
|
245
|
+
decoration step, their small boxes take `--radius-md` like every other field,
|
|
246
|
+
and a `TimeWindowPicker` preset is drawn as the small button it is.
|
|
247
|
+
`CalendarGrid`'s day cells take `--radius-xs`, the corner every small row in
|
|
248
|
+
the package wears.
|
|
249
|
+
|
|
250
|
+
**`TimeWindowPicker` is a rail of NAMED windows beside the calendar.** The two
|
|
251
|
+
tabs are gone. The relative pane used to be a grid of bare numbers under unit
|
|
252
|
+
headings, so "Last 7 days" was a 7 in a row called Days and reaching it meant
|
|
253
|
+
switching tab first; the windows a reader actually asks for (Last 15 minutes,
|
|
254
|
+
Last hour, Last 4 hours, Today, Yesterday, Last 24 hours, Last 7 days, Last 30
|
|
255
|
+
days) are now a radio group down the side of the panel, with the absolute range
|
|
256
|
+
beside them rather than behind them. The two sides SAY THE SAME THING: take a
|
|
257
|
+
named window and the calendar shades it and the boxes fill with the moments it
|
|
258
|
+
resolves to (a rolling window's end is drawn as the now it reaches); edit a box
|
|
259
|
+
or take a day and the window becomes that range and the rail lets go. The panel
|
|
260
|
+
is sized by its content rather than pinned to 560px, so a calendar is not
|
|
261
|
+
squeezed at comfortable density, and it scrolls only where one column will not
|
|
262
|
+
fit. What to change: `presets` is a flat `TimeWindowPreset[]` of
|
|
263
|
+
`{ label, duration | span }` rather than groups of `{ label, unit, values }`;
|
|
264
|
+
every individual label prop (`absoluteLabel`, `cancelLabel`, `presetLabel`, and
|
|
265
|
+
the rest) moves into one `labels` bag (`Partial<TimeWindowLabels>`), as
|
|
266
|
+
`DataView` already takes its own; and a screen with a default window should pass
|
|
267
|
+
`defaultValue`, which is what the new Reset goes back to and without which no
|
|
268
|
+
Reset is drawn.
|
|
269
|
+
|
|
270
|
+
**A time window can be a CALENDAR SPAN, not only a duration.** `TimeWindowValue`
|
|
271
|
+
grows `span` (`today`, `yesterday`), which starts at midnight in the value's own
|
|
272
|
+
time zone rather than a fixed number of hours ago, and it is its own field
|
|
273
|
+
because a field called `duration` holding `today` is a field that lies. A value
|
|
274
|
+
carrying both keeps the span. `resolveTimeWindow` takes an optional `now`, so a
|
|
275
|
+
caller can resolve two windows against one moment, and `describeTimeWindow` is
|
|
276
|
+
new: it answers what a window is CALLED, what it resolves to, and both as one
|
|
277
|
+
sentence, which is the one place the trigger's text, the trigger's name and the
|
|
278
|
+
panel's live line all read from.
|
|
279
|
+
|
|
280
|
+
**A picker trigger is named by the value it holds.** `DateTimePicker` and
|
|
281
|
+
`TimeWindowPicker` each carried `aria-label={ariaLabel}` over their own text,
|
|
282
|
+
and an `aria-label` REPLACES the content of the element it is on: a button
|
|
283
|
+
drawn as "Last 7 days" announced itself as "Time window", so the one thing the
|
|
284
|
+
control exists to say was the one thing a screen reader could not hear from it.
|
|
285
|
+
The name is the label and the value now ("Time window: Last 7 days", "Starts
|
|
286
|
+
at: Mar 10, 2026, 09:30"), which is also what Label in Name asks for, and how it
|
|
287
|
+
is composed is a prop. `DateTimePicker` grows a Now (or Today, where there is no
|
|
288
|
+
time to set), clamped to the bounds rather than refused, and moves its own label
|
|
289
|
+
props into one `labels` bag; `placeholder` and `ariaLabel` stay where they are,
|
|
290
|
+
so `DataView`'s moment editor needs no edit. What to change: a test asserting
|
|
291
|
+
the old bare name, and a screen that drew a Now button of its own beside the
|
|
292
|
+
picker.
|
|
293
|
+
|
|
294
|
+
**`CalendarGrid` draws a range as one band.** Its ends carry `is-range-start`
|
|
295
|
+
and `is-range-end`, asked of `inRange` about each chosen day's neighbours
|
|
296
|
+
rather than passed in as two more props, and the band closes the 1px gap
|
|
297
|
+
between cells with an outline in its own colour. An outline rather than a
|
|
298
|
+
box-shadow because today's ring is a box-shadow, and a day that was both today
|
|
299
|
+
and inside the range used to lose the ring saying which day it was.
|
|
300
|
+
|
|
301
|
+
**`ListInput`'s rows are tighter.** A value and its Move and Remove controls
|
|
302
|
+
sit one scale step apart rather than two, the controls carry no gap of their
|
|
303
|
+
own because an icon button's square already separates them, and they sit
|
|
304
|
+
level with the top of the control rather than nudged down.
|
|
305
|
+
|
|
306
|
+
**The overlays wear the engine dashboard's look.** Every surface that floats
|
|
307
|
+
over a page repaints. `Modal`, `ConfirmModal` and anything built on them sit on
|
|
308
|
+
`--color-surface-veil`, the page's own background at 0.72 rather than a black
|
|
309
|
+
scrim, so the page behind fades to context instead of being darkened out of the
|
|
310
|
+
way; the panel is `--color-surface-subtle`; a dialog is TOP placed by default,
|
|
311
|
+
`--spacing-10` down from the top of the window; the head is centred and its
|
|
312
|
+
title is the body size at semibold; a sheet's head is tighter than a dialog's;
|
|
313
|
+
and the body's prose is the page's own ink. `Menu` and `Popover` take the same
|
|
314
|
+
panel step, the menu at `--radius-md` and sized by its own longest row rather
|
|
315
|
+
than floored at 200px, the popover floored at 260px, both entering with a short
|
|
316
|
+
rise from under the trigger. A menu row is one line at `--size-row-sm` with
|
|
317
|
+
`--spacing-2` of inline padding and `--radius-xs`; a disabled row is the
|
|
318
|
+
tertiary ink rather than half-strength; a row that carries a description is the
|
|
319
|
+
only one drawn taller. `Listbox`, and with it `Select`, `Combobox`,
|
|
320
|
+
`MultiPicker` and `CommandPalette`, moves to a taller row (`--spacing-2` by
|
|
321
|
+
`--spacing-3` at `--radius-md`) whose resting ink is the secondary step.
|
|
322
|
+
`Tooltip` takes the panel step and `--radius-md`. What to change: a dialog that
|
|
323
|
+
relied on being vertically centred passes `placement="center"`; a caller that
|
|
324
|
+
sized a `Menu` or `Popover` through a wrapper of its own should drop it; a
|
|
325
|
+
caller measuring a fixed number of visible listbox rows should re-measure. To
|
|
326
|
+
get the palette's keyboard band, pass `CommandPalette`'s `footer` a line of
|
|
327
|
+
`Kbd` hints; it is drawn at the micro register rather than a modal footer's.
|
|
328
|
+
|
|
329
|
+
**What the overlay restyle settles on top of that.** A dialog CLIPS to its own
|
|
330
|
+
corners, so a body with `flush` no longer paints a square edge over a round
|
|
331
|
+
one; a sheet has no corners and no clip. A `Menu` panel is as wide as its
|
|
332
|
+
longest row up to 320px and the label that does not fit truncates, where the
|
|
333
|
+
pair of widths it shipped with let the panel leave the window instead. A
|
|
334
|
+
`Popover` row wraps: it is a panel of a declared width, and a line that cannot
|
|
335
|
+
break has nowhere to go in one. The row a menu or a panel hands focus to is
|
|
336
|
+
TINTED, not only ringed, so a menu opened with the pointer shows where the keys
|
|
337
|
+
will land; the ring is still `:focus-visible` only. A row that is both
|
|
338
|
+
destructive and disabled is drawn in the tertiary ink, because the danger ink
|
|
339
|
+
on a row nobody can press is the loudest invitation in the palette. And
|
|
340
|
+
`--color-surface-veil` is each root's OWN `--color-surface-background` at 0.72,
|
|
341
|
+
the marketing root included, which is a rule the palette suite now measures.
|
|
342
|
+
|
|
343
|
+
**`Popover`.** Its default `role` is `dialog` rather than `menu`, because a
|
|
344
|
+
filter form announced as a menu of commands told a screen reader that every
|
|
345
|
+
field in it was a menu item. `onOpenChange` takes a second argument saying why,
|
|
346
|
+
and is never called on mount; the effect it replaced reported one on mount and
|
|
347
|
+
on every render after it.
|
|
348
|
+
|
|
349
|
+
**`Button` and `IconButton`.** Sizes follow `--size-control-*`, so a small
|
|
350
|
+
button is 28px and a large one 44px. `IconButton`'s `sm` step is 24px rather
|
|
351
|
+
than 22px, which is the smallest target WCAG 2.2 accepts.
|
|
352
|
+
|
|
353
|
+
**Glyphs are nodes, not names.** `TabsItem.icon`, `DataTable`'s `icon` and
|
|
354
|
+
`actionIcon`, `DataTableRowAction.icon` and the Toaster's `icon` each take a
|
|
355
|
+
glyph component now, and the `material-symbols-outlined` class has left
|
|
356
|
+
component markup.
|
|
357
|
+
|
|
358
|
+
**Every dropdown is the design system's own.** `Select` draws its listbox in
|
|
359
|
+
every mode it is used in, which is what 0.2.0 did and what the product's own
|
|
360
|
+
look asks for: the list takes the theme, the density, the tokens and the layer
|
|
361
|
+
stack, rather than the operating system's palette in the middle of a dark
|
|
362
|
+
dialog. What a reader used to get free from the platform it now earns back for
|
|
363
|
+
itself, and those rules are shared with every other list in the package
|
|
364
|
+
(`useOptionKeys`): type-ahead, Home and End, disabled answers stepped over, the
|
|
365
|
+
highlight announced through `aria-activedescendant`, Tab closing the list and
|
|
366
|
+
carrying on to the next control, Escape stopping at the list, and a stored
|
|
367
|
+
value the options no longer offer kept rather than swapped. A real `<select>`
|
|
368
|
+
is available as `mode="native"` for a surface that wants the phone's own
|
|
369
|
+
full-screen picker, and a call site that asks for it should say why. The two
|
|
370
|
+
inside `TimeWindowPicker`, its timezone and its custom unit, had no such reason
|
|
371
|
+
and are drawn here now.
|
|
372
|
+
|
|
373
|
+
**An empty answer the options offer is a choice.** A `Select` reads the empty
|
|
374
|
+
string as "nothing chosen" and shows its placeholder, unless the list itself
|
|
375
|
+
offers an option whose value is empty, which every filter row does ("Any
|
|
376
|
+
role"). On those lists the placeholder was drawn OVER the answer already
|
|
377
|
+
selected. What to change: a caller that offered `{ value: '', label: 'Any' }`
|
|
378
|
+
AND a `placeholder` now sees the option's own label, which is what it meant.
|
|
379
|
+
|
|
380
|
+
**A search field's clear control is a real button.** `<Input type="search">`
|
|
381
|
+
used to leave it to `::-webkit-search-cancel-button`, which one browser family
|
|
382
|
+
draws, nobody else does, and no keyboard anywhere can reach. Pass `onClear` and
|
|
383
|
+
the field draws the package's own: named, in the tab order, and it hands focus
|
|
384
|
+
back to the box it emptied. Without `onClear` a search field now has no clear
|
|
385
|
+
control at all, which is the honest version of what most readers already had.
|
|
386
|
+
|
|
387
|
+
**A table that does not page is not offered a page size, and draws no
|
|
388
|
+
chevrons.** `DataTable`'s settings frame always drew an items-per-page row and
|
|
389
|
+
its toolbar always drew Previous, a page number and Next; with
|
|
390
|
+
`paginated={false}` the caller slices its own rows, so nothing read that number
|
|
391
|
+
and neither chevron could ever be pressed. Both are drawn for a table that
|
|
392
|
+
reads them: `paginated`, or a caller that says it reads them itself
|
|
393
|
+
(`onItemsPerPageChange` for the size, `onPageChange` with `pageCount` for the
|
|
394
|
+
chevrons). A table that passes none of those keeps the rest of the frame, the
|
|
395
|
+
column order and visibility and the wrap-lines toggle, unchanged.
|
|
396
|
+
|
|
397
|
+
**The page, its size and the column order are controlled pairs.** `page` /
|
|
398
|
+
`onPageChange`, `itemsPerPage` / `onItemsPerPageChange` and `columnOrder` /
|
|
399
|
+
`onColumnOrderChange`, each the shape `visibleColumns` / `onVisibleColumnsChange`
|
|
400
|
+
already had: the table owns the choice when the pair is absent, and the caller
|
|
401
|
+
owns it when it is present. Which is the split the table now states outright.
|
|
402
|
+
What is on screen, the page, the size, the columns shown, their order and the
|
|
403
|
+
sort, is what a reader would send somebody, so every one of them can be lifted
|
|
404
|
+
into a screen's own history (a URL). What is a fact about this browser, the
|
|
405
|
+
wrapping and the column widths, stays in `localStorage` under `storageKey`. A
|
|
406
|
+
controlled choice is neither read from storage nor written to it, so the two
|
|
407
|
+
can never disagree on a reload.
|
|
408
|
+
|
|
409
|
+
**`All` means every row, not the row count when it was pressed.** The chip
|
|
410
|
+
wrote `data.length`, so a reader who chose All at twelve rows was on "twelve
|
|
411
|
+
per page" and the table began paging silently at the thirteenth; on an empty
|
|
412
|
+
table it wrote 0, and a page of no rows hid every row that arrived afterwards,
|
|
413
|
+
for the life of that `storageKey`. It is the sentinel `ALL_ITEMS` (`'all'`)
|
|
414
|
+
now, in the prop, in the callback and in storage, and the chip's accessible
|
|
415
|
+
name carries the count ("All 1,284 rows") because drawing every row of a long
|
|
416
|
+
list is a decision with a size on it.
|
|
417
|
+
|
|
418
|
+
**A column can be one a reader may not hide.** `DataTableColumn.hideable:
|
|
419
|
+
false` marks the column that says which row this is, and the last visible
|
|
420
|
+
column cannot be unticked either, in the settings frame or in the Columns
|
|
421
|
+
panel. Both drew a tick that unticked, and a table with no columns is a spacer
|
|
422
|
+
with a footer.
|
|
423
|
+
|
|
424
|
+
**The column list reorders from a control with a name.** The drag handle was an
|
|
425
|
+
`aria-hidden` glyph on a row draggable along its whole width. It is a button
|
|
426
|
+
now: named "Reorder <column>", in the tab order, and the drag starts from IT
|
|
427
|
+
rather than from anywhere in the row. Its arrow keys move the column, as the
|
|
428
|
+
Move up and Move down buttons beside it do; those two are `aria-disabled` with
|
|
429
|
+
a reason at the ends of the list rather than `disabled`, because disabling the
|
|
430
|
+
button a press is on drops focus to the document body. Each move is announced.
|
|
431
|
+
|
|
432
|
+
**A resize handle is a separator, not a button.** `role="separator"` with
|
|
433
|
+
`aria-orientation`, `aria-valuenow` and `aria-valuemin`, which is the shape a
|
|
434
|
+
reader's software already knows how to step. A column header is named by its
|
|
435
|
+
label alone now (`aria-labelledby`), because a cell named from its contents
|
|
436
|
+
reads every control that has drifted into it: with the handle a real control,
|
|
437
|
+
each header announced itself as "Seat Resize Seat".
|
|
438
|
+
|
|
439
|
+
**Reset to Default restores the order a table opens in.** It cleared the sort
|
|
440
|
+
outright, while the header's own reset put `defaultSort` back: one button, one
|
|
441
|
+
name, two meanings of "default". A table that opens ordered by spend, or by
|
|
442
|
+
when a turn started, answered Reset to Default with the order its rows happened
|
|
443
|
+
to arrive in. A caller holding the sort is told `defaultSort` now rather than
|
|
444
|
+
`null`, so a screen keeping the sort in a URL stops carrying a non-default
|
|
445
|
+
after a reset. `DataView` passes its own `defaultSort` down for the same
|
|
446
|
+
reason. What to change: a table whose default really is no order at all passes
|
|
447
|
+
no `defaultSort`, which is still what it gets.
|
|
448
|
+
|
|
449
|
+
**The settings cog no longer depends on a row-actions column.** In
|
|
450
|
+
`variant="default"` the cog lived inside the actions header, so a table with no
|
|
451
|
+
`rowActions` and no `onRowAction` had no way into its own settings at all. The
|
|
452
|
+
trailing chrome column is drawn for the cog on its own, which is one more
|
|
453
|
+
column in the row of a default-variant table that has none today.
|
|
454
|
+
|
|
455
|
+
**The Columns panel is gone from the table's chrome.** `DataTable`'s toolbar
|
|
456
|
+
carried a Columns popover, drawn for a caller that passed the
|
|
457
|
+
`visibleColumns` / `onVisibleColumnsChange` pair, and the settings frame owns
|
|
458
|
+
that job: a reader looking for which columns are shown goes to the gear, and
|
|
459
|
+
two controls for one answer is two places for the rules about it to disagree
|
|
460
|
+
(they already had, over the column a table may not hide). The frame is the way
|
|
461
|
+
in for every table now, the toolbar gets the width back, and a table with no
|
|
462
|
+
chevrons and no gear draws no toolbar row at all rather than one holding the
|
|
463
|
+
panel alone. `labels.columnsMenu` and `labels.columnsMenuTitle` go with it,
|
|
464
|
+
as do `.crewlet-data-table__columns-panel`, `__columns-fieldset` and
|
|
465
|
+
`__columns-legend`. The controlled pair is untouched and still drives the
|
|
466
|
+
table: what it changes is where the answer is KEPT, never how it is reached.
|
|
467
|
+
What to change: a caller that passed the pair alongside `showSettings={false}`
|
|
468
|
+
has taken the reader's only way in with it, so it draws a control of its own
|
|
469
|
+
or drops `showSettings={false}`.
|
|
470
|
+
|
|
471
|
+
**Every table opens the same settings frame.** `settingsVariant` is gone, and
|
|
472
|
+
so is the `compact` shape it chose: a table with a title, a description or an
|
|
473
|
+
icon used to get a frame holding the page size and the wrapping and nothing
|
|
474
|
+
else, so the only way to its columns was the Columns panel in the toolbar. One
|
|
475
|
+
frame now, at the wide step, holding the items-per-page chips, Wrap lines, and
|
|
476
|
+
the column list with its handles, its ticks and its Move up and Move down. The
|
|
477
|
+
two shapes differed in nothing a caller could have wanted besides that list: a
|
|
478
|
+
dialog carrying a drag-reorder list wants the width whatever the table above it
|
|
479
|
+
looks like. `labels.pageSize` goes with it, since the section is named
|
|
480
|
+
`labels.itemsPerPage` on every table. What to change: drop `settingsVariant`,
|
|
481
|
+
and where a card-style table was chosen for the shorter frame, expect the full
|
|
482
|
+
one.
|
|
483
|
+
|
|
484
|
+
**The count line under a table is gone, and so is `TableFooter`.** Every list
|
|
485
|
+
closed on a row saying "Showing 12 of 300", or "12 rows" where nothing was
|
|
486
|
+
hidden. What it said is said better where a reader already looks: how many of
|
|
487
|
+
a thing there are is a fact about the LIST rather than about the table drawing
|
|
488
|
+
it, so it belongs in the screen's own header beside the name of the thing
|
|
489
|
+
counted (`PageHeader`'s `badges`, or `Card.Header`'s `count` over a panel
|
|
490
|
+
table); which filters narrowed it is on the chips a step above the rows; and
|
|
491
|
+
which rows of how many are on this page is what the pager announces, the
|
|
492
|
+
control that moved the reader there. A second number under the table was a row
|
|
493
|
+
of chrome on every panel and a figure free to disagree with the one above it.
|
|
494
|
+
The component is deleted rather than left undrawn: nothing in the package
|
|
495
|
+
renders it, and the only shape a caller had left was hand-drawing it beside a
|
|
496
|
+
`DataTable`, which is the thing being removed. `DataView`'s `totalCount`,
|
|
497
|
+
`pagination` and `labels.footer` go with it, each having had the footer as its
|
|
498
|
+
only reader. What to change: put the count in the screen's header, and reach
|
|
499
|
+
for `DataTable`'s own `hasMore` / `onLoadMore` for a Load more control, which
|
|
500
|
+
draws under the rows inside the table.
|
|
501
|
+
|
|
502
|
+
**A table that pages can be paged, whichever variant it is.** The pager was
|
|
503
|
+
the compact variant's alone and `paginated` defaults to true in both, so a
|
|
504
|
+
`variant="default"` table sliced its rows into pages of ten and drew nothing to
|
|
505
|
+
turn them with: on twenty-five rows, fifteen of them were reachable only by
|
|
506
|
+
opening the settings frame and choosing All. Which control a reader is offered
|
|
507
|
+
is a question about whether the table pages, not about how it looks, so the
|
|
508
|
+
chrome row is drawn on either variant wherever there are pages to turn. The
|
|
509
|
+
variant still decides where the COG goes, which is the trailing chrome column's
|
|
510
|
+
header on a default table and that row on a compact one, so neither draws it
|
|
511
|
+
twice. `--crewlet-data-table-chrome-pad-x` moves onto the root both variants
|
|
512
|
+
carry: unset on the default one, every padding written through it was an
|
|
513
|
+
invalid declaration a browser drops whole, and the bar would have arrived with
|
|
514
|
+
no inset at all. What to change: a default-variant table that should not page
|
|
515
|
+
passes `paginated={false}`, which is what it always meant.
|
|
516
|
+
|
|
517
|
+
**A table inside a card draws its controls on the card's header.** A panel
|
|
518
|
+
whose header says what the rows are and how many there are, with a bar under it
|
|
519
|
+
holding a pager and a cog and nothing else, is two rows of chrome over one
|
|
520
|
+
table. Where a compact `DataTable` fills a `Card` that has a `Card.Header`, the
|
|
521
|
+
pager and the settings cog are now drawn at the end of that header row and no
|
|
522
|
+
toolbar is drawn for them; the header keeps its icon, its title and its count
|
|
523
|
+
on the left, and the card's own `actions` keep the end of the row with the
|
|
524
|
+
table's controls closing onto them. Nothing is passed between the two: the card
|
|
525
|
+
publishes the row and the table draws into it, so a screen writes a card, a
|
|
526
|
+
header and a table exactly as before. The row is taken by ONE table, so a card
|
|
527
|
+
holding two of them puts the first on the header and leaves the second with its
|
|
528
|
+
own bar; a table that titles itself keeps its controls in its own header; and a
|
|
529
|
+
table outside a card, or in a card with no header, is unchanged. The seam is
|
|
530
|
+
`useCardHeaderSlot`, exported for any component that draws controls of its own.
|
|
531
|
+
`.crewlet-data-table__toolbar-end` is renamed `.crewlet-data-table__chrome`,
|
|
532
|
+
since the row is no longer always in a toolbar, and every rule dressing the
|
|
533
|
+
pager and the cog is now written through it rather than through the table's
|
|
534
|
+
root, which is no longer an ancestor once a card's header holds them. What to
|
|
535
|
+
change: nothing, unless a stylesheet targeted the old class name.
|
|
536
|
+
|
|
537
|
+
**A table keeps itself inside the container it is drawn in.** Its columns are
|
|
538
|
+
fitted to that container by a pass that measures the scroller, and three things
|
|
539
|
+
were stopping it. The widths the pass computed were saved under the same entry
|
|
540
|
+
as the widths an operator drags to, and the existence of that entry turned the
|
|
541
|
+
pass off for the life of the table, so it found its own arithmetic in the store
|
|
542
|
+
on the next mount and stood down: a window narrowed after the first paint left
|
|
543
|
+
every column at the width a wider one had justified. Which columns are on was
|
|
544
|
+
watched by nothing, so turning a hidden one back on added its width to a row
|
|
545
|
+
already exactly as wide as its box. And what asked for a fit was a flag the
|
|
546
|
+
pass could only clear once it had something to measure, so a table mounted in a
|
|
547
|
+
panel that is not on screen never fitted at all once it opened. Measured on the
|
|
548
|
+
engine dashboard before the change: twelve of twelve tables scrolling sideways
|
|
549
|
+
by 300 to 500px, seven of them with no rows in them. Only the columns a reader
|
|
550
|
+
resized are persisted now, under `<storageKey>_resizedColumns`, and the fit
|
|
551
|
+
treats those exactly as it treats a `width` a caller locked;
|
|
552
|
+
`<storageKey>_columnWidths` is retired and deleted on mount, because nothing in
|
|
553
|
+
it could be told from the pass's own arithmetic. The shrink also takes its room
|
|
554
|
+
from every column still above the floor rather than from whichever is last in
|
|
555
|
+
the row, and a row a single pixel over its container is corrected rather than
|
|
556
|
+
tolerated. What to change: nothing, unless a screen relied on a table holding
|
|
557
|
+
widths measured against some other container. A reader who had dragged a column
|
|
558
|
+
wide sees it back at the table's own width once.
|
|
559
|
+
|
|
560
|
+
**A table with no rows is laid out by its content.** A column width is a claim
|
|
561
|
+
about content: this column needs this much room for what is in it. A table
|
|
562
|
+
drawing one full-width message instead of rows, because it has nothing to show
|
|
563
|
+
or an error to show in its place, has no such content, and under the fixed
|
|
564
|
+
layout that claim is an instruction the browser carries out whatever it costs,
|
|
565
|
+
so the row came out wider than the box it sits in with a reader scrolling
|
|
566
|
+
sideways past columns holding nothing. The widths stay stated, so the header
|
|
567
|
+
keeps the shape its rows will land in; the table carries
|
|
568
|
+
`crewlet-data-table__table--rowless` until they arrive, and that class puts it
|
|
569
|
+
on the automatic layout, which reads a stated width as a preference and shrinks
|
|
570
|
+
the row to its container rather than past it. The loading skeleton keeps the
|
|
571
|
+
fixed layout, because its bars stand in for the rows about to replace them.
|
|
572
|
+
What to change: nothing.
|
|
573
|
+
|
|
574
|
+
**A frozen table scrolls rather than clip.** A rail carrying a `sticky: 'right'`
|
|
575
|
+
column locked its scroller to `overflow-x: hidden`, on the reasoning that the
|
|
576
|
+
fit pass always redistributes the regular columns to fit. The fit has a floor,
|
|
577
|
+
`minColumnWidth`, so a container narrower than those floors plus the pinned pane
|
|
578
|
+
is one the columns cannot be fitted to, and what the lock did there was clip
|
|
579
|
+
them: measured in a 460px box, 164px of the row was cut off, reachable by no
|
|
580
|
+
wheel, trackpad or bar a reader has. The row scrolls in that state and the pane
|
|
581
|
+
stays pinned to the scroller's right edge at its own width. The gutter the fit
|
|
582
|
+
pass reserves beside that pane is read off the cell that draws it rather than
|
|
583
|
+
assumed, so it is right at every density: it was a flat 24px, and the
|
|
584
|
+
comfortable density draws 27, which put the row three pixels past its container
|
|
585
|
+
and is a scrollbar now that the rail is not locked shut. What to change:
|
|
586
|
+
nothing.
|
|
587
|
+
|
|
588
|
+
**`TagsInput` has its own folder**, so `@crewlethq/ui/TagsInput` carries its
|
|
589
|
+
stylesheet. Importing `styles.css` whole, or the root barrel, changes nothing.
|
|
590
|
+
|
|
591
|
+
**The focus ring is an OUTLINE.** Nineteen component rules drew it as `outline:
|
|
592
|
+
none` plus a box-shadow, and forced-colors mode drops every box-shadow and
|
|
593
|
+
keeps outlines, so that ring disappeared entirely for the readers who most need
|
|
594
|
+
one. An application stylesheet that restyled a uilet component's focus ring by
|
|
595
|
+
overriding its `box-shadow` no longer reaches it and overrides `outline`
|
|
596
|
+
instead.
|
|
597
|
+
|
|
598
|
+
**`Tabs` renders a `div role="tablist"`, not a `nav`.** A tab strip is not a
|
|
599
|
+
navigation landmark, and a screen reader listing a page's landmarks was offered
|
|
600
|
+
every strip on it. A stylesheet or a query that selected the `nav` element
|
|
601
|
+
selects `.crewlet-tabs` instead; a class selector is unaffected.
|
|
602
|
+
|
|
603
|
+
**`Tabs` is one tab stop, and selection does not follow focus.** Every tab
|
|
604
|
+
used to be a tab stop and no arrow key did anything. The arrows, Home and End
|
|
605
|
+
move along the row now and Enter or Space selects, because a tab is a section
|
|
606
|
+
that pushes a history entry. Tabs carry `aria-selected` and no longer carry
|
|
607
|
+
`aria-current`: on a tab that told a screen reader the tab was the page the
|
|
608
|
+
reader was on. In `renderItem` (router) mode the root is a `nav` and nothing
|
|
609
|
+
carries a tab role, which is what a row of links has always been.
|
|
610
|
+
|
|
611
|
+
**`Checkbox`'s accessible name is now the label alone.** The label element
|
|
612
|
+
wrapped the description too, so the name was the whole paragraph and the
|
|
613
|
+
consequence of ticking the box was buried inside the name of the box. The
|
|
614
|
+
description is pointed at with `aria-describedby`, merged with any the caller
|
|
615
|
+
passes. The root is the `label` element rather than a div beside one, so the
|
|
616
|
+
whole row is one target.
|
|
617
|
+
|
|
618
|
+
**`FormField` renders the help line BESIDE an error, not instead of it**, and
|
|
619
|
+
joins both into `aria-describedby` in reading order. A form that relied on the
|
|
620
|
+
help line disappearing when a value was refused now shows both.
|
|
621
|
+
|
|
622
|
+
**`Select`'s keyboard changed.** Its list is a popup on the layer stack, so
|
|
623
|
+
Escape closes the list and stops there, and a press on a dialog's veil closes
|
|
624
|
+
the list and leaves the dialog. The arrows, Home and End step over disabled
|
|
625
|
+
options, type-ahead reaches a row, and `aria-selected` marks the CHOSEN option
|
|
626
|
+
rather than the highlighted one, which `aria-activedescendant` points at. The
|
|
627
|
+
document `mousedown` listener and the swallowed presses inside the panel are
|
|
628
|
+
gone with it.
|
|
629
|
+
|
|
630
|
+
**`TimeWindowPicker` validates by round trip.** 31 February, 31 April and
|
|
631
|
+
24:60:60 were accepted and rolled forward by `Date`; they are refused. The
|
|
632
|
+
year is no longer capped at 2030. Its masked boxes hold only what was typed,
|
|
633
|
+
with the mask letters drawn as an aria-hidden overlay rather than being the
|
|
634
|
+
input's own value, and every key that is not a printable non-digit reaches the
|
|
635
|
+
browser again.
|
|
636
|
+
|
|
637
|
+
**`DateTimePicker`'s month is a real grid.** It was 42 buttons in a row under
|
|
638
|
+
a `grid` role with no rows in it, each its own tab stop. It is one tab stop
|
|
639
|
+
with the arrow, Home, End and Page keys, and each day is named by its full
|
|
640
|
+
date. A stylesheet that targeted the old `.crewlet-datetime__cell` or
|
|
641
|
+
`.crewlet-datetime__grid` classes targets `.crewlet-calendar__day` and
|
|
642
|
+
`.crewlet-calendar` instead.
|
|
643
|
+
|
|
644
|
+
**`TagsInput` draws its chips above the field**, not inline in the box, and
|
|
645
|
+
its markup is `.crewlet-tags-input__chips` over `.crewlet-tags-input__field`.
|
|
646
|
+
A value shared a box with the text somebody was typing, so a long one pushed
|
|
647
|
+
the caret off the end and the Move controls an ordered chain needs had nowhere
|
|
648
|
+
to go.
|
|
649
|
+
|
|
650
|
+
**`Modal` closes on the backdrop's CLICK**, not its `mousedown`. The overlay
|
|
651
|
+
used to be gone before a tap's compatibility mouse events were hit-tested, so
|
|
652
|
+
the click a finger ended with landed on whatever the dialog had been covering.
|
|
653
|
+
A test that fired `mouseDown` on the overlay to close a dialog fires `click`.
|
|
654
|
+
|
|
655
|
+
**Headings follow where a component sits.** `Accordion.Item`'s trigger and
|
|
656
|
+
`Card.Title` used to render `h3` whatever was above them. Both now take the
|
|
657
|
+
level the surrounding surface declares, which is `h2` at the top of a page and
|
|
658
|
+
one deeper inside a `Card` or a titled `Section`. A hard-coded `h3` in an
|
|
659
|
+
outline is a level missing, which is what a screen reader navigating by
|
|
660
|
+
heading meets. Pass `headingLevel` on an Accordion, or `as` on a Card title, to
|
|
661
|
+
pin one.
|
|
662
|
+
|
|
663
|
+
**`Copyable` is ONE tab stop.** Its value was a `role="button"` span with its
|
|
664
|
+
own `tabindex` beside the button that did the same thing, so every identifier
|
|
665
|
+
in a table was two stops on the way to the next row. The value is plain text
|
|
666
|
+
now, and the icon button is the only control; a click on the value no longer
|
|
667
|
+
copies. It also REPORTS A REFUSAL rather than showing the tick over an empty
|
|
668
|
+
clipboard, which is what it did on any origin the Clipboard API is not
|
|
669
|
+
available at.
|
|
670
|
+
|
|
671
|
+
**`Card`'s section padding is applied.** `padding` on `Card.Header`,
|
|
672
|
+
`Card.Body` and `Card.Footer` was declared and never read, so it reached the
|
|
673
|
+
DOM as a `padding="md"` attribute and styled nothing. It is a class now, and
|
|
674
|
+
`Card.Body` and `Card.Footer` take the SAME five steps as the card itself, so
|
|
675
|
+
one word names one inset wherever it is said. `Card.Header` takes none at all:
|
|
676
|
+
its rhythm is the header row's, and `divided` is the control over it. A
|
|
677
|
+
selector matching `[padding]` matches nothing. An interactive card also lifts
|
|
678
|
+
by its shadow rather than by a one pixel `translateY`, so a grid of them no
|
|
679
|
+
longer shifts under the pointer.
|
|
680
|
+
|
|
681
|
+
**`CodeBlock`'s lines carry a real newline**, so `textContent` and a selection
|
|
682
|
+
keep the structure they used to lose, and with line numbers off the code is one
|
|
683
|
+
text node. Its header control is `CopyButton`, which is a labelled button
|
|
684
|
+
("Copy") rather than the icon-only one named "Copy code to clipboard".
|
|
685
|
+
|
|
686
|
+
**`Accordion`'s trigger hover is neutral**, not the accent, and the trigger
|
|
687
|
+
carries `crewlet-disclosure__trigger` alongside `crewlet-accordion__trigger`,
|
|
688
|
+
because `Accordion.Item` and `Disclosure` are now one implementation.
|
|
689
|
+
|
|
690
|
+
**`Container`'s narrow gutter switches at 480px**, the small breakpoint token,
|
|
691
|
+
rather than at 640px, which was a step nothing else in the system uses.
|
|
692
|
+
|
|
693
|
+
**`Kbd` is set in the mono face, and `subtle` no longer fades it.** A keycap is
|
|
694
|
+
a literal, and a proportional face draws `l`, `I` and `1` as three widths of
|
|
695
|
+
one mark. `subtle` used to be `opacity: 0.7`, which quiets the printed key as
|
|
696
|
+
well as the frame and measured 3.43:1 on the panel; it gives up its raised edge
|
|
697
|
+
and its fill instead and takes the tertiary ink, which is measured at 4.5:1 on
|
|
698
|
+
every surface. A hint that names the key a reader is told to press is the last
|
|
699
|
+
thing on a row that may be unreadable.
|
|
700
|
+
|
|
701
|
+
**One spelling for a status.** `Tag`'s `warn` variant is `warning`, and the
|
|
702
|
+
Toaster's `warn` and `error` are `warning` and `danger`. The package shipped
|
|
703
|
+
`warn` in two components and `warning` in two others, and `danger` beside
|
|
704
|
+
`error`, so the same state had two names depending on which component drew it.
|
|
705
|
+
A tag's tones are also the phase vocabulary now (`phase-onboarding`,
|
|
706
|
+
`phase-execute`, `phase-review`), and every tone takes its fill, ink and line
|
|
707
|
+
from the token layer rather than from the five dark-theme literals it used to
|
|
708
|
+
spell, which measured between 1.14:1 and 1.58:1 on a light page.
|
|
709
|
+
|
|
710
|
+
**Nothing animates on arrival.** A `Tag` no longer fades in on mount: tags
|
|
711
|
+
arrive in their hundreds on a data push, and a list that animates every arrival
|
|
712
|
+
is one nobody can read while it is arriving. `animateIn` opts back in, guarded
|
|
713
|
+
by reduced motion.
|
|
714
|
+
|
|
715
|
+
**`Toaster`.** Its live regions are mounted from the start rather than arriving
|
|
716
|
+
with the first toast, split into a polite one and an assertive one, and no
|
|
717
|
+
toast carries a region of its own. Dismissal is a timer that pauses on hover
|
|
718
|
+
and on focus and keeps what is left of it, where it used to be the drain's
|
|
719
|
+
`animationend`: under reduced motion, and under any application rule that
|
|
720
|
+
collapses animations, a toast never went away at all. A toast with no
|
|
721
|
+
`duration` now takes its variant's default (4s for `success` and `info`, sticky
|
|
722
|
+
for `warning` and `danger`) rather than being sticky, and the stack portals
|
|
723
|
+
into the nearest `LayerHost`. `ToastProvider` and `useToast` are new.
|
|
724
|
+
|
|
725
|
+
**`Callout` sets no role.** Every callout was a `role="status"`, so a screen
|
|
726
|
+
reader arriving at a screen read out each static banner in turn and one that
|
|
727
|
+
had been up for an hour was announced as news. Announcing is opt-in through
|
|
728
|
+
`live` or an explicit `role`. It also always draws a glyph, which cannot be
|
|
729
|
+
turned off: under deuteranopia the warning and danger hues move towards one
|
|
730
|
+
another and a callout has no label of its own to fall back on.
|
|
731
|
+
|
|
732
|
+
**`Avatar` is neutral.** The initials fallback no longer takes the brand fill,
|
|
733
|
+
and the seeded tints need `tone="seeded"` beside `colorSeed`. A hash of a name
|
|
734
|
+
carries no information: rename the seat and its colour changes. Its default
|
|
735
|
+
size is the `md` step (32px) rather than 64px, and its initials split on
|
|
736
|
+
hyphen, underscore and dot as well as whitespace, so `backend-engineer` is BE.
|
|
737
|
+
|
|
738
|
+
**`StatCard` drops the `brand` tone**, and the tone it is given now paints the
|
|
739
|
+
VALUE rather than the icon, which is what its props always said. A `loading`
|
|
740
|
+
tile draws a placeholder line and carries `aria-busy` instead of a literal
|
|
741
|
+
`--`, which a screen reader reads as "dash dash".
|
|
742
|
+
|
|
743
|
+
**`Skeleton` drops `glowColor` and `animationDuration`**, and the
|
|
744
|
+
`SkeletonGlow` type with them. Each of the five glows was a raw `rgba` outside
|
|
745
|
+
the palette, and a blue placeholder and a red one say nothing different about
|
|
746
|
+
content that has not arrived.
|
|
747
|
+
|
|
748
|
+
**`Modal` names itself.** `labelledBy` is gone: the title is linked by an id the
|
|
749
|
+
component mints, so a dialog is named whether or not the caller remembered to
|
|
750
|
+
name it, and a surface that draws no title takes `ariaLabel` instead. None of
|
|
751
|
+
conlet's sixteen dialogs passed `labelledBy`, and every one of them was
|
|
752
|
+
announced as "dialog" and nothing more. `describedBy` still overrides, and
|
|
753
|
+
defaults to the subtitle.
|
|
754
|
+
|
|
755
|
+
**`Modal`'s title is not a heading element.** It was an `h2`; it is drawn at the
|
|
756
|
+
heading register on a `p` now, and a surface PROVIDES heading level 2 to
|
|
757
|
+
everything inside it, so a section in a dialog is an `h2` however deeply the
|
|
758
|
+
component that opened the dialog was nested. The dialog role already announces
|
|
759
|
+
the title, and a reader moving by heading met it twice. A stylesheet on
|
|
760
|
+
`.crewlet-modal__title` is unaffected; a query for an `h2` inside a dialog is
|
|
761
|
+
not.
|
|
762
|
+
|
|
763
|
+
**`Modal`'s footer holds two slots.** `footerStart` is drawn at the inline start
|
|
764
|
+
and `footer` at the inline end, each in a wrapper of its own, so a stylesheet
|
|
765
|
+
selecting `.crewlet-modal__footer > button` selects nothing now and wants
|
|
766
|
+
`.crewlet-modal__footer-end > button`.
|
|
767
|
+
|
|
768
|
+
**`ConfirmModal` mid-request refuses every way out.** `submitting` used to stop
|
|
769
|
+
the veil alone, so Escape and the close control still abandoned a request whose
|
|
770
|
+
outcome nobody had seen; it maps to `dismissable` now, which is all three. The
|
|
771
|
+
confirm button reports `aria-busy` instead of having its label swapped for
|
|
772
|
+
"Working", the prompt is a form whose confirm is the submit button (so Enter
|
|
773
|
+
from a field answers it), a `destructive` prompt is an `alertdialog`, and a
|
|
774
|
+
rejected `onConfirm` leaves the prompt open rather than becoming an unhandled
|
|
775
|
+
rejection. `confirmDisabled` and `submitting` no longer set the native
|
|
776
|
+
`disabled` attribute on the prompt's buttons either: each is soft-disabled with
|
|
777
|
+
`aria-disabled` and a reason, so the button keeps its focus and its pointer
|
|
778
|
+
events and can say why it refuses, which a natively disabled control cannot.
|
|
779
|
+
|
|
780
|
+
**`DataTable` sorts and marks rows differently.** The first press on a header
|
|
781
|
+
sorts ascending unless the column says otherwise with `firstDirection`, the
|
|
782
|
+
comparator is index-stable, and an absent value (null, undefined or NaN) sorts
|
|
783
|
+
last in BOTH directions, because a seat with no meter has not spent the least,
|
|
784
|
+
it has not been measured. A row's expanded state moved from the `tr` to a
|
|
785
|
+
button inside it, so `aria-expanded` is on the control rather than on the row;
|
|
786
|
+
a press that starts on a link, button or field inside a row no longer also
|
|
787
|
+
fires `onRowClick`; and the page resets on a filter or a sort rather than on
|
|
788
|
+
the row count. The props are generic in the row type (`DataTableProps<TRow>`),
|
|
789
|
+
which only a TypeScript consumer sees.
|
|
790
|
+
|
|
791
|
+
**A DataTable row action says `danger`, not `variant`.** `DataTableRowAction`
|
|
792
|
+
takes `danger: true` in place of `variant: 'danger'`, and `variant: 'primary'`
|
|
793
|
+
is gone: the kebab is the package `Menu` now rather than a second menu built
|
|
794
|
+
inside a Popover, and a menu has one destructive step and no headline one. Its
|
|
795
|
+
markup and its `crewlet-data-table__row-actions-*` classes went with it.
|
|
796
|
+
|
|
797
|
+
**A DataTable's archived row is neutral, and names its own property.** The
|
|
798
|
+
row used to be tinted and railed in a red the stylesheet mixed itself, which
|
|
799
|
+
says "this failed" about a row whose only fact is that nobody is using it any
|
|
800
|
+
more; a state that is not a problem takes no status hue. The rail is
|
|
801
|
+
`--crewlet-data-table-archived-rail`, one colour rather than the RGB triple
|
|
802
|
+
`--crewlet-data-table-archived-tint-rgb` carried, and that property is gone,
|
|
803
|
+
so an application that set it now has a neutral rail and no error to say so.
|
|
804
|
+
|
|
805
|
+
**`Table` renders a table.** It was a grid of `div`s and `span`s, which a
|
|
806
|
+
screen reader reads as a pile of text with no columns and no header
|
|
807
|
+
association. It emits `table`, `thead`, `th scope="col"`, `tbody` and `td`
|
|
808
|
+
now, with the same look and the same class names, so a stylesheet that
|
|
809
|
+
selected `.crewlet-table__row` as a `div` finds a `tr`.
|
|
810
|
+
|
|
811
|
+
**`AppShell` owns the layout.** It used to be passive: the slots positioned
|
|
812
|
+
themselves with `position: fixed` and the shell only reserved a matching margin
|
|
813
|
+
and padding on the content column, so every consumer repeated the rail's width
|
|
814
|
+
and the bar's height as literals in its own stylesheet. It is a grid now, sized
|
|
815
|
+
from `--size-shell-rail` and `--size-shell-topbar`, at `100dvh` with `overflow:
|
|
816
|
+
hidden` on the root, with real landmarks (an `aside` rail, a `header` outside
|
|
817
|
+
`main`, a focusable `main`), a skip link, and a narrow layout in which the rail
|
|
818
|
+
becomes a modal drawer on the layer stack. `sidebarWidth` and `topbarHeight` are
|
|
819
|
+
gone with the margins they set. The slot NAMES do not change: `sidebar` is still
|
|
820
|
+
`sidebar` and `topbar` is still `topbar`, and `AppShell.Rail` and
|
|
821
|
+
`AppShell.Topbar` are new sub-components to render into them.
|
|
822
|
+
|
|
823
|
+
**The rail's rows are chrome, and the row the reader is on takes the accent.**
|
|
824
|
+
`SidebarNav` drew the current row as a neutral lift with a one-pixel accent bar
|
|
825
|
+
outside its left edge, and its rows at the body step in the regular weight on a
|
|
826
|
+
12px corner. A rail is read at a glance rather than read through: a row is the
|
|
827
|
+
compact step in the medium weight on the chrome's own 8px corner now, and the
|
|
828
|
+
row the reader is on takes the accent's soft tint AND the accent ink, for its
|
|
829
|
+
label and for its glyph together. Position and hue, so it is found by a reader
|
|
830
|
+
who sees the hue and by one who does not. The glyph keeps no ramp of its own
|
|
831
|
+
either: it is the decoration step at rest and the row's own colour on a hovered
|
|
832
|
+
row and on the current one. Every pair is measured on the rail's own composites
|
|
833
|
+
by @crewlethq/tokens' palette suite. What does not change is where the state
|
|
834
|
+
comes from: `aria-current="page"` and nothing beside it.
|
|
835
|
+
|
|
836
|
+
**A rail row reads at full ink, and the rows in a group are flush.** A row at
|
|
837
|
+
rest takes `--color-text-primary` rather than the secondary step: a destination
|
|
838
|
+
is not quiet text, and fifteen rows in the quiet step are a column of grey a
|
|
839
|
+
reader has to hunt through. Contrast goes up on every ground a row can have,
|
|
840
|
+
and what carries hover is now the tint and the glyph alone, because there is no
|
|
841
|
+
ink step above the resting one. `NavGroup` also stopped putting a step between
|
|
842
|
+
the rows it holds: the group's label has its own bottom inset and the rail's
|
|
843
|
+
own gap separates one run from the next, so a step per row was 4px fifteen
|
|
844
|
+
times over, which stood the engine's rail 823px in a 755px scroller and left
|
|
845
|
+
its last two destinations reachable only by scrolling. A group whose label is
|
|
846
|
+
empty (`""`, `null` or `false`) draws no heading box at all and adds no level
|
|
847
|
+
for a screen reader to walk, where an empty `<div>` used to carry the label's
|
|
848
|
+
own padding and put sixteen blank pixels at the top of the rail.
|
|
849
|
+
|
|
850
|
+
**A nav badge has a tone, and the slot is what paints it.** `NavItem` takes
|
|
851
|
+
`badgeTone="attention"` for the one badge in the chrome allowed a status hue: a
|
|
852
|
+
count of what is waiting on a person, drawn as the warning ink on the warning
|
|
853
|
+
tint in a pill. The slot draws every badge now, so whatever is passed in
|
|
854
|
+
contributes its text and the name a screen reader reads after the row's own,
|
|
855
|
+
and a `<Count>` in a rail row is a figure rather than a second pill inside the
|
|
856
|
+
first. A caller that relied on a `<Count>` keeping its own inset pill in a nav
|
|
857
|
+
row sees a bare figure instead, which is what a rail badge was always meant to
|
|
858
|
+
be.
|
|
859
|
+
|
|
860
|
+
**The rail's head draws no divider, and its foot has a row shape.** `AppShell`
|
|
861
|
+
put a bottom border under `AppShell.Rail`'s header, at the same height as the
|
|
862
|
+
top bar's own, so the two read as one rule running the width of the window with
|
|
863
|
+
the brand and the screen title above it like a second bar. The rail is one
|
|
864
|
+
column: the bar's line belongs to the bar and the rail's right edge separates
|
|
865
|
+
the two. `AppShell.RailRow` is new, for the foot: a row on the rail's own two
|
|
866
|
+
insets (the gutter and the row pad), so a status line's mark lands on the line
|
|
867
|
+
every nav glyph above it sits on, with the 24px target floor that
|
|
868
|
+
`--size-row-sm` carries. It is a control only when it takes an `onClick`, and
|
|
869
|
+
`label` is refused by the type without one, because `aria-label` on an element
|
|
870
|
+
with no role is a name nobody is ever told.
|
|
871
|
+
|
|
872
|
+
**And the foot claims whatever else lands in it.** An application with a status
|
|
873
|
+
control of its own reaches for that one whatever this package offers, so a
|
|
874
|
+
`Button` that is a direct child of `AppShell.Rail`'s `footer` is drawn as a row
|
|
875
|
+
in the rail: the gutter and the row pad, the quiet ink, the regular weight,
|
|
876
|
+
left aligned, filling the rail, its label truncating, and its focus ring inside
|
|
877
|
+
its own box because the shell clips at exactly the viewport's height. A button
|
|
878
|
+
dropped there used to be centred in a 263px rail on a toolbar's inset, in a
|
|
879
|
+
toolbar's ink. Only a DIRECT child is claimed, so the theme and density
|
|
880
|
+
switchers a foot also holds, which sit in a row of their own, keep their own
|
|
881
|
+
register. The row itself is a spacing step above and below its line (34px at
|
|
882
|
+
the normal density) rather than a control height, with `--size-row-sm` under it
|
|
883
|
+
as the 24px target floor, and its transparent boundary spends one pixel of that
|
|
884
|
+
step rather than adding one, so a bordered row and a borderless one in one foot
|
|
885
|
+
stand the same height.
|
|
886
|
+
|
|
887
|
+
**The brand lockup is one block in the rail's own register.** `BrandLockup`
|
|
888
|
+
put a 4px gap between the product name and the context line under it, which
|
|
889
|
+
read as a name with a caption rather than as one lockup; the leading is the
|
|
890
|
+
space between them now, and it is the document's own rather than a tight step,
|
|
891
|
+
because the eight pixels it spends are eight the rail's head already has and
|
|
892
|
+
the two lines otherwise sat closer together than any other pair of lines in the
|
|
893
|
+
product. The context line drops from the wider uppercase step to the wide one,
|
|
894
|
+
which is the step the rail's group labels take: the two uppercase runs in the
|
|
895
|
+
rail sit twenty pixels apart, and at the wider step the company's name was
|
|
896
|
+
visibly broader than the sections under it. It also drops to the regular
|
|
897
|
+
weight, which is the one thing left separating it from those group labels: at
|
|
898
|
+
the same size, tracking, case and weight the company's name read as a third
|
|
899
|
+
section heading. The mark stays 24px, because that box is also the link's
|
|
900
|
+
target height and 22px would take the one control that always means "go home"
|
|
901
|
+
under the 24px floor.
|
|
902
|
+
|
|
903
|
+
**A keycap and a monogram are set on the document's own line.** `Kbd` took a
|
|
904
|
+
tighter step than the text it is drawn inside, so a cap stood a pixel under its
|
|
905
|
+
line and every row holding one sat a pixel off the rows that did not; a cap is
|
|
906
|
+
21.5px tall now rather than 20.4px. `Avatar`'s initials took a leading of 1 and
|
|
907
|
+
take the document's too, which draws identically (a fixed square centres
|
|
908
|
+
whatever line it holds) and is one number fewer in a component that sets four.
|
|
909
|
+
|
|
910
|
+
**`Input`'s widest step is 520px**, up from 480px. The steps are named for what
|
|
911
|
+
they hold and the widest one is the field a reader types a SENTENCE into rather
|
|
912
|
+
than a name, which is what the engine's knowledge search asks for: its own
|
|
913
|
+
placeholder runs past sixty characters and was cut mid-word at the step below.
|
|
914
|
+
Nothing spells `width="lg"` in the console today, so nothing there moves.
|
|
915
|
+
|
|
916
|
+
**The engine's look: the marks, the status surfaces and the feedback.** Every
|
|
917
|
+
one of these draws what the engine dashboard draws, which is the look the
|
|
918
|
+
product is standardising on. Nothing about any component's API, keyboard model,
|
|
919
|
+
ARIA or focus ring changes.
|
|
920
|
+
|
|
921
|
+
- **`Tag`** is the engine's badge: an 11px label in the medium weight, a 4px
|
|
922
|
+
corner, one pixel of padding above and below and eight on each side, on the
|
|
923
|
+
tone's soft tint under its ink. An unsized tag renders at `size="sm"`, which
|
|
924
|
+
is that 20px badge, where it used to render at 28px; `size="md"` keeps 28px
|
|
925
|
+
and moves to a 12px label. A tag that ACTS is 24px, because the engine's own
|
|
926
|
+
20px press is a target under the WCAG 2.2 floor and that is the one place its
|
|
927
|
+
look and the rule disagree. A phase variant renders UPPERCASE in the
|
|
928
|
+
micro-label register. A neutral `outline` tag takes the tertiary ink. A
|
|
929
|
+
`dot` inside a pill is drawn in the PILL'S OWN INK rather than in the
|
|
930
|
+
tone's fill step: the fill is measured as a mark against a page surface,
|
|
931
|
+
and on the tone's own soft tint the accent fell to 2.65:1 and the danger
|
|
932
|
+
red to 2.96:1 in the dark palette.
|
|
933
|
+
- **`FilterChip`** is neutral until it is on: the tertiary ink at the regular
|
|
934
|
+
weight, 8px of side padding rather than 12px. Its count inherits the chip's
|
|
935
|
+
ink rather than drawing a step quieter.
|
|
936
|
+
- **`Avatar`** is a ROUNDED SQUARE unless `shape="circle"` is passed, and the
|
|
937
|
+
square's corner moves with the box (4px at 20, 8px at 26 and 32, 12px at 40).
|
|
938
|
+
A NUMERIC size follows the same ladder rather than taking one fixed step,
|
|
939
|
+
so `size={24}` draws an 8px corner where it used to draw 12px, and any box
|
|
940
|
+
of 36px or more keeps the 12px it had. A dashed human badge takes the
|
|
941
|
+
default hairline. `ImageUpload` follows both: its trigger and its overlay
|
|
942
|
+
round with the badge inside them rather than at a step of their own.
|
|
943
|
+
- **`EntityChip`** takes the accent ink on hover as well as underlining.
|
|
944
|
+
- **`Callout`** draws no coloured boundary: the tint IS the boundary. Its
|
|
945
|
+
padding is 8px and 12px rather than 12px and 16px, and its corner 8px.
|
|
946
|
+
- **`Toaster`** lands on the panel surface with an 8px corner and the md
|
|
947
|
+
shadow, 16px from the viewport edge, 420px wide. A danger toast paints its
|
|
948
|
+
title, message and close control in the danger ink.
|
|
949
|
+
- **`Skeleton`**'s line, avatar, circle and button sweep a gradient rather than
|
|
950
|
+
fading a flat fill, at 1.5s; its card, list-item and info-item frames no
|
|
951
|
+
longer pulse.
|
|
952
|
+
- **`EmptyState`** sets its title in the secondary ink and its description in
|
|
953
|
+
the tertiary.
|
|
954
|
+
- **`Meter`**'s track is 4px at both sizes, and its legend label is tertiary.
|
|
955
|
+
- **`StatCard`** renders its LABEL ABOVE ITS VALUE, which is the order a board
|
|
956
|
+
is read and heard in, with its sub line in the tertiary ink. A standalone
|
|
957
|
+
tile and a `StatGroup` both cast the panel's xs shadow, and a `StatGroup`
|
|
958
|
+
reflows to two columns at 900px rather than 768px.
|
|
959
|
+
- **`PricingCard.Tag` and `PricingCard.PriceNote` render a `Tag`**, so they are
|
|
960
|
+
no longer uppercase, their corner is 4px rather than a pill, and the top tag
|
|
961
|
+
takes the accent's soft tint under its ink rather than the solid accent fill
|
|
962
|
+
under white. The card casts the panel's xs shadow.
|
|
963
|
+
- **`Count`, `StatusDot`, `TagGroup` and `NewItemsNotice`** keep their APIs and
|
|
964
|
+
their shapes. `StatusDot`'s pulse completes in 1.8s rather than 3.6s, and
|
|
965
|
+
`NewItemsNotice` drops to the regular weight with the accent FILL on its
|
|
966
|
+
hover boundary.
|
|
967
|
+
|
|
968
|
+
Three engine values are deliberately not adopted, each because it puts a colour
|
|
969
|
+
under the floor it has to clear: the neutral `StatusDot` and the neutral
|
|
970
|
+
`Meter` fill keep `--color-text-tertiary` rather than the decoration step,
|
|
971
|
+
which measures 2.33:1 on a light page and 2.25:1 on a dark one as a mark; a
|
|
972
|
+
`FilterChip`'s count keeps a measured text step for the same reason; and a
|
|
973
|
+
pressed `Tag` keeps its boundary rather than repainting its ground, which the
|
|
974
|
+
engine draws as its own ink on its own ink, at 1:1.
|
|
975
|
+
|
|
976
|
+
**Every table is drawn in one register.** The two `DataTable` variants,
|
|
977
|
+
`Table` and `TreeGrid` each spelled their own version of a column head; they
|
|
978
|
+
take one now, which is the micro-label step the rest of the system uses: 11px,
|
|
979
|
+
medium, uppercase, on the wide tracking, in the tertiary ink, with one rule
|
|
980
|
+
under it and a hairline between one column and the next. A cell is the compact
|
|
981
|
+
step on the table's own inset pair, in a row at least as tall as
|
|
982
|
+
`--size-row-md`, so a table tightens with the density setting. What this costs
|
|
983
|
+
a consumer: a header that was body-sized words is now a micro-label row.
|
|
984
|
+
|
|
985
|
+
**The header row is a band only where the rows are bounded.**
|
|
986
|
+
`maxBodyHeight` is new on `DataTable`, passed through by `DataView`, and unset
|
|
987
|
+
by default. A number of pixels or any CSS length caps the rows, so they scroll,
|
|
988
|
+
and that is also what pins the column names and paints the ground they need to
|
|
989
|
+
be read over the rows passing behind them. ONE prop, because either half alone
|
|
990
|
+
does nothing: the rows already sit in a scroller of their own (a table has to
|
|
991
|
+
be able to overflow sideways, and a box that scrolls in one axis scrolls in
|
|
992
|
+
both), so a pinned header pins against THAT box, and unbounded it is as tall as
|
|
993
|
+
its rows and never moves. Unset, the header row stands on whatever the table
|
|
994
|
+
stands on, which is what a table the PAGE scrolls wants. The comfortable
|
|
995
|
+
density still paints its header either way: at that density an unfilled header
|
|
996
|
+
row reads as one more row.
|
|
997
|
+
|
|
998
|
+
**Every row lights up under the pointer, and a row that acts points.** On a
|
|
999
|
+
table twelve columns wide the tint is how a reader holds their eye on one row
|
|
1000
|
+
while they cross it, so every row takes it; what says a row can be PRESSED is
|
|
1001
|
+
the cursor, and only a row that is `--clickable` or carries a row link takes
|
|
1002
|
+
one. `--crewlet-data-table-row-hover-bg` is the OVERLAY step rather than an
|
|
1003
|
+
opaque surface, because a table sits on the page, on a card and inside a dialog
|
|
1004
|
+
and only an overlay composites onto all three; the rows themselves paint no
|
|
1005
|
+
background at all. A hovered row carries that tint all the way to its pinned
|
|
1006
|
+
right pane, whether or not the row is a control: the table the pane was drawn
|
|
1007
|
+
for holds its action as a link inside the pane and has no clickable row at all.
|
|
1008
|
+
|
|
1009
|
+
**A chevron column is drawn at the chevron's size, and pressed at the target
|
|
1010
|
+
floor.** The expand-all control in the header and the expand control on each
|
|
1011
|
+
row are the drawing and a step of halo, not a 28px control box, because those
|
|
1012
|
+
two cells are what set the height of the column-name row and of every row in
|
|
1013
|
+
the table: painted at the control step they cost six pixels of the first and
|
|
1014
|
+
ten of each of the rest. The pressable area is a centred pseudo element at
|
|
1015
|
+
`--size-control-sm`, which floors at 24px however the density is set and
|
|
1016
|
+
reaches into the cell's own padding rather than making the cell taller. Their
|
|
1017
|
+
focus ring is the one ring in this component drawn OUTSIDE its control, since
|
|
1018
|
+
an inset one would be a square on the glyph.
|
|
1019
|
+
|
|
1020
|
+
**A selected row is the tint, and a row's state rail is an ink step.** The
|
|
1021
|
+
selection ring came off: it is the mark a selected CARD takes, and on a run of
|
|
1022
|
+
hairline rows it boxed one row out of the list. The tone rails
|
|
1023
|
+
(`--tone-danger`, `--tone-warning`, `--tone-info`) move from the fill step to
|
|
1024
|
+
the matching `-ink` step, because a fill is measured to 3:1 against the OPAQUE
|
|
1025
|
+
surfaces and a rail is drawn on the row: on the accent tint of a selected row
|
|
1026
|
+
the danger fill measured 2.86:1 in dark on a card, so the one row that was both
|
|
1027
|
+
the reader's and broken had a mark nobody could see. The other two tones cleared
|
|
1028
|
+
the floor there and move with it anyway, because which tone a row carries is not
|
|
1029
|
+
a reason for its rail to be drawn to a different floor. @crewlethq/tokens'
|
|
1030
|
+
palette suite measures all three on every ground a row can have now; the
|
|
1031
|
+
tightest is 5.05:1.
|
|
1032
|
+
|
|
1033
|
+
**The table says what it only drew.** The pagination cluster is a named
|
|
1034
|
+
`group`, so the name it has always carried is attached to a role and read: on
|
|
1035
|
+
a plain div `aria-label` names nothing. A group and not a landmark, because a
|
|
1036
|
+
landmark named "Pagination" has to be unique on the page and a table cannot
|
|
1037
|
+
know how many tables it is on screen with. The page number announces which rows the page is as
|
|
1038
|
+
well as which page it is (`labels.pageRange`), because a page number says where
|
|
1039
|
+
a reader is in the chrome and nothing about what they are paging through. One
|
|
1040
|
+
live region sits beside the table, mounted with it and empty until it has
|
|
1041
|
+
something to say, and reports both the loading state (`labels.loading`, beside
|
|
1042
|
+
the table's own `aria-busy`) and the empty row's own rendered text, so a filter
|
|
1043
|
+
that empties a list is heard and not merely seen. The TEXT rather than the node:
|
|
1044
|
+
`emptyMessage` is usually a whole `EmptyState` and sometimes holds a control, and
|
|
1045
|
+
a second copy of that inside the region would be a tab stop nobody can see. It is beside the table rather than around
|
|
1046
|
+
the sentence because a region that arrives together with its first content
|
|
1047
|
+
announces nothing: a reader's software registers the region, then reports what
|
|
1048
|
+
changes inside it, and the empty row appears at exactly the moment it would
|
|
1049
|
+
have had to speak.
|
|
1050
|
+
|
|
1051
|
+
**A pinned column keeps an opaque base.** A sticky-right cell mirrors the row's
|
|
1052
|
+
tint as a background LAYER over its own opaque colour rather than as the
|
|
1053
|
+
background itself, so the rows scrolling behind it still do not show through.
|
|
1054
|
+
Its default ground is the elevated step: the pane is the one part of a row that
|
|
1055
|
+
is not the row, and it reads as the pane it is by standing one step off the
|
|
1056
|
+
ground the rows are on. A table on another ground rebinds
|
|
1057
|
+
`--crewlet-data-table-sticky-bg`.
|
|
1058
|
+
|
|
1059
|
+
**`Table` frames itself and can be bounded.** The boundary and the radius move
|
|
1060
|
+
from the scroller to the table, so the frame clips a header band that runs to
|
|
1061
|
+
the edge and an action row that is fenced from the rows above it rather than
|
|
1062
|
+
floating under the frame. The plain `Table` draws no hover tint, for the reason
|
|
1063
|
+
above: none of its rows is a control. `maxHeight` is new, and it is what makes
|
|
1064
|
+
the sticky header mean something: a bounded table becomes its own vertical
|
|
1065
|
+
scroller and keeps its column names on screen.
|
|
1066
|
+
|
|
1067
|
+
**`PageHeader` is the operator's head, not the marketing one.** The title drops
|
|
1068
|
+
from the 32px display step to 20px and the description from 16px body to the
|
|
1069
|
+
13px compact step in the tertiary ink, with one spacing step between them
|
|
1070
|
+
instead of two. On a screen whose job is to show a table, the old head took a
|
|
1071
|
+
third of the first fold before a reader reached a row.
|
|
1072
|
+
|
|
1073
|
+
**`DataView` pages when a screen asks it to.** The composite passed
|
|
1074
|
+
`paginated={false}` as a literal, so no list screen could page whatever it
|
|
1075
|
+
passed, and the settings frame's page-size row was gated off with it. It is a
|
|
1076
|
+
prop that still DEFAULTS to false (a list screen scrolls unless it asks not
|
|
1077
|
+
to), and `page`, `itemsPerPage`, `pageCount`, their callbacks,
|
|
1078
|
+
`defaultItemsPerPage`, `itemsPerPageOptions`, `columnOrder`,
|
|
1079
|
+
`onColumnOrderChange` and `resizable` reach the table too.
|
|
1080
|
+
|
|
1081
|
+
**A list screen is one object.** `DataView`'s toolbar renders in the table's
|
|
1082
|
+
own toolbar row, through the table's `renderToolbar` slot, so the screen's
|
|
1083
|
+
search, Filter and Sort share a row with the table's pagination and settings
|
|
1084
|
+
cog rather than drawing a second bar above it. The chips go in
|
|
1085
|
+
`renderFilterBar`, a new `DataTable` slot for the band between the toolbar and
|
|
1086
|
+
the rows, drawn only when something is in it: a screen with no filters on has
|
|
1087
|
+
no empty strip. The chip row keeps its own fences when it is used on its own,
|
|
1088
|
+
which is what the exported `FilterAxisBar` is for. A framed list clips with
|
|
1089
|
+
`overflow: clip` rather than `overflow: hidden`, because `hidden` makes the
|
|
1090
|
+
panel the nearest scrollport and a sticky child of a box that never scrolls
|
|
1091
|
+
never moves, which is what `stickyToolbar` needs; it now pins the whole band,
|
|
1092
|
+
the table's half of the row included.
|
|
1093
|
+
|
|
1094
|
+
**`DataView`'s panel holds the table and nothing else.** The count that used
|
|
1095
|
+
to close it is gone from every list, so `.crewlet-data-view__table` has one
|
|
1096
|
+
child and the rows are the last thing inside the frame. The last row keeps its
|
|
1097
|
+
hairline cancelled, because what closes the list now is the frame's own bottom
|
|
1098
|
+
edge one pixel below it. The chip fence is a solid hairline rather than a
|
|
1099
|
+
dashed one, because a dashed edge means something else in this system: it is
|
|
1100
|
+
the mark a card wears when it stands for somebody outside it. The axis name on
|
|
1101
|
+
a chip and the chevron beside it drop their opacity, which had taken the chip's
|
|
1102
|
+
own measured ink down to 3.97:1 and 3.00:1 respectively.
|
|
1103
|
+
|
|
1104
|
+
**The canvas stands on the page's ground, and `TreeCanvas` draws the card.**
|
|
1105
|
+
`Canvas` paints `--color-surface-background` rather than the panel surface, so
|
|
1106
|
+
the cards on it read as objects on a field rather than as one sheet; a card
|
|
1107
|
+
that painted the panel colour is now visible against it, and one that painted
|
|
1108
|
+
`--color-surface-elevated` should move to the panel step.
|
|
1109
|
+
`.crewlet-tree-canvas__card` carries the frame (surface, boundary, 12px radius,
|
|
1110
|
+
`--shadow-xs`), the inset and radius of every `role="treeitem"` inside it, the
|
|
1111
|
+
focus ring those items never had, and the accent ring on the selected one, so a
|
|
1112
|
+
consumer that drew all of that itself now draws it twice. `cardOutline` is the
|
|
1113
|
+
new prop for the dashed edge of a card that stands for somebody outside the
|
|
1114
|
+
system. The connectors take `--color-border-control` rather than
|
|
1115
|
+
`--color-border-hover`: they are the only thing on the chart that says who
|
|
1116
|
+
reports to whom, and at 1.48:1 in light they were a line a reader had to hunt
|
|
1117
|
+
for.
|
|
1118
|
+
|
|
1119
|
+
**`TreeGrid` stands on the panel surface** rather than the elevated step, which
|
|
1120
|
+
is the colour a hovered row takes elsewhere, so an outline beside a table read
|
|
1121
|
+
as a table with every row hovered at once.
|
|
1122
|
+
|
|
1123
|
+
**`Charts` gains `TimeSeries` and `Sparkline`,** the two shapes the kit had no
|
|
1124
|
+
way to draw: a quantity over time whose x domain is the WINDOW rather than the
|
|
1125
|
+
data, and a shape beside a number that is hidden from assistive technology
|
|
1126
|
+
because the number already says everything it could. The legend swatch is a
|
|
1127
|
+
rounded square rather than a circle (at the smallest radius step an 8px swatch
|
|
1128
|
+
is a `StatusDot`), and a bar list's sub-label, tail and empty line move from
|
|
1129
|
+
the 11px micro-label step to the 12px caption step, which is what they are.
|
|
1130
|
+
|
|
1131
|
+
**`ErrorBoundary` is a panel.** It takes the panel's body inset and its rim
|
|
1132
|
+
light, its title drops from the 18px heading step to the panel-title step, and
|
|
1133
|
+
its description to the compact step.
|
|
1134
|
+
|
|
1135
|
+
**The rule under a table's last row belongs to whatever ends the rows.** A
|
|
1136
|
+
table standing on the page draws it, because nothing else closes the list and
|
|
1137
|
+
without it the rows stop in mid-air. Inside a `DataView` panel the frame takes
|
|
1138
|
+
it away, since the count row's own border sits one hairline lower and the two
|
|
1139
|
+
met as a doubled rule; the suppression is written where the frame is, which is
|
|
1140
|
+
`DataView`'s sheet, so a consumer framing a table itself keeps the same
|
|
1141
|
+
freedom. Both horizontal scrollers also contain their overscroll now, so a
|
|
1142
|
+
trackpad swipe that runs out of table no longer carries on into the browser's
|
|
1143
|
+
back navigation.
|
|
1144
|
+
|
|
1145
|
+
**A `TreeCanvas` node is a stack the component draws.** The card's frame moved
|
|
1146
|
+
into the component and the rhythm inside a node did not, so `role="treeitem"`
|
|
1147
|
+
now carries the flex column, the one-step gap and the default cursor along
|
|
1148
|
+
with the inset and the radius it already had. A consumer drawing those round
|
|
1149
|
+
its own name and meta line should drop them.
|
|
1150
|
+
|
|
1151
|
+
**A `TreeCanvas` card is one column, and the pointer's controls are drawn over
|
|
1152
|
+
its end.** The card was two columns and the second held the buttons, so every
|
|
1153
|
+
name in a chart was measured against the width three controls left over and the
|
|
1154
|
+
longer half were truncated at rest, for controls nobody was pointing at. The
|
|
1155
|
+
strip is quiet until the card is reached (by hover, by focus, on the selected
|
|
1156
|
+
node, and while a menu opened from it is up), so a name has the whole card.
|
|
1157
|
+
`ctx.actions(id)` is the whole of that strip now and replaces spreading
|
|
1158
|
+
`ctx.press(id)` on one: it carries the class the reveal is keyed on, the
|
|
1159
|
+
removal from the accessibility tree and the same press. It has to sit
|
|
1160
|
+
immediately after the node's own treeitem. `ctx.press` stays, for a
|
|
1161
|
+
pointer-only region that is NOT that strip. `renderUnder` is new: what hangs on
|
|
1162
|
+
the branch below a card, normally the control that adds a child, in a band the
|
|
1163
|
+
card's measured height already includes.
|
|
1164
|
+
|
|
1165
|
+
**`TreeCanvas` connectors turn near the child rather than running flat across
|
|
1166
|
+
the gap.** They were a square step, whose corners sit on the horizontal run a
|
|
1167
|
+
parent's children all share; they are a rounded step, so the corner belongs to
|
|
1168
|
+
the child it reaches. A consumer that measured the path data will see `V`, `Q`
|
|
1169
|
+
and `H` where it saw `V` and `H`.
|
|
1170
|
+
|
|
1171
|
+
**`TreeCanvas` draws an org chart node as well as a card, and `layoutConnectors`
|
|
1172
|
+
returns objects.** `appearance="node"` is the console org chart's own drawing: a
|
|
1173
|
+
node one rank tall and as wide as its own name between a fixed icon zone and a
|
|
1174
|
+
fixed actions column, the actions as a column down its right edge, and the
|
|
1175
|
+
control that adds a child as a disc on the branch below. `connector="curve"` is
|
|
1176
|
+
that chart's branch, one cubic from a parent's bottom into a child's top.
|
|
1177
|
+
`cardTone` tints a node with one of six hues, which reaches its fill, its edge,
|
|
1178
|
+
a halo outside it, its name's ink and the branch arriving at it, from one
|
|
1179
|
+
answer. `OrgNodeLabel` and `OrgNodeLead` are what such a node normally holds, the
|
|
1180
|
+
first of them `OrgLabel` in its node layout, which is the same component a
|
|
1181
|
+
table row's name cell is.
|
|
1182
|
+
Nothing changes for a chart that asks for none of it, except that
|
|
1183
|
+
`layoutConnectors` now returns `{ id, parent, d }` rather than a bare path
|
|
1184
|
+
string, so a connector can be asked about the node it reaches.
|
|
1185
|
+
|
|
1186
|
+
**An org chart is drawn on RANKS, and the space around it is part of the
|
|
1187
|
+
layout.** `appearance="node"` lays every card of a depth on one band, as tall
|
|
1188
|
+
as the tallest card in it and each card centred in it, with the next band a gap
|
|
1189
|
+
below: a rank of an organization is a row a reader scans across, and two units
|
|
1190
|
+
of one company sit on one line whether or not one of them carries a lead along
|
|
1191
|
+
its bottom edge. It is what the console chart does, and the spacing is that
|
|
1192
|
+
chart's own, as the nearest steps on this scale: `--spacing-9` between
|
|
1193
|
+
neighbours on a rank, `--spacing-11` between ranks, and `--spacing-8` around
|
|
1194
|
+
the whole chart, measured from probes so density scales them. A chart of panels
|
|
1195
|
+
is unchanged, and either answer can be asked for outright with the new `ranks`
|
|
1196
|
+
prop. `layoutForest` takes `ranks` and `margin` alongside its gaps, and its
|
|
1197
|
+
refusal now names the three rather than "gaps".
|
|
1198
|
+
|
|
1199
|
+
**A chart arrives rank by rank, and a relayout moves the cards rather than
|
|
1200
|
+
replacing them.** Cards are revealed 60ms apart, down the chart and then across
|
|
1201
|
+
each row, and each branch waits for the card it arrives at; a relayout tweens
|
|
1202
|
+
every card and every connector from where it was to where it is, over 350ms. A
|
|
1203
|
+
card waiting its turn carries `data-enter="waiting"` and is drawn at nothing,
|
|
1204
|
+
still measured and still in the layout. Neither runs under
|
|
1205
|
+
`prefers-reduced-motion: reduce`, where the chart is drawn whole and every
|
|
1206
|
+
relayout is a jump. A consumer that read a card's `transform` immediately after
|
|
1207
|
+
a change will now read where the card WAS, and should read it a frame later or
|
|
1208
|
+
ask the layout.
|
|
1209
|
+
|
|
1210
|
+
**The control that adds a child is a pill that splits, and it is drawn on a
|
|
1211
|
+
resting chart.** `AddPill` is the console chart's own gesture: one quiet mark on
|
|
1212
|
+
the branch, a spacing step across against the 24px pointer target around it,
|
|
1213
|
+
which opens into a section per kind when it is pointed at, with a spring out of
|
|
1214
|
+
a fraction of its width and a recoil back. In `appearance="node"` the strip
|
|
1215
|
+
under a card is no longer hidden until the card is reached; what is in it and is
|
|
1216
|
+
not the pill still is. A chart that hung a `Menu` under its cards keeps working
|
|
1217
|
+
and keeps its old resting state; one that wants the console's gesture returns an
|
|
1218
|
+
`AddPill` from `renderUnder`.
|
|
1219
|
+
|
|
1220
|
+
**`OrgTable` draws an organization as an indented table of rows,** which is the
|
|
1221
|
+
console's org table and the other half of the pair `TreeCanvas` opens: the
|
|
1222
|
+
chart shows the shape and the table answers questions about it, in an order a
|
|
1223
|
+
sort would destroy. It composes `TreeGrid`, so the rows, the keys, the cells,
|
|
1224
|
+
the add row and the layer its menus open in are unchanged; what it adds is the
|
|
1225
|
+
drawing that table already had and no consumer could reach. The tree's wires
|
|
1226
|
+
run down one gutter, and the element that draws a row's branch IS its indent,
|
|
1227
|
+
so the two can never disagree. `OrgTableName` is the two-line name group, and it
|
|
1228
|
+
is the SAME COMPONENT a chart node carries (`OrgLabel`) in its row layout,
|
|
1229
|
+
ranged left rather than centred, because a column of names is read down its
|
|
1230
|
+
leading edge. `OrgTableActions` is the strip
|
|
1231
|
+
of row controls, quiet until the row is reached by the pointer, by focus or by
|
|
1232
|
+
being the selected row, and simply drawn where a pointer cannot hover.
|
|
1233
|
+
`OrgTableAdd` is the SLOT the add pill opens in, and the pill in it is
|
|
1234
|
+
`AddPill` itself, so a row's plus and a branch's are one gesture. `tone` marks a row with one of the six
|
|
1235
|
+
node hues, reaching the branch arriving at it and the mark and name in it, and
|
|
1236
|
+
never the caption: the accent clears 4.2:1 on the table's own ground, which is
|
|
1237
|
+
over the floor for a drawing and under it for a word. Expand all and Collapse
|
|
1238
|
+
all are a tab on the table's own top edge, since they act on that table rather
|
|
1239
|
+
than on the screen around it.
|
|
1240
|
+
|
|
1241
|
+
**The org table's add is the chart's own pill, and a row stands at the
|
|
1242
|
+
console's own step.** `OrgTableAdd` held a second implementation of the split
|
|
1243
|
+
pill, which had no split, no divider between its kinds and a boundary of its
|
|
1244
|
+
own; it holds `AddPill` now and passes that component's props straight through,
|
|
1245
|
+
so one keyframe pair, one divider and one colour reach both surfaces. The plus
|
|
1246
|
+
is a sibling of `OrgTableActions` rather than a control inside it, so it is
|
|
1247
|
+
drawn on every row that can take a child instead of waiting for the pointer,
|
|
1248
|
+
and the row reserves the width the pill takes OPEN (`--crewlet-org-table-add`,
|
|
1249
|
+
one pointer target per kind), so the split no longer grows back over the cell
|
|
1250
|
+
beside it. The indent is `--spacing-8` a level where it was `--spacing-5`, a
|
|
1251
|
+
row in the tree stands at `--size-row-lg` where it stood at the grid's own step,
|
|
1252
|
+
the name group fills its cell so a trailing slot lines up down the column, and
|
|
1253
|
+
the Expand all and Collapse all pair no longer wears the same plus as the add
|
|
1254
|
+
on the rows beneath it. In `TreeGrid`, opening a row TURNS its chevron rather
|
|
1255
|
+
than swapping one drawing for another.
|
|
1256
|
+
|
|
1257
|
+
**`Canvas` says its zoom, between the two steppers.** Pressing the percentage
|
|
1258
|
+
puts a field in its place; Enter applies, Escape and blur abandon. Two labels
|
|
1259
|
+
join `CanvasLabels` for it, `zoom` and `zoomLevel`, so a canvas in another
|
|
1260
|
+
language sets them. Nothing needs an edit: a canvas drawing its own zoom
|
|
1261
|
+
readout beside the controls now draws two.
|
|
1262
|
+
|
|
1263
|
+
**`AddPill` is GREEN, and it is one control for two surfaces.** The mark, the
|
|
1264
|
+
pill's edge, the dividers and a hovered choice read the node ramp's green
|
|
1265
|
+
(`--color-node-green` and its derived steps) rather than the brand accent: the
|
|
1266
|
+
org chart this is drawn from paints this control, and nothing else on the
|
|
1267
|
+
chart, in one hue, which is what lets "the green plus" name a control. The
|
|
1268
|
+
quiet at rest is on the PLUS rather than on the whole button, because the disc
|
|
1269
|
+
behind it is a mask for the branch and a mask at half opacity is not one; the
|
|
1270
|
+
resting plus measures 4.01:1 and 3.79:1 on the dark grounds and 3.50:1 and
|
|
1271
|
+
3.31:1 on the light, where it measured 2.96 and 2.47 before. It also takes
|
|
1272
|
+
`size` (`sm`, the branch's disc; `md`, a row's control step) and `layout`
|
|
1273
|
+
(`split` over the mark; `inline` beside it), so a table row draws the same
|
|
1274
|
+
gesture as the chart, and a section may carry a `disabledReason`. Nothing
|
|
1275
|
+
needs an edit: the defaults are what the chart drew before.
|
|
1276
|
+
|
|
1277
|
+
**`AddPill` wears the console chart's own chrome.** The disc and the pill carry
|
|
1278
|
+
the NODE's ground (the page's own, with the card's step painted over it, so the
|
|
1279
|
+
pair is opaque wherever the chart is panned to) rather than the page's alone,
|
|
1280
|
+
which read as a hole punched in the chart; the disc's hairline is drawn at rest
|
|
1281
|
+
and fades over 150ms, as that chart's does; the two dividers are inset to the
|
|
1282
|
+
pill's own sixth, take no press, and are ELEMENTS rather than a border on a
|
|
1283
|
+
section, because a border cannot be shorter than its box and the box here is a
|
|
1284
|
+
pointer target half as tall again as the pill. A resting choice is at the
|
|
1285
|
+
plus's own step, which measures 3.80:1 and 3.99 on the dark grounds and 3.31
|
|
1286
|
+
and 3.51 on the light; that chart draws its choices at 0.6, which measures 2.51
|
|
1287
|
+
on the light node ground. What to change: nothing, unless a stylesheet reached
|
|
1288
|
+
the disc as `.crewlet-add-pill__mark::before` or the divider as a section's
|
|
1289
|
+
`border-left` (see the table below).
|
|
1290
|
+
|
|
1291
|
+
**`OrgNodeDisclosure` is new, and a chart that collapses draws its expander in
|
|
1292
|
+
it.** The branch under a node is the ADD's, and only the add's, which is what
|
|
1293
|
+
that chart draws: a second control beside it is a pair fighting over one place,
|
|
1294
|
+
and a pill splitting open covers whatever was next to it. In the `node`
|
|
1295
|
+
appearance this straddles the card's leading boundary and is out of the flow,
|
|
1296
|
+
so a node that can be expanded is the same width, the same height and in the
|
|
1297
|
+
same place as one that cannot, and it is quiet until the node is reached, by
|
|
1298
|
+
focus as well as by a pointer. **Render it as a SIBLING of the element you
|
|
1299
|
+
spread `ctx.item` on**, exactly where `ctx.actions` goes: a tree's items hold
|
|
1300
|
+
nothing focusable, so it cannot be a slot on `OrgNodeLabel`, which renders
|
|
1301
|
+
inside that element. It hides itself, as the actions strip does, and costs a
|
|
1302
|
+
reader nothing, because expansion is on the treeitem as `aria-expanded` and the
|
|
1303
|
+
Right and Left arrows. Spread `ctx.press(id)` on it, exactly as the strip under
|
|
1304
|
+
a card takes it: a press has to leave focus on the NODE, and the component owns
|
|
1305
|
+
what cannot be forgotten while the caller passes the one thing only the chart
|
|
1306
|
+
knows. What to change: a chart drawing an expander in
|
|
1307
|
+
`renderUnder`, or as a third cell of the actions column (16px on one rank,
|
|
1308
|
+
under the target floor), draws it here instead.
|
|
1309
|
+
|
|
1310
|
+
**`OrgNodeLead` draws the pill and the clear the way that chart does.** The
|
|
1311
|
+
pill is bounded at the hover step rather than the separator's, since it is a
|
|
1312
|
+
boundary around a value and every other line inside a node is a separator, and
|
|
1313
|
+
its words carry medium weight; the clear is a small disc with a ground a step
|
|
1314
|
+
past the card's and a boundary of its own, with a glyph a third of it, inside
|
|
1315
|
+
the pointer target that is pressed. Both the disc and the pill's own ends are
|
|
1316
|
+
ELEMENTS, because what a node is painted with is measured and no cascade
|
|
1317
|
+
reports a pseudo element's paint. What to change: nothing, unless a stylesheet
|
|
1318
|
+
reached inside the clear.
|
|
1319
|
+
|
|
1320
|
+
**A `TreeCanvas` node is painted over the page's ground, toned or not.** The
|
|
1321
|
+
branches are drawn under the cards and the neutral surface step is translucent
|
|
1322
|
+
where no theme is loaded, so a node with no tone had the rank above running
|
|
1323
|
+
through its own face and its name; a toned card already went down this way.
|
|
1324
|
+
What to change: nothing.
|
|
1325
|
+
|
|
1326
|
+
**`OrgNodeLabel`'s caption is the neutral tertiary ink on every node.** It used
|
|
1327
|
+
to take the node's own hue on a toned card, where it measures 4.2:1: over the
|
|
1328
|
+
floor for a drawing and under it for a WORD, and the same word was drawn in two
|
|
1329
|
+
colours across a chart and the table beside it. What to change: a chart that
|
|
1330
|
+
wants its caption tinted draws its own; the hue still reaches the card, its
|
|
1331
|
+
edge, its halo, its branch and its name.
|
|
1332
|
+
|
|
1333
|
+
**`OrgNodeLabel`'s trailing slot is a fixed width, and only drawn when asked
|
|
1334
|
+
for.** It was a 4px floor, so a live badge arriving made the node 35px wider
|
|
1335
|
+
and relaid the chart, which is what both this component and the chart above it
|
|
1336
|
+
promise never happens. It holds one control step of room whatever it is
|
|
1337
|
+
given, with its own separation either side, and a node that passes no
|
|
1338
|
+
`trailing` is drawn with no slot at all. What to change: a
|
|
1339
|
+
chart whose marks are wider than that step either shortens them or draws them
|
|
1340
|
+
on the caption line, where the wiring marks already ride.
|
|
1341
|
+
|
|
1342
|
+
**`OrgNodeLabel` and `OrgTableName` are one component now, `OrgLabel`.** They
|
|
1343
|
+
said the same four things about the same organization, in two components under
|
|
1344
|
+
two class prefixes: measured declaration by declaration the two stylesheets
|
|
1345
|
+
agreed on 38 of the 62 they set across the six elements they both draw, and
|
|
1346
|
+
seventeen of the 24 that differed are one difference restated, that a chart
|
|
1347
|
+
node is a box as wide as its own name and a table row is a line in a column.
|
|
1348
|
+
That is `layout="node" | "row"` now, and every other rule is written once. The
|
|
1349
|
+
other seven were the same rule written two ways, which is where the two defects
|
|
1350
|
+
below lived. Both names stay, as the layout filled in, so no
|
|
1351
|
+
call site changes.
|
|
1352
|
+
|
|
1353
|
+
Two defects go with the split. The chart repainted a mark that publishes its
|
|
1354
|
+
own ink and the TABLE did not, so a brand mark on a toned row was the one thing
|
|
1355
|
+
on it still drawn in somebody else's colour; the table held every child of its
|
|
1356
|
+
caption to a fixed size and the CHART held only a bare glyph, so a mark that
|
|
1357
|
+
arrived inside a span naming it shrank with the strip around it. Each surface
|
|
1358
|
+
had one of the two; both now hold for both.
|
|
1359
|
+
|
|
1360
|
+
What to change: a stylesheet reaching either component's parts by class. The
|
|
1361
|
+
shared elements are `.crewlet-org-label__icon`, `__text`, `__name`,
|
|
1362
|
+
`__caption`, `__kind` and `__trailing` for both surfaces, and a row's wrapper,
|
|
1363
|
+
which was `.crewlet-org-table__node`, is `.crewlet-org-label--row` and still
|
|
1364
|
+
carries `data-tone`. Nothing a caller passes moved, and `OrgNodeDisclosure`,
|
|
1365
|
+
`OrgNodeLead` and the rest of the table are untouched.
|
|
1366
|
+
|
|
1367
|
+
**`OrgNodeLabel` takes `iconSize`.** `lg` fills three quarters of the icon
|
|
1368
|
+
zone, which is the proportion the console chart gives the mark that stands for
|
|
1369
|
+
the thing a chart is ABOUT; `md`, the default, is unchanged.
|
|
1370
|
+
|
|
1371
|
+
**`OrgNodeLead` wraps its `clear` control**, so the chart can reveal it with
|
|
1372
|
+
the node's other controls and grow its target past the pill's own edge. What to
|
|
1373
|
+
change: nothing, unless a stylesheet targeted the control as a direct child of
|
|
1374
|
+
the pill.
|
|
1375
|
+
|
|
1376
|
+
**`TreeCanvas`'s add hangs BELOW the node it belongs to.** In its `node`
|
|
1377
|
+
appearance the strip under a card is out of the flow and drawn on the branch
|
|
1378
|
+
the children come off, centred on the node's own axis in a three-track grid,
|
|
1379
|
+
which is where the console chart puts it; it was inside the card, 13px above
|
|
1380
|
+
the branch's own start, and 14px right of the axis whenever an expander shared
|
|
1381
|
+
the strip. The room it needs moved into the rank gap with it, so the distance a
|
|
1382
|
+
reader sees between two ranks is the same number it was. The hairlines inside a
|
|
1383
|
+
node are inset at both ends, as that chart's are. What to change: nothing; a
|
|
1384
|
+
caller that positioned something in that strip itself should check it.
|
|
1385
|
+
|
|
1386
|
+
**`TreeCanvas` puts the canvas controls in the chart's own corner.** The
|
|
1387
|
+
`node` appearance defaults them to the top right, which is where that chart's
|
|
1388
|
+
toolbar is. `controlsExtra`, `controlsBelow` and `controlsPlacement` pass
|
|
1389
|
+
through to `Canvas`; `dimmed` pushes the chart back behind a surface opened
|
|
1390
|
+
about one of its nodes, and the ref is a `TreeCanvasHandle`, which adds
|
|
1391
|
+
`focusRegion` and `restoreView` to what a tree view answers.
|
|
1392
|
+
|
|
1393
|
+
**`TreeCanvas` adds a node IN the chart, not in a dialog over it.**
|
|
1394
|
+
`composing` names a GHOST: `{ id, parent, label, render, onCancel }`, a card
|
|
1395
|
+
the layout knows about and the data does not. The chart grafts it into the CARD
|
|
1396
|
+
forest after `parent`'s own children, opens `parent` if it is closed, makes
|
|
1397
|
+
room for it in the rank the new node will land in, draws the branch into it
|
|
1398
|
+
dashed, eases onto it with the room a form needs
|
|
1399
|
+
(`CANVAS_COMPOSE_CONTEXT`, two thirds of the pane, where a node opened about
|
|
1400
|
+
gets a third) and puts focus on the first control inside it; Escape calls
|
|
1401
|
+
`onCancel`, and when it goes the view the reader left is given back and focus
|
|
1402
|
+
returns to `parent`. Escape cancels from anywhere in the chart, not only from inside the form: a
|
|
1403
|
+
node being composed is a MODE, and a reader leaves a mode wherever they are
|
|
1404
|
+
standing. Its id must NOT be an id of `nodes`: that is what keeps
|
|
1405
|
+
every real node's level, position in set, set size and key the same while a
|
|
1406
|
+
node is being composed, and keeps the arrows, type ahead and the selection off
|
|
1407
|
+
something that is not there yet. It is drawn OUTSIDE the tree element, because
|
|
1408
|
+
a form is not a `treeitem`, and it is the one card in the chart that holds a
|
|
1409
|
+
tab stop. Width is `--crewlet-tree-canvas-compose-width` (20rem). What to
|
|
1410
|
+
change: nothing; a chart that asked for a child's name in a modal over itself
|
|
1411
|
+
can hand that form to `composing` instead.
|
|
1412
|
+
|
|
1413
|
+
**`CanvasHandle.focusRegion` takes a second argument, and `rememberView` is
|
|
1414
|
+
new beside it.** `focusRegion(target, context)`, where `context` is how many
|
|
1415
|
+
times the region's own width the visible width comes to: `CANVAS_FOCUS_CONTEXT`
|
|
1416
|
+
(3) when it is left out, which is what every existing caller gets.
|
|
1417
|
+
**The canvas has TWO eases, and `data-animate` names which is running.** A
|
|
1418
|
+
fit, a zoom step and a reveal are CORRECTIONS to the view the reader already
|
|
1419
|
+
has, and keep the default step; easing onto a region and giving the view back
|
|
1420
|
+
are the chart GOING somewhere, and take two `moderate` steps with the in-out
|
|
1421
|
+
curve, which is the console chart's own 400ms viewBox ease. At the correction's
|
|
1422
|
+
step that movement read as a jump, 2.7 times faster than the chart it is drawn
|
|
1423
|
+
to match, and what it is for is saying WHERE the place is. Reduced motion
|
|
1424
|
+
cancels both. What to change: a stylesheet selecting
|
|
1425
|
+
`.crewlet-canvas[data-animate='true']` now wants `'near'`, or `'travel'`.
|
|
1426
|
+
|
|
1427
|
+
A third argument, `ceiling`, caps how far in the ease may zoom, for a region
|
|
1428
|
+
with a size of its own to respect: a chart with few nodes has room to zoom far
|
|
1429
|
+
past a form's designed size and took it. `rememberView()` marks the view
|
|
1430
|
+
`restoreView` gives back without moving anything, for a region that does not
|
|
1431
|
+
exist yet: a card being added changes the content before there is a rectangle
|
|
1432
|
+
to ease onto, and the canvas answers content that changed shape by keeping what
|
|
1433
|
+
it holds reachable, so by the time `focusRegion` can be called the view is no
|
|
1434
|
+
longer the reader's. What to change: nothing.
|
|
1435
|
+
|
|
1436
|
+
**`Canvas`'s zoom keys answer anywhere inside it, and its limits are the
|
|
1437
|
+
content's.** `+`, `-` and `0` used to reach only the viewport ELEMENT, which is
|
|
1438
|
+
never what holds focus in a chart whose tab stop is an item: a reader who had
|
|
1439
|
+
navigated the chart pressed `+` and nothing happened. The arrows are still the
|
|
1440
|
+
viewport's own, and a text field inside keeps every key. The floor is now the
|
|
1441
|
+
FIT rather than a quarter, because there is nothing past the whole chart to
|
|
1442
|
+
see, and the ceiling is the largest item filling the viewport
|
|
1443
|
+
(`largestItemWidth`) rather than double size. `zoomAt`, `pinchView` and
|
|
1444
|
+
`clampZoom` take the limits as a last argument, defaulting to the fixed pair.
|
|
1445
|
+
What to change: a canvas that relied on zooming out past its own fit.
|
|
1446
|
+
|
|
1447
|
+
**`Canvas`'s controls are a GROUP, and the bar inside it is
|
|
1448
|
+
`.crewlet-canvas__bar`.** The group is a column in one corner holding the zoom
|
|
1449
|
+
bar, anything the caller adds to the end of it (`controlsExtra`) and any bars
|
|
1450
|
+
it stacks under it (`controlsBelow`). The zoom field is one control step wide
|
|
1451
|
+
rather than one step at least, so pressing the readout no longer moves the bar
|
|
1452
|
+
111px. What to change: a stylesheet that targeted `.crewlet-canvas__controls`
|
|
1453
|
+
for the bar's own ground or padding now wants `.crewlet-canvas__bar`.
|
|
1454
|
+
|
|
1455
|
+
**`Modal`'s side sheet is committed from its head.** The sheet variant takes a
|
|
1456
|
+
`headerActions` slot beside its title, its head stands at the shell's own
|
|
1457
|
+
top-bar height (`--size-shell-topbar`) with 16px over 24px insets rather than
|
|
1458
|
+
12 over 16, its title is drawn at `--font-size-md`, and its body is inset 24px
|
|
1459
|
+
with an 8px stack gap. It also arrives properly: the frame travels its own
|
|
1460
|
+
width in from off screen with no opacity ramp, where it used to move 16px and
|
|
1461
|
+
fade, and its separation is cast on the INLINE axis through a new
|
|
1462
|
+
`--shadow-sheet` token rather than borrowing `--shadow-xl`, whose two vertical
|
|
1463
|
+
offsets both fall off the screen on a frame that runs the full height of the
|
|
1464
|
+
window. The veil under a sheet now fades over the same 300ms the sheet travels
|
|
1465
|
+
rather than 150ms. A dialog is unchanged in every one of those. What to
|
|
1466
|
+
change: nothing, unless a sheet was relying on its head being 49px tall.
|
|
1467
|
+
|
|
1468
|
+
**`ConfirmModal` is drawn as a prompt.** A confirmation is one question and one
|
|
1469
|
+
answer, and it used to carry a head band with a glyph and a close control that
|
|
1470
|
+
did exactly what the Cancel two inches below it did. It is 420px, centred, with
|
|
1471
|
+
no head band, no glyph and no close control: the title is the body's own first
|
|
1472
|
+
line and the actions hang off its bottom edge. It still names the frame, and a
|
|
1473
|
+
destructive one is still an `alertdialog`. `Modal` takes the same
|
|
1474
|
+
`shape="prompt"` directly, and `shape="framed"` is the way back for a
|
|
1475
|
+
confirmation that grew a form inside it. What to change: a confirmation that
|
|
1476
|
+
passed `size` to widen itself needs `shape="framed"` as well, since the prompt
|
|
1477
|
+
has a width of its own.
|
|
1478
|
+
|
|
1479
|
+
**The veil's blur is drawn again.** `Modal.css` declared `backdrop-filter` and
|
|
1480
|
+
then `-webkit-backdrop-filter` by hand, and a minifier answers a rule holding
|
|
1481
|
+
both by keeping the prefixed declaration and dropping the standard one: the
|
|
1482
|
+
shipped rule carried the prefixed spelling alone, which Chrome does not
|
|
1483
|
+
support, so the 3px softening behind every dialog and every sheet in the
|
|
1484
|
+
product computed to `none`. The hand-written prefix is gone; bundlers emit
|
|
1485
|
+
their own. Nothing needs an edit.
|
|
1486
|
+
|
|
1487
|
+
**`ListInput` is one card, and the box is above it.** The list was a stack of
|
|
1488
|
+
separate boxes, each a two-row textarea with Move up, Move down and Remove
|
|
1489
|
+
beside it: three items measured 256px where the console's own list draws 111,
|
|
1490
|
+
and the second row of every textarea was empty. It is a bordered card whose
|
|
1491
|
+
rows are divided by a rule, each row one growing line with its position drawn
|
|
1492
|
+
in the accent, and the new-item box sits above the list rather than below it,
|
|
1493
|
+
which is the order the two read in. The add control is an accent square beside
|
|
1494
|
+
the legend, where the console puts it and where the eye is when the list is
|
|
1495
|
+
empty. Move up and Move down are still there and still named, revealed by the
|
|
1496
|
+
pointer and by the focus rather than drawn on every row, and still on Alt+Up
|
|
1497
|
+
and Alt+Down from the item itself. What to change: `rows` is gone, since an
|
|
1498
|
+
item is one line that grows; `addLabel` is a function of the item's name
|
|
1499
|
+
(`addLabel={(name) => "Add " + name}`) rather than a fixed word, because
|
|
1500
|
+
the control is now an icon and its name is all a screen reader has.
|
|
1501
|
+
|
|
1502
|
+
**A tag's remove control reaches the pill's own outer edge.** It is a flex
|
|
1503
|
+
child stretched to the container's CONTENT box, which is the pill's 1px
|
|
1504
|
+
boundary shorter on each side, so it drew 22px inside the 24px a
|
|
1505
|
+
`--crewlet-tag-acts` pill is floored at: under the target floor the class
|
|
1506
|
+
beside it exists to hold. Nothing needs an edit, unless a stylesheet gave the
|
|
1507
|
+
control its own block margins.
|
|
1508
|
+
|
|
1509
|
+
**A `Modal` that submits is no longer a `<form>` with a dialog role on it.**
|
|
1510
|
+
A `<form>` accepts only `search`, `none` and `presentation` as an explicit
|
|
1511
|
+
role, so the frame this package drew for every surface that submits (the node
|
|
1512
|
+
editor, every confirmation) was a conformance failure axe reports on each one.
|
|
1513
|
+
The form is INSIDE the frame now, drawing no box of its own, so Enter from any
|
|
1514
|
+
control still submits and the head, the body and the footer are still the
|
|
1515
|
+
frame's own children. What to change: a stylesheet or a query that reached the
|
|
1516
|
+
frame as a `form` element; the frame is the element carrying `role="dialog"`,
|
|
1517
|
+
whichever way it submits.
|
|
1518
|
+
|
|
1519
|
+
**A row's control strip keeps its room whether or not it draws it.** The
|
|
1520
|
+
actions cell is end-aligned and the add pill sits before the strip, so a row
|
|
1521
|
+
drawing fewer controls dragged the plus along with it: measured on the engine's
|
|
1522
|
+
builder, the company's row draws no trash and no menu, its strip stood 28px
|
|
1523
|
+
against every other row's 92, and the plus a reader scans down the table for
|
|
1524
|
+
was 64px to the right of the one on the row beneath it. The strip is floored at
|
|
1525
|
+
`--crewlet-org-table-strip` (three control steps and their two gaps) and
|
|
1526
|
+
end-aligned inside it. Nothing needs an edit, unless a table draws more than
|
|
1527
|
+
three controls in a row's strip, which sets that variable.
|
|
1528
|
+
|
|
1529
|
+
### What to change
|
|
1530
|
+
|
|
1531
|
+
| Was | Is |
|
|
1532
|
+
|---|---|
|
|
1533
|
+
| `<IconButton aria-label="Add" />` | `<IconButton label="Add" />` (required) |
|
|
1534
|
+
| `icon="cached"` on a Tabs item, a DataTable column or a row action | `icon={<CachedGlyph />}` |
|
|
1535
|
+
| `<Popover>` holding a form or a panel | the same, plus `label`, which the type now requires for a dialog |
|
|
1536
|
+
| `<Popover>` whose children carry `menuitem` or `option` roles | `role="menu"` or `role="listbox"`, where `label` stays optional |
|
|
1537
|
+
| a stylesheet targeting `.material-symbols-outlined` inside a uilet component | `.crewlet-glyph` |
|
|
1538
|
+
| a stylesheet overriding a component's focus `box-shadow` | override `outline` |
|
|
1539
|
+
| a click handler relying on `Copyable`'s value span | press the copy control, which is the only one |
|
|
1540
|
+
| `<Card.Body padding="tight">` for the vertical-only step | `padding="sm"`; `tight` is the tile's 12px on every edge, as on the card |
|
|
1541
|
+
| a `<Card.Footer>` relied on for 8px over 16px from `padding="md"` | it is the default, and the default is `sm`; `md` is 16px on every edge |
|
|
1542
|
+
| `<ListInput rows={3}>` | nothing: an item is one line that grows with its text |
|
|
1543
|
+
| `<ListInput addLabel="Add a goal">` | `addLabel={(name) => "Add a " + name}` |
|
|
1544
|
+
| `<ConfirmModal size="md">` widened by hand | `<ConfirmModal size="md" shape="framed">`; the prompt shape has its own 420px |
|
|
1545
|
+
| a stylesheet overriding `.crewlet-modal__header--sheet` padding | the sheet's head is 64px with 16px over 24px insets; override `--size-shell-topbar` or the rule itself |
|
|
1546
|
+
| a `<Card.Footer padding="tight">` relied on for 4px over 16px | `padding="sm"`, or `none` and a rhythm of its own |
|
|
1547
|
+
| `padding` on a `Card.Header` | drop it, and reach for `divided` |
|
|
1548
|
+
| an `Accordion.Item` heading assumed to be `h3` | `headingLevel={3}`, or the level where it sits |
|
|
1549
|
+
| a stylesheet styling `.crewlet-canvas__controls` as the bar | `.crewlet-canvas__bar` |
|
|
1550
|
+
| a stylesheet or a test reading `.crewlet-canvas[data-animate='true']` | `'near'` for a fit, a zoom or a reveal; `'travel'` for a region ease |
|
|
1551
|
+
| `<OrgNodeLabel>` relied on for a trailing slot with no `trailing` passed | pass `trailing`, even empty, where a push can arrive |
|
|
1552
|
+
| a stylesheet reaching a chart node's name group as `.crewlet-org-node__icon`, `__text`, `__name`, `__caption`, `__kind` or `__trailing` | the same part under `.crewlet-org-label__`, which both surfaces now draw |
|
|
1553
|
+
| a stylesheet reaching a table row's name group as `.crewlet-org-table__icon`, `__text`, `__name`, `__caption`, `__kind` or `__trailing` | the same part under `.crewlet-org-label__` |
|
|
1554
|
+
| a stylesheet or a query reaching a row's name cell as `.crewlet-org-table__node` | `.crewlet-org-label--row`, which still carries `data-tone` |
|
|
1555
|
+
| a stylesheet reaching the add pill's disc as `.crewlet-add-pill__mark::before` | `.crewlet-add-pill__disc`, which is an element |
|
|
1556
|
+
| a stylesheet reaching a pill divider as `.crewlet-add-pill__section` `border-left` | `.crewlet-add-pill__divider`, which is an element between two sections |
|
|
1557
|
+
| a chart drawing its expander in `renderUnder` or in the actions column | `<OrgNodeDisclosure>`, beside the treeitem, on the card's leading edge |
|
|
1558
|
+
| `ref={...}` on `TreeCanvas` typed as `TreeViewHandle` | `TreeCanvasHandle` |
|
|
1559
|
+
| a query or a stylesheet reaching a submitting `Modal`'s frame as a `form` | the frame is the element with `role="dialog"`; the form is inside it |
|
|
1560
|
+
|
|
1561
|
+
| `<Tag variant="warn">` | `<Tag variant="warning">` |
|
|
1562
|
+
| `{ variant: 'warn' }` or `{ variant: 'error' }` on a toast | `'warning'` or `'danger'` |
|
|
1563
|
+
| a success toast with no `duration`, kept until dismissed | `duration={0}` |
|
|
1564
|
+
| a `<Callout>` that had to be announced | add `live="polite"`, or `role="alert"` |
|
|
1565
|
+
| `<Avatar colorSeed={id} />` | `<Avatar tone="seeded" colorSeed={id} />` |
|
|
1566
|
+
| `<Avatar />` at the old default size | `<Avatar size={64} />` |
|
|
1567
|
+
| `<Avatar />` where a round badge is wanted | `<Avatar shape="circle" />` |
|
|
1568
|
+
| `<ImageUpload />` where a round badge is wanted | `<ImageUpload shape="circle" />` |
|
|
1569
|
+
| `<Tag />` where the 28px pill is wanted | `<Tag size="md" />` |
|
|
1570
|
+
| a stylesheet that restyled `.crewlet-pricing-card__tag` | restyle `.crewlet-tag`, or pass a `variant` |
|
|
1571
|
+
| a layout that assumed a StatCard's value came first | the label comes first; read `.crewlet-statcard__value` by class |
|
|
1572
|
+
| `<StatCard tone="brand" />` | drop the tone, or pick the outcome it reports |
|
|
1573
|
+
| `<Skeleton variant="box" glowColor="green" />` | `<Skeleton variant="box" />` |
|
|
1574
|
+
|
|
1575
|
+
| `<Tabs>` whose sections have a panel | the same, plus `panelId`, with `<TabPanel id value>` around the section |
|
|
1576
|
+
| a `<Segmented>`-style row hand-built from buttons | `<SegmentedControl label semantics="radio" \| "tabs">` |
|
|
1577
|
+
| `<Checkbox onChange={(event) => …}>` | the same, or `onCheckedChange={(checked) => …}` |
|
|
1578
|
+
| a binary setting drawn as an On and Off `<Select>` | `<Switch label checked onCheckedChange>` |
|
|
1579
|
+
| an application that boots its theme from an inline script | `applyStoredPreferences({ themeKey, densityKey })` from the module bundle |
|
|
1580
|
+
| a component that announces its own changes in a live region of its own | mount one `<Announcer>` in the shell; `TagsInput` and `ListInput` speak through it |
|
|
1581
|
+
|
|
1582
|
+
| `<Modal labelledBy="heading-id">` | drop it, or `ariaLabel` for a surface with no title |
|
|
1583
|
+
| a stylesheet or query for `.crewlet-modal__footer > button` | `.crewlet-modal__footer-end > button` |
|
|
1584
|
+
| a query for the `h2` inside a dialog | `.crewlet-modal__title`, or the dialog's accessible name |
|
|
1585
|
+
| `<ConfirmModal submitting>` relied on for a closeable dialog | it is `dismissable={false}` now: close it from the caller |
|
|
1586
|
+
| `aria-label`, `aria-labelledby` or `aria-describedby` on a `Modal` | `ariaLabel` and `describedBy`, which the component actually reads |
|
|
1587
|
+
| a check that a `ConfirmModal` button is `disabled` | it is `aria-disabled` with a reason, and it keeps focus |
|
|
1588
|
+
|
|
1589
|
+
| `{ label: 'Delete', variant: 'danger' }` in `rowActions` | `{ label: 'Delete', danger: true }` |
|
|
1590
|
+
| `{ label: 'Publish', variant: 'primary' }` in `rowActions` | `{ label: 'Publish' }` (a menu has no headline step) |
|
|
1591
|
+
| a stylesheet targeting `.crewlet-data-table__row-actions-item` | `.crewlet-menu__item` |
|
|
1592
|
+
| `Record<string, DataTableColumn>` in TypeScript | `Record<string, DataTableColumn<MyRow>>` |
|
|
1593
|
+
| a `div` selector under `.crewlet-table` | the `tr` or `td` it is now |
|
|
1594
|
+
| `--crewlet-data-table-archived-tint-rgb: 196, 96, 96` | `--crewlet-data-table-archived-rail: <a colour>`, or nothing for the neutral rail |
|
|
1595
|
+
| a `paginated={false}` table relied on for its Previous / Next chevrons | `paginated`, or `onPageChange` with `pageCount` |
|
|
1596
|
+
| a `paginated={false}` table relied on for the page-size chips | `onItemsPerPageChange`, which is what says the number is read |
|
|
1597
|
+
| `itemsPerPage` read back as `data.length` after pressing All | `ALL_ITEMS` (`'all'`), in the prop, the callback and storage |
|
|
1598
|
+
| a settings frame dragged from anywhere in a column row | drag from the handle, which is a named button now |
|
|
1599
|
+
| a query for the drag handle as `.crewlet-data-table__column-drag-handle` glyph | it is an `IconButton` carrying that class |
|
|
1600
|
+
| a check that Move up is `disabled` at the top of the list | it is `aria-disabled` with a reason, and it keeps focus |
|
|
1601
|
+
| a query for the resize handle as a `button` | `role="separator"`, named the same |
|
|
1602
|
+
| a `variant="default"` table with no row actions, assumed to have no trailing column | it has one, holding the settings cog |
|
|
1603
|
+
| a settings frame expected to open onto a 350px column list | the list is as tall as its rows, capped at 40vh and scrolled |
|
|
1604
|
+
| `settingsVariant="compact"` or `"rich"`, on `DataTable` or `DataView` | drop it: every table opens one frame, and it holds the column list |
|
|
1605
|
+
| a `labels.pageSize` override | `labels.itemsPerPage`, which names the section on every table |
|
|
1606
|
+
| a titled table's settings frame expected at the `sm` dialog step | it is `lg`, because it carries the column list |
|
|
1607
|
+
| the toolbar's Columns button, reached by name or by click | open the settings frame and use its column list |
|
|
1608
|
+
| a `labels.columnsMenu` or `labels.columnsMenuTitle` override | drop it: the frame's section is `labels.columnsSection` |
|
|
1609
|
+
| a stylesheet for `.crewlet-data-table__columns-panel` or `__columns-fieldset` | drop it: neither is emitted |
|
|
1610
|
+
| a stylesheet targeting `.crewlet-data-table__toolbar-end` | `.crewlet-data-table__chrome`, which a card's header may hold |
|
|
1611
|
+
| the controlled pair passed with `showSettings={false}` | keep the cog, or draw your own control: the frame was the way in |
|
|
1612
|
+
| a `sticky: 'right'` table relied on never showing a scrollbar | it scrolls where the container is narrower than its column floors |
|
|
1613
|
+
| an empty table relied on for the fixed layout its rows are drawn under | it carries `crewlet-data-table__table--rowless` and is laid out by content |
|
|
1614
|
+
| a table relied on for widths measured against another container | it fits the container it is in, and a drag is what is remembered |
|
|
1615
|
+
| a query for `<storageKey>_columnWidths` in storage | `<storageKey>_resizedColumns`, holding only what a reader dragged |
|
|
1616
|
+
| `import { TableFooter }` or `TABLE_FOOTER_LABELS` | drop it: no table draws a count line |
|
|
1617
|
+
| `<DataView totalCount={n}>` | drop it, and say the number in the screen's header |
|
|
1618
|
+
| `<DataView pagination={<LoadOlder />}>` | `hasMore` with `onLoadMore`, which the table draws under its rows |
|
|
1619
|
+
| a `labels.footer` override on `DataView` | drop it: there is no sentence left to translate |
|
|
1620
|
+
|
|
1621
|
+
| `<Count>` in a `NavItem` badge, relied on for its own pill | it draws as a figure; pass `badgeTone="attention"` for a pill |
|
|
1622
|
+
| a stylesheet overriding `.crewlet-nav-item__row` to tint the current row | drop it: the row takes `--color-brand-accent-soft` and `--color-brand-accent-ink` |
|
|
1623
|
+
| a stylesheet reading `.crewlet-nav-item__row[aria-current='page']::before` | the bar is gone; the row's own tint is the mark |
|
|
1624
|
+
|
|
1625
|
+
| a `<Button block>` dropped into `AppShell.Rail`'s `footer` | nothing: the foot draws a direct-child button as a row on the rail's own insets. `<AppShell.RailRow>` is still the one to reach for, because it also decides whether the row is a control |
|
|
1626
|
+
| a stylesheet putting a dropped-in foot button back on a toolbar's inset | drop it: the foot's own rule is what the rail draws |
|
|
1627
|
+
| a stylesheet putting the rail's head divider back | nothing: the bar's line is the only one at that height |
|
|
1628
|
+
|
|
1629
|
+
| a stylesheet restyling a table's `th` | it is a sticky micro-label band on `--color-surface-subtle` with no column dividers |
|
|
1630
|
+
| a table row relied on for a hover tint | it lights up only with `onRowClick` or a row link |
|
|
1631
|
+
| `--crewlet-data-table-row-hover-bg` set to an opaque surface | an overlay step, or accept `--color-surface-hover` |
|
|
1632
|
+
| `--crewlet-data-table-sticky-bg` left unset on a table not on the panel surface | set it: the default is `--color-surface-subtle` now |
|
|
1633
|
+
| a selected row expected to carry `--shadow-selection` | the accent tint is the whole mark |
|
|
1634
|
+
| `.crewlet-table__container` styled as the table's frame | the frame is `.crewlet-table`; the container is the scroller |
|
|
1635
|
+
| a `Table` with many rows and a wrapper that scrolled it | `<Table maxHeight="24rem">`, which also makes the header stick |
|
|
1636
|
+
| a page relying on `PageHeader`'s 32px title | it is 20px over a 13px sentence; use `Text` for a display heading |
|
|
1637
|
+
| a query, a stylesheet or a test for `.crewlet-table-footer` | nothing emits it: no table draws a count line |
|
|
1638
|
+
| a `TreeCanvas` card drawing its own surface, border, radius or shadow | drop all four: `.crewlet-tree-canvas__card` draws them |
|
|
1639
|
+
| a `TreeCanvas` card drawing its own dashed edge for a human seat | `cardOutline={(id) => …}` |
|
|
1640
|
+
| a `TreeCanvas` treeitem drawing its own padding, focus ring or selection ring | drop all three |
|
|
1641
|
+
| a card placed on a `Canvas` painted `--color-surface-elevated` | `--color-surface-subtle`: the canvas is the page's ground now |
|
|
1642
|
+
| a stylesheet expecting the expanded detail row on `--color-surface-subtle` | it is `--color-surface-inset` |
|
|
1643
|
+
| a pagination or settings button hover expecting `--color-surface-elevated` | it is the `--color-surface-hover` overlay |
|
|
1644
|
+
| a stylesheet cancelling the last row's `border-bottom` | the table does it |
|
|
1645
|
+
| a `TreeCanvas` treeitem drawing its own column layout, gap or cursor | drop all three |
|
|
1646
|
+
| a `TreeCanvas` actions strip spreading `ctx.press(id)` with its own class and `aria-hidden` | `{...ctx.actions(id)}`, immediately after the node's treeitem |
|
|
1647
|
+
| a `TreeCanvas` card drawing its own two-column grid for that strip | drop it: the card is one column and the strip is drawn over its end |
|
|
1648
|
+
| a `TreeCanvas` consumer drawing an add control beside a card's name | `renderUnder`, which hangs it on the branch below the card |
|
|
1649
|
+
| a `TreeCanvas` consumer hanging a `Menu` under a card to add a child | `AddPill`, whose sections are the same list |
|
|
1650
|
+
| a `TreeCanvas` consumer opening a modal to name a child | `composing`, which draws that form in the chart where the child will go |
|
|
1651
|
+
| a chart of org chart nodes expecting each card under its own parent | `ranks="per-parent"`, or read a rank as a row |
|
|
1652
|
+
| `layoutForest` called with `{ gapX, gapY }` and expected to start at the origin | the same, plus `margin` where the frame clips |
|
|
1653
|
+
| a test reading a card's `transform` in the same tick as the change | read it after a frame, or from `layoutForest` |
|
|
1654
|
+
| `layoutConnectors(layout)` read as `string[]` | `{ id, parent, d }[]`; map to `.d` for the path, and `.id` is the node it arrives at |
|
|
1655
|
+
| a chart drawing its own compact org-chart node, its lead strip or its hue | `appearance="node"`, `cardTone`, `OrgNodeLabel` and `OrgNodeLead` |
|
|
1656
|
+
|
|
1657
|
+
| `<AppShell sidebarWidth={64}>` | `style={{ '--crewlet-app-shell-rail': '64px' }}` on the shell, or set `--size-shell-rail` |
|
|
1658
|
+
| `<AppShell topbarHeight={72}>` | `style={{ '--crewlet-app-shell-topbar': '72px' }}` on the shell |
|
|
1659
|
+
| a sidebar or header stylesheet with `position: fixed`, a width and a top offset | drop all three: the shell's grid places both slots |
|
|
1660
|
+
| a page that scrolled as a whole | it scrolls in `main`; pass `mainId` or `mainRef` to reach that scroller |
|
|
1661
|
+
| a `<Section>` relying on the 80px marketing band it defaulted to | `spacing="md"`; the default is `compact`, the console band |
|
|
1662
|
+
| a `<BarList emptyLabel>` relied on to carry an inset of its own | it starts where the bars start; the panel holding it carries the inset |
|
|
1663
|
+
| a `<Card>` with a header whose other children were relied on to sit flush | pass `padding="none"`; a card draws its own content in a body now |
|
|
1664
|
+
| a `<Card.Title>` relied on to wrap onto a second line | it is one line; the subtitle beside it gives up its tail first |
|
|
1665
|
+
| a `<Card padding="lg">` built from slots, relied on to pad the card itself | it pads what the card holds beside its slots; the slots still reach its edges |
|
|
1666
|
+
| an `<AutoGrid>` relied on to stretch a row that is short of cards | the tracks are kept; a lone card is drawn at a full card's width |
|
|
1667
|
+
| a `<Card variant="subtle">` relied on for the 16px inset | pass `padding="md"` |
|
|
1668
|
+
| a `<Card variant="subtle">` expected flush with the page | it is the panel's ground with no lift, at a 12px inset; `variant="outlined"` is the transparent one |
|
|
1669
|
+
| a `<Section>` description measured at 13px | it is `--font-size-xs` |
|
|
1670
|
+
| a `<PageHeader>`, `<Card.Title>`, `<Section>` or `<EmptyState>` title measured at 1.2 leading | it is `--font-line-height-normal` |
|
|
1671
|
+
| a `<Section>` description measured at 13px | it is `--font-size-xs` |
|
|
1672
|
+
| a `<PageHeader>`, `<Card.Title>`, `<Section>` or `<EmptyState>` title measured at 1.2 leading | it is `--font-line-height-normal` |
|
|
1673
|
+
| a stylesheet matching the pill chip's 6px corner | it is `--radius-chip`, one hairline inside the well |
|
|
1674
|
+
| a pill `<Tabs>` or `<SegmentedControl>` chip measured at a control step | it is 26px, and 24px at `size="sm"` |
|
|
1675
|
+
| a pill or underline tab narrower than 24px, which a one-letter label or a bare glyph drew | every tab carries `min-width: var(--size-target-min)` |
|
|
1676
|
+
| a pill chip's content aligned to the start | it is centred; the segmented CARD row is the one that starts at its own inset |
|
|
1677
|
+
| a `size="sm"` pill chip measured at 8px of side padding with a 14px glyph | 4px and 12px, so a rail's foot reads quieter |
|
|
1678
|
+
|
|
1679
|
+
**Card is the engine dashboard's panel (C1).** A card built from
|
|
1680
|
+
`Card.Header`, `Card.Body` or `Card.Footer` is FLUSH: it carries no padding of
|
|
1681
|
+
its own, the slots carry theirs at the panel's inset, and the card clips them
|
|
1682
|
+
to its radius so the header's rule and a table's first row reach the edges. A
|
|
1683
|
+
card given raw children is still padded, at `--spacing-4` rather than the
|
|
1684
|
+
`--spacing-5`/`--spacing-6` block it was. `Card.Header` draws its rule unless
|
|
1685
|
+
`divided={false}`, renders its subtitle beside the title rather than under it,
|
|
1686
|
+
and no longer wraps the title in `.crewlet-card__header-text`. Its row is ONE
|
|
1687
|
+
LINE: the title never wraps, and the subtitle absorbs the shrinking, so a head
|
|
1688
|
+
one pixel too narrow gives up the subtitle's tail rather than the title's. Two grounds are
|
|
1689
|
+
new, `variant="inset"` and `variant="quiet"`.
|
|
1690
|
+
|
|
1691
|
+
A card's OWN content, where it holds any beside those slots, is drawn in a body
|
|
1692
|
+
of its own at the inset `padding` names, so a sentence written straight under a
|
|
1693
|
+
`Card.Header` starts on the same line as the title rather than against the
|
|
1694
|
+
border. `padding` on a flush card never goes back onto the card itself: the
|
|
1695
|
+
slots have to reach its edges, and `padding="none"` leaves that content exactly
|
|
1696
|
+
as it was written, for a table.
|
|
1697
|
+
|
|
1698
|
+
Every padding step moved with it, so a card that names one renders differently
|
|
1699
|
+
too: `sm` is 8px over 16px (was 12 over 16), `md` is 16px (was 20 over 24) and
|
|
1700
|
+
`lg` is 20px over 24px (was 24 over 32). The section steps are those same five
|
|
1701
|
+
now, rather than a scale of three on which `tight` meant a vertical-only step
|
|
1702
|
+
and `md` meant one inset on a body and another on a footer. And a `default` card that is NOT built
|
|
1703
|
+
from slots now LIFTS, at `--shadow-xs` with the dark theme's hairline along its
|
|
1704
|
+
top edge, which is what the dashboard's own panel carries; a flush one stays
|
|
1705
|
+
flat, because its rule and its slots are what give it depth. Slots reached
|
|
1706
|
+
through a fragment count as slots, so the flush recipe survives the ordinary
|
|
1707
|
+
`{ready ? <><Card.Header /><Card.Body /></> : <Skeleton />}`.
|
|
1708
|
+
|
|
1709
|
+
**The type registers move onto the dashboard's values (C1).** `Eyebrow` is THE
|
|
1710
|
+
micro-label now, the same register `Text variant="label"` and a table's column
|
|
1711
|
+
head take: 11px, medium, the wide track, uppercase, tertiary, in the document's
|
|
1712
|
+
own face. It was monospace, one size up, semibold and on the wider track, and
|
|
1713
|
+
`variant="accent"` spent the accent FILL on a word, which clears 3:1 as a mark
|
|
1714
|
+
and not the 4.5:1 a word owes; it paints `--color-brand-accent-ink` now.
|
|
1715
|
+
`Text variant="heading"` and a `Section` title track tighter, a `Section`
|
|
1716
|
+
description is 13px with a measure rather than 12px truncated, and `Container`'s
|
|
1717
|
+
gutter is 20px, 12px under 480px.
|
|
1718
|
+
|
|
1719
|
+
**Rows, expanders and records (C1).** `List` renders `variant="divided"` unless
|
|
1720
|
+
told otherwise: it is the list every screen draws, and `plain` is one word away.
|
|
1721
|
+
A `Disclosure` head is 12px with a 4px gap, its `size="compact"` no longer
|
|
1722
|
+
restates that register, and `variant="aside"` rules its panel in
|
|
1723
|
+
`--color-border-hover`. An `Accordion` trigger is semibold on the tight track
|
|
1724
|
+
and its compact trigger lands on the disclosure head's own register. A
|
|
1725
|
+
`Timeline` step takes `--radius-md`. `DescriptionList` was already the
|
|
1726
|
+
dashboard's own key/value grid and is unchanged.
|
|
1727
|
+
|
|
1728
|
+
**Code, chips and keycaps (C1).** `CodeBlock` WRAPS unless `wrap={false}`, and
|
|
1729
|
+
its block is 12px on `--radius-md` in `--color-text-secondary` at `--spacing-3`,
|
|
1730
|
+
with a header that is a rule rather than a second ground. `InlineCode`
|
|
1731
|
+
`tone="inherit"` keeps the inset chip and takes only the message's ink (the
|
|
1732
|
+
composite is measured by `@crewlethq/tokens`, tightest pair 4.61:1), and
|
|
1733
|
+
`variant="reference"` is `--color-text-secondary`. `Kbd` is
|
|
1734
|
+
`--color-text-tertiary` on `--color-surface-inset` at regular weight.
|
|
1735
|
+
`Copyable`'s value is `--color-text-secondary`, its chip takes the chip's own
|
|
1736
|
+
padding and its block the `--color-surface-subtle` surface.
|
|
1737
|
+
|
|
1738
|
+
| was | is now |
|
|
1739
|
+
| --- | ------ |
|
|
1740
|
+
| `<Card padding="none">` with slots, plus inline padding on each slot | drop both: a card built from slots is flush and its slots are inset |
|
|
1741
|
+
| `<Card padding="sm">` or `padding="lg"` laid out against the old numbers | `sm` is 8px over 16px and `lg` is 20px over 24px |
|
|
1742
|
+
| a `default` card a stylesheet assumed was flat | it carries `--shadow-xs` and the hairline unless it is built from slots |
|
|
1743
|
+
| a `Card.Title` holding a phrase with inline markup in it | the title is a flex row at `--spacing-2`, so give it one run of text and put a mark beside it |
|
|
1744
|
+
| `<Card.Header divided>` | drop it, the rule is the default; `divided={false}` for a card whose body is one paragraph |
|
|
1745
|
+
| a stylesheet or query for `.crewlet-card__header-text` | the title and the subtitle are siblings in `.crewlet-card__header` |
|
|
1746
|
+
| a stylesheet for `.crewlet-card__header--divided` | `.crewlet-card__header--plain`, on the opposite condition |
|
|
1747
|
+
| a plain `<List>` relied on for an undivided stack | `<List variant="plain">` |
|
|
1748
|
+
| `<CodeBlock wrap>` | drop it, wrapping is the default |
|
|
1749
|
+
| `<CodeBlock>` relied on for a sideways scroll | `<CodeBlock wrap={false}>` |
|
|
1750
|
+
| a stylesheet for `.crewlet-codeblock--wrap` | `.crewlet-codeblock--nowrap`, on the opposite condition |
|
|
1751
|
+
| an `Eyebrow` relied on for a monospace kicker | `<Text variant="label" mono>` |
|
|
1752
|
+
| a stylesheet keyed on `.crewlet-input:focus-within` or on a field's focus `box-shadow` | `.crewlet-input:has(.crewlet-input__control:focus-visible)`, and the ring is an `outline` |
|
|
1753
|
+
| a stylesheet keyed on `.crewlet-select__trigger:focus-visible` for the select's ring | `.crewlet-select:focus-within`, which is the frame a reader reads as the field |
|
|
1754
|
+
| a stylesheet keyed on `.crewlet-switch__control:focus-visible` | `.crewlet-switch__track:has(.crewlet-switch__control:focus-visible)` |
|
|
1755
|
+
|
|
1756
|
+
| `<OrgTableAdd options={[{ label, icon, disabledReason, onSelect }]} />` | `<OrgTableAdd sections={[{ label, icon, disabled, onSelect }]} />`, which is `AddPill`'s own list |
|
|
1757
|
+
| an `<OrgTableAdd>` rendered inside `<OrgTableActions>` | a sibling before it, so the plus is drawn at rest |
|
|
1758
|
+
| a stylesheet targeting `.crewlet-org-table__pill` or `.crewlet-org-table__pill-btn` | `.crewlet-add-pill__sections` and `.crewlet-add-pill__section` |
|
|
1759
|
+
| a table whose add offers a number of kinds other than three | set `--crewlet-org-table-add-kinds` on `.crewlet-org-table` |
|
|
1760
|
+
| a layout measured against the org table's 20px indent or 36px row | 40px a level and 48px a row |
|
|
61
1761
|
|
|
62
1762
|
## Why CSS, not CSS-in-JS
|
|
63
1763
|
|