@thewhatmatters/wmds 0.4.9 → 0.4.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +303 -0
- package/CONSUMING.md +62 -1
- package/README.md +1 -1
- package/bin/wmds-check.mjs +47 -0
- package/dist/colors.css +92 -0
- package/dist/components/atoms/Badge/Badge.d.ts +13 -5
- package/dist/components/atoms/Badge/Badge.d.ts.map +1 -1
- package/dist/components/atoms/Badge/Badge.js +56 -48
- package/dist/components/atoms/Badge/badgeStyles.d.ts +15 -0
- package/dist/components/atoms/Badge/badgeStyles.d.ts.map +1 -1
- package/dist/components/atoms/Badge/badgeStyles.js +42 -20
- package/dist/components/atoms/Band/Band.d.ts +27 -0
- package/dist/components/atoms/Band/Band.d.ts.map +1 -0
- package/dist/components/atoms/Band/Band.js +22 -0
- package/dist/components/atoms/Band/bandStyles.d.ts +20 -0
- package/dist/components/atoms/Band/bandStyles.d.ts.map +1 -0
- package/dist/components/atoms/Band/bandStyles.js +23 -0
- package/dist/components/atoms/Button/Button.d.ts +1 -1
- package/dist/components/atoms/Button/Button.d.ts.map +1 -1
- package/dist/components/atoms/Button/Button.js +67 -48
- package/dist/components/atoms/Button/ButtonStatusButton.d.ts.map +1 -1
- package/dist/components/atoms/Button/ButtonStatusButton.js +12 -5
- package/dist/components/atoms/Button/buttonStatusMotion.d.ts.map +1 -1
- package/dist/components/atoms/Button/buttonStatusMotion.js +6 -1
- package/dist/components/atoms/Button/buttonStyles.d.ts +2 -2
- package/dist/components/atoms/Button/buttonStyles.d.ts.map +1 -1
- package/dist/components/atoms/Button/buttonStyles.js +16 -14
- package/dist/components/atoms/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/atoms/Checkbox/Checkbox.js +69 -68
- package/dist/components/atoms/Checkbox/checkboxStyles.d.ts +1 -1
- package/dist/components/atoms/Checkbox/checkboxStyles.d.ts.map +1 -1
- package/dist/components/atoms/Checkbox/checkboxStyles.js +1 -1
- package/dist/components/atoms/IconButton/IconButton.d.ts.map +1 -1
- package/dist/components/atoms/IconButton/IconButton.js +13 -12
- package/dist/components/atoms/IconButton/iconButtonStyles.d.ts +2 -2
- package/dist/components/atoms/IconButton/iconButtonStyles.d.ts.map +1 -1
- package/dist/components/atoms/IconButton/iconButtonStyles.js +1 -1
- package/dist/components/atoms/InlinePill/InlinePill.d.ts +42 -0
- package/dist/components/atoms/InlinePill/InlinePill.d.ts.map +1 -0
- package/dist/components/atoms/InlinePill/InlinePill.js +48 -0
- package/dist/components/atoms/InlinePill/inlinePillStyles.d.ts +27 -0
- package/dist/components/atoms/InlinePill/inlinePillStyles.d.ts.map +1 -0
- package/dist/components/atoms/InlinePill/inlinePillStyles.js +19 -0
- package/dist/components/atoms/Input/Input.d.ts +9 -4
- package/dist/components/atoms/Input/Input.d.ts.map +1 -1
- package/dist/components/atoms/Input/Input.js +59 -59
- package/dist/components/atoms/Input/inputShellStyles.d.ts +10 -4
- package/dist/components/atoms/Input/inputShellStyles.d.ts.map +1 -1
- package/dist/components/atoms/Input/inputShellStyles.js +40 -40
- package/dist/components/atoms/RiveHand/RiveHand.d.ts.map +1 -1
- package/dist/components/atoms/RiveHand/RiveHand.js +83 -81
- package/dist/components/atoms/Skeleton/skeletonStyles.js +1 -1
- package/dist/components/atoms/Switch/Switch.d.ts.map +1 -1
- package/dist/components/atoms/Switch/Switch.js +45 -44
- package/dist/components/atoms/Switch/switchStyles.d.ts +1 -1
- package/dist/components/atoms/Switch/switchStyles.d.ts.map +1 -1
- package/dist/components/atoms/Switch/switchStyles.js +1 -1
- package/dist/components/atoms/Table/Table.d.ts +54 -0
- package/dist/components/atoms/Table/Table.d.ts.map +1 -0
- package/dist/components/atoms/Table/Table.js +66 -0
- package/dist/components/atoms/Table/tableStyles.d.ts +18 -0
- package/dist/components/atoms/Table/tableStyles.d.ts.map +1 -0
- package/dist/components/atoms/Table/tableStyles.js +14 -0
- package/dist/components/atoms/TextArea/TextArea.d.ts +6 -0
- package/dist/components/atoms/TextArea/TextArea.d.ts.map +1 -1
- package/dist/components/atoms/TextArea/TextArea.js +86 -78
- package/dist/components/atoms/TextArea/textareaShellStyles.d.ts +2 -2
- package/dist/components/atoms/TextArea/textareaShellStyles.d.ts.map +1 -1
- package/dist/components/atoms/TextArea/textareaShellStyles.js +5 -5
- package/dist/components/molecules/Accordion/accordionStyles.d.ts +1 -1
- package/dist/components/molecules/Accordion/accordionStyles.d.ts.map +1 -1
- package/dist/components/molecules/Accordion/accordionStyles.js +1 -1
- package/dist/components/molecules/Breadcrumb/Breadcrumb.d.ts.map +1 -1
- package/dist/components/molecules/Breadcrumb/Breadcrumb.js +80 -122
- package/dist/components/molecules/Card/Card.d.ts +28 -6
- package/dist/components/molecules/Card/Card.d.ts.map +1 -1
- package/dist/components/molecules/Card/Card.js +83 -42
- package/dist/components/molecules/Card/cardStyles.d.ts +16 -1
- package/dist/components/molecules/Card/cardStyles.d.ts.map +1 -1
- package/dist/components/molecules/Card/cardStyles.js +19 -5
- package/dist/components/molecules/ChatQa/chatQaStyles.d.ts +1 -1
- package/dist/components/molecules/ChatQa/chatQaStyles.d.ts.map +1 -1
- package/dist/components/molecules/ChatQa/chatQaStyles.js +1 -1
- package/dist/components/molecules/Chip/chipStyles.js +9 -9
- package/dist/components/molecules/CopyField/CopyField.d.ts +41 -0
- package/dist/components/molecules/CopyField/CopyField.d.ts.map +1 -0
- package/dist/components/molecules/CopyField/CopyField.js +120 -0
- package/dist/components/molecules/CopyField/copyFieldStyles.d.ts +28 -0
- package/dist/components/molecules/CopyField/copyFieldStyles.d.ts.map +1 -0
- package/dist/components/molecules/CopyField/copyFieldStyles.js +7 -0
- package/dist/components/molecules/DisplayControls/DisplayControls.d.ts +16 -7
- package/dist/components/molecules/DisplayControls/DisplayControls.d.ts.map +1 -1
- package/dist/components/molecules/DisplayControls/DisplayControls.js +41 -39
- package/dist/components/molecules/DisplayControls/displayControlsState.d.ts +21 -0
- package/dist/components/molecules/DisplayControls/displayControlsState.d.ts.map +1 -1
- package/dist/components/molecules/DisplayControls/displayControlsState.js +46 -4
- package/dist/components/molecules/Dropdown/Dropdown.d.ts +1 -1
- package/dist/components/molecules/Dropdown/DropdownItem.d.ts +2 -2
- package/dist/components/molecules/Dropdown/DropdownItem.d.ts.map +1 -1
- package/dist/components/molecules/Dropdown/DropdownItem.js +30 -27
- package/dist/components/molecules/Dropdown/dropdownStyles.js +1 -1
- package/dist/components/molecules/Dropdown/resolveOverflow.d.ts +14 -0
- package/dist/components/molecules/Dropdown/resolveOverflow.d.ts.map +1 -0
- package/dist/components/{organisms/Tab/tabOverflow.js → molecules/Dropdown/resolveOverflow.js} +2 -2
- package/dist/components/molecules/Dropdown/useDropdownMenu.d.ts +70 -0
- package/dist/components/molecules/Dropdown/useDropdownMenu.d.ts.map +1 -0
- package/dist/components/molecules/Dropdown/useDropdownMenu.js +155 -0
- package/dist/components/molecules/Dropdown/useOverflowMenu.d.ts +71 -0
- package/dist/components/molecules/Dropdown/useOverflowMenu.d.ts.map +1 -0
- package/dist/components/molecules/Dropdown/useOverflowMenu.js +61 -0
- package/dist/components/molecules/Field/Field.d.ts.map +1 -1
- package/dist/components/molecules/Field/Field.js +11 -10
- package/dist/components/molecules/LogView/LogView.d.ts +31 -0
- package/dist/components/molecules/LogView/LogView.d.ts.map +1 -0
- package/dist/components/molecules/LogView/LogView.js +109 -0
- package/dist/components/molecules/LogView/logViewStyles.d.ts +11 -0
- package/dist/components/molecules/LogView/logViewStyles.d.ts.map +1 -0
- package/dist/components/molecules/LogView/logViewStyles.js +6 -0
- package/dist/components/molecules/NavList/NavList.d.ts +23 -4
- package/dist/components/molecules/NavList/NavList.d.ts.map +1 -1
- package/dist/components/molecules/NavList/NavList.js +65 -52
- package/dist/components/molecules/NavList/navListStyles.d.ts +2 -0
- package/dist/components/molecules/NavList/navListStyles.d.ts.map +1 -1
- package/dist/components/molecules/NavList/navListStyles.js +3 -3
- package/dist/components/molecules/PageHeader/PageHeader.js +3 -3
- package/dist/components/molecules/PillGroup/pillGroupStyles.js +1 -1
- package/dist/components/molecules/Search/searchStyles.js +1 -1
- package/dist/components/molecules/SegmentedControl/segmentedControlStyles.d.ts +1 -1
- package/dist/components/molecules/SegmentedControl/segmentedControlStyles.d.ts.map +1 -1
- package/dist/components/molecules/SegmentedControl/segmentedControlStyles.js +8 -8
- package/dist/components/molecules/Select/Select.d.ts +3 -1
- package/dist/components/molecules/Select/Select.d.ts.map +1 -1
- package/dist/components/molecules/Select/Select.js +75 -119
- package/dist/components/molecules/Select/selectStyles.js +3 -3
- package/dist/components/molecules/Stat/Stat.d.ts +31 -5
- package/dist/components/molecules/Stat/Stat.d.ts.map +1 -1
- package/dist/components/molecules/Stat/Stat.js +83 -49
- package/dist/components/molecules/Stat/statStyles.d.ts +24 -4
- package/dist/components/molecules/Stat/statStyles.d.ts.map +1 -1
- package/dist/components/molecules/Stat/statStyles.js +30 -13
- package/dist/components/molecules/TaskRows/TaskRows.d.ts +13 -3
- package/dist/components/molecules/TaskRows/TaskRows.d.ts.map +1 -1
- package/dist/components/molecules/TaskRows/TaskRows.js +81 -57
- package/dist/components/molecules/TaskRows/TaskRowsTable.d.ts +54 -0
- package/dist/components/molecules/TaskRows/TaskRowsTable.d.ts.map +1 -0
- package/dist/components/molecules/TaskRows/TaskRowsTable.js +200 -0
- package/dist/components/molecules/TaskRows/taskRowsStyles.d.ts +19 -1
- package/dist/components/molecules/TaskRows/taskRowsStyles.d.ts.map +1 -1
- package/dist/components/molecules/TaskRows/taskRowsStyles.js +7 -3
- package/dist/components/molecules/TextSequence/TextSequence.d.ts.map +1 -1
- package/dist/components/molecules/TextSequence/TextSequence.js +35 -35
- package/dist/components/organisms/AppShell/AppShell.d.ts +84 -0
- package/dist/components/organisms/AppShell/AppShell.d.ts.map +1 -0
- package/dist/components/organisms/AppShell/AppShell.js +231 -0
- package/dist/components/organisms/AppShell/appShellStyles.d.ts +20 -0
- package/dist/components/organisms/AppShell/appShellStyles.d.ts.map +1 -0
- package/dist/components/organisms/AppShell/appShellStyles.js +8 -0
- package/dist/components/organisms/Carousel/Carousel.d.ts +9 -1
- package/dist/components/organisms/Carousel/Carousel.d.ts.map +1 -1
- package/dist/components/organisms/Carousel/Carousel.js +220 -182
- package/dist/components/organisms/Carousel/carouselMath.d.ts +5 -0
- package/dist/components/organisms/Carousel/carouselMath.d.ts.map +1 -1
- package/dist/components/organisms/Carousel/carouselMath.js +9 -1
- package/dist/components/organisms/Carousel/carouselStyles.d.ts +2 -0
- package/dist/components/organisms/Carousel/carouselStyles.d.ts.map +1 -1
- package/dist/components/organisms/Carousel/carouselStyles.js +8 -7
- package/dist/components/organisms/Chart/Chart.d.ts +5 -1
- package/dist/components/organisms/Chart/Chart.d.ts.map +1 -1
- package/dist/components/organisms/Chart/Chart.js +126 -62
- package/dist/components/organisms/Chart/ChartLegend.d.ts +8 -1
- package/dist/components/organisms/Chart/ChartLegend.d.ts.map +1 -1
- package/dist/components/organisms/Chart/ChartLegend.js +43 -13
- package/dist/components/organisms/Chart/ChartSegmentedRing.d.ts +52 -0
- package/dist/components/organisms/Chart/ChartSegmentedRing.d.ts.map +1 -0
- package/dist/components/organisms/Chart/ChartSegmentedRing.js +95 -0
- package/dist/components/organisms/Chart/chartStyles.d.ts +32 -0
- package/dist/components/organisms/Chart/chartStyles.d.ts.map +1 -1
- package/dist/components/organisms/Chart/chartStyles.js +15 -3
- package/dist/components/organisms/ChatDock/ChatDock.d.ts.map +1 -1
- package/dist/components/organisms/ChatDock/ChatDock.js +29 -25
- package/dist/components/organisms/Dialog/AlertDialog.d.ts +7 -1
- package/dist/components/organisms/Dialog/AlertDialog.d.ts.map +1 -1
- package/dist/components/organisms/Dialog/AlertDialog.js +54 -50
- package/dist/components/organisms/Dialog/Dialog.d.ts.map +1 -1
- package/dist/components/organisms/Dialog/Dialog.js +52 -71
- package/dist/components/organisms/Dialog/OverlayPanelHeader.d.ts.map +1 -1
- package/dist/components/organisms/Dialog/OverlayPanelHeader.js +1 -0
- package/dist/components/organisms/Dialog/dialogStyles.d.ts +14 -1
- package/dist/components/organisms/Dialog/dialogStyles.d.ts.map +1 -1
- package/dist/components/organisms/Dialog/dialogStyles.js +3 -3
- package/dist/components/organisms/FilterPanel/FilterPanel.d.ts.map +1 -1
- package/dist/components/organisms/FilterPanel/FilterPanel.js +52 -42
- package/dist/components/organisms/IntakeModal/IntakeModal.d.ts +6 -1
- package/dist/components/organisms/IntakeModal/IntakeModal.d.ts.map +1 -1
- package/dist/components/organisms/IntakeModal/IntakeModal.js +54 -66
- package/dist/components/organisms/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/components/organisms/MoreMenu/MoreMenu.js +45 -101
- package/dist/components/organisms/Panel/Panel.d.ts.map +1 -1
- package/dist/components/organisms/Panel/Panel.js +44 -53
- package/dist/components/organisms/Sheet/Sheet.d.ts +3 -1
- package/dist/components/organisms/Sheet/Sheet.d.ts.map +1 -1
- package/dist/components/organisms/Sheet/Sheet.js +66 -81
- package/dist/components/organisms/Sheet/sheetStyles.d.ts +8 -2
- package/dist/components/organisms/Sheet/sheetStyles.d.ts.map +1 -1
- package/dist/components/organisms/Sheet/sheetStyles.js +16 -11
- package/dist/components/organisms/SiteNav/SiteNav.d.ts +0 -9
- package/dist/components/organisms/SiteNav/SiteNav.d.ts.map +1 -1
- package/dist/components/organisms/SiteNav/SiteNav.js +236 -286
- package/dist/components/organisms/SiteNav/siteNavStyles.js +1 -1
- package/dist/components/organisms/Tab/Tab.d.ts.map +1 -1
- package/dist/components/organisms/Tab/Tab.js +77 -127
- package/dist/components/organisms/Tab/tabStyles.d.ts +2 -1
- package/dist/components/organisms/Tab/tabStyles.d.ts.map +1 -1
- package/dist/components/organisms/Tab/tabStyles.js +7 -7
- package/dist/fonts.css +10 -0
- package/dist/grid.css +38 -0
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +152 -139
- package/dist/lib/chartTheme.d.ts +11 -0
- package/dist/lib/chartTheme.d.ts.map +1 -1
- package/dist/lib/compositionAudit.d.ts.map +1 -1
- package/dist/lib/contrast.d.ts +11 -0
- package/dist/lib/contrast.d.ts.map +1 -0
- package/dist/lib/dialogOverlay.d.ts +34 -1
- package/dist/lib/dialogOverlay.d.ts.map +1 -1
- package/dist/lib/dialogOverlay.js +100 -23
- package/dist/lib/sheetParam/index.d.ts +3 -0
- package/dist/lib/sheetParam/index.d.ts.map +1 -0
- package/dist/lib/sheetParam/sheetParam.d.ts +15 -0
- package/dist/lib/sheetParam/sheetParam.d.ts.map +1 -0
- package/dist/lib/sheetParam/sheetParam.js +26 -0
- package/dist/lib/sheetParam/useSheetParam.d.ts +25 -0
- package/dist/lib/sheetParam/useSheetParam.d.ts.map +1 -0
- package/dist/lib/sheetParam/useSheetParam.js +29 -0
- package/dist/lib/typography.d.ts +1 -1
- package/dist/lib/typography.d.ts.map +1 -1
- package/dist/shape.css +24 -3
- package/dist/styles.css +1 -1
- package/dist/typography.css +55 -8
- package/docs/README.md +1 -1
- package/docs/component-contracts.md +101 -0
- package/docs/components.md +19 -12
- package/docs/exports.json +142 -15
- package/docs/patterns/components-accordion--flush.tsx +1 -1
- package/docs/patterns/components-appshell--portal-dashboard-pattern.tsx +176 -0
- package/docs/patterns/components-avatar--dropdown-row.tsx +1 -1
- package/docs/patterns/components-badge--outline-tags.tsx +1 -1
- package/docs/patterns/components-badge--status-pill.tsx +31 -0
- package/docs/patterns/components-badge--with-avatar.tsx +1 -1
- package/docs/patterns/components-band--page-in-bands.tsx +39 -0
- package/docs/patterns/components-breadcrumb--editorial.tsx +1 -1
- package/docs/patterns/components-breadcrumb--long-path.tsx +1 -1
- package/docs/patterns/components-breadcrumb--path.tsx +1 -1
- package/docs/patterns/components-breadcrumb--router-links.tsx +1 -1
- package/docs/patterns/components-button-button--copy-button.tsx +1 -1
- package/docs/patterns/components-button-button--destructive-action.tsx +1 -1
- package/docs/patterns/components-button-button--external-link.tsx +1 -1
- package/docs/patterns/components-button-button--ghost-action.tsx +1 -1
- package/docs/patterns/components-button-button--inverse-action.tsx +1 -1
- package/docs/patterns/components-button-button--link-render.tsx +1 -1
- package/docs/patterns/components-button-button--multi-state-badge.tsx +1 -1
- package/docs/patterns/components-button-button--outline-mono.tsx +1 -1
- package/docs/patterns/components-button-button--primary-action.tsx +1 -1
- package/docs/patterns/components-button-button--row-hug.tsx +1 -1
- package/docs/patterns/components-button-button--row-layout.tsx +1 -1
- package/docs/patterns/components-button-button--secondary-action.tsx +1 -1
- package/docs/patterns/components-button-button--suggestion-pills.tsx +1 -1
- package/docs/patterns/components-button-button--with-count.tsx +1 -1
- package/docs/patterns/components-button-button--with-icon.tsx +1 -1
- package/docs/patterns/components-button-floatingactionbutton--vertical-actions.tsx +1 -1
- package/docs/patterns/components-calembed--pattern-skip-in-footer.tsx +1 -1
- package/docs/patterns/components-card-card--body-slot-occupancy-history.tsx +1 -1
- package/docs/patterns/components-card-card--headerless-media.tsx +1 -1
- package/docs/patterns/components-card-card--outlined-layout.tsx +1 -1
- package/docs/patterns/components-card-card--pinned-height.tsx +1 -1
- package/docs/patterns/components-card-card--terminal-frame.tsx +48 -0
- package/docs/patterns/components-card-selectablecard--selectable-cards-pattern.tsx +1 -1
- package/docs/patterns/components-carousel--image-carousel.tsx +1 -1
- package/docs/patterns/components-chart--audience-activity-heatmap.tsx +1 -1
- package/docs/patterns/components-chart--cartesian-no-data-gaps.tsx +1 -1
- package/docs/patterns/components-chart--occupancy-history-in-card.tsx +1 -1
- package/docs/patterns/components-chart--occupancy-in-card.tsx +1 -1
- package/docs/patterns/components-chart--post-reach-distribution.tsx +1 -1
- package/docs/patterns/components-chart--ranked-audience-breakdown.tsx +1 -1
- package/docs/patterns/components-chart--revenue-by-line.tsx +68 -0
- package/docs/patterns/components-chart--segmented-ring-pattern.tsx +144 -0
- package/docs/patterns/components-chart--unit-grid-composition.tsx +1 -1
- package/docs/patterns/components-chatdock--pattern-chat-dock.tsx +1 -1
- package/docs/patterns/components-chatdock--pattern-start-project-gate.tsx +1 -1
- package/docs/patterns/components-chatqa--pattern-qa-with-reply.tsx +1 -1
- package/docs/patterns/components-checkbox-checkbox--default.tsx +1 -1
- package/docs/patterns/components-checkbox-checkbox--with-count.tsx +1 -1
- package/docs/patterns/components-checkbox-checkbox--with-description.tsx +1 -1
- package/docs/patterns/components-checkbox-checkboxgroup--default.tsx +1 -1
- package/docs/patterns/components-checkbox-checkboxgroup--option-counts.tsx +1 -1
- package/docs/patterns/components-copyfield--clipboard-blocked.tsx +9 -0
- package/docs/patterns/components-copyfield--copied.tsx +9 -0
- package/docs/patterns/components-copyfield--copy-key.tsx +9 -0
- package/docs/patterns/components-copyfield--on-navy.tsx +29 -0
- package/docs/patterns/components-descriptionlist--details.tsx +1 -1
- package/docs/patterns/components-descriptionlist--post-metadata.tsx +1 -1
- package/docs/patterns/components-dialog--alert-dialog-destructive.tsx +1 -1
- package/docs/patterns/components-dialog--alert-dialog-typed-confirmation.tsx +54 -0
- package/docs/patterns/components-dialog--default-dialog.tsx +1 -1
- package/docs/patterns/components-dialog--form-dialog.tsx +1 -1
- package/docs/patterns/components-displaycontrols--page-utilities.tsx +1 -1
- package/docs/patterns/components-displaycontrols--theme-switch.tsx +35 -0
- package/docs/patterns/components-field--horizontal-input.tsx +1 -1
- package/docs/patterns/components-field--horizontal-text-area.tsx +1 -1
- package/docs/patterns/components-field--multi-control.tsx +1 -1
- package/docs/patterns/components-filterpanel--panel.tsx +1 -1
- package/docs/patterns/components-footerreveal--marketing-hero-pattern.tsx +1 -1
- package/docs/patterns/components-footerreveal--marketing-hero-ruled-pattern.tsx +1 -1
- package/docs/patterns/components-footerreveal--marketing-hero-with-gallery-intro.tsx +1 -1
- package/docs/patterns/components-footerreveal--marketing-page-pattern.tsx +1 -1
- package/docs/patterns/components-footerreveal--ruled-grid-footer-pattern.tsx +1 -1
- package/docs/patterns/components-footerreveal--sequenced-marketing-hero.tsx +1 -1
- package/docs/patterns/components-herointro--two-line-intro.tsx +1 -1
- package/docs/patterns/components-herotilestack--marketing-hero-pattern.tsx +1 -1
- package/docs/patterns/components-herotilestack--marketing-hero-text-sequence.tsx +1 -1
- package/docs/patterns/components-herotilestack--marketing-hero-with-gallery-intro.tsx +1 -1
- package/docs/patterns/components-indexlist--post-index.tsx +1 -1
- package/docs/patterns/components-intakeconfirmation--booked-pattern.tsx +1 -1
- package/docs/patterns/components-intakeconfirmation--emailed-pattern.tsx +1 -1
- package/docs/patterns/components-intakeform--about-you-pattern.tsx +1 -1
- package/docs/patterns/components-intakeform--short-form-pattern.tsx +1 -1
- package/docs/patterns/components-intakemodal--intake-modal-pattern.tsx +1 -1
- package/docs/patterns/components-kbd--command-row.tsx +1 -1
- package/docs/patterns/components-kbd--numbered-choices.tsx +1 -1
- package/docs/patterns/components-kbd--shortcut.tsx +1 -1
- package/docs/patterns/components-linktile--resource-tile.tsx +1 -1
- package/docs/patterns/components-linktile--router-link.tsx +1 -1
- package/docs/patterns/components-logview--detail-pane.tsx +80 -0
- package/docs/patterns/components-logview--log-viewer.tsx +20 -0
- package/docs/patterns/components-moremenu--action-menu.tsx +1 -1
- package/docs/patterns/components-moremenu--in-card-header.tsx +1 -1
- package/docs/patterns/components-navlist--app-sidebar.tsx +118 -0
- package/docs/patterns/components-navlist--side-nav-settings.tsx +1 -1
- package/docs/patterns/components-pageheader--app-header-pattern.tsx +1 -1
- package/docs/patterns/components-pageheader--page-section-header.tsx +1 -1
- package/docs/patterns/components-pageheader--toolbar-header.tsx +1 -1
- package/docs/patterns/components-panel--end-detail-rail.tsx +1 -1
- package/docs/patterns/components-pillgroup--pill-group-pattern.tsx +1 -1
- package/docs/patterns/components-promptbar--pattern-prompt-bar.tsx +1 -1
- package/docs/patterns/components-prose--article-body.tsx +1 -1
- package/docs/patterns/components-radio-radiogroup--default.tsx +1 -1
- package/docs/patterns/components-scrollhorizontal--project-gallery-pattern.tsx +1 -1
- package/docs/patterns/components-scrollhorizontal--with-intro.tsx +1 -1
- package/docs/patterns/components-sectioncaption--column-caption.tsx +1 -1
- package/docs/patterns/components-sectioncaption--with-action.tsx +1 -1
- package/docs/patterns/components-sheet--bottom-sheet.tsx +1 -1
- package/docs/patterns/components-sheet--end-sheet.tsx +1 -1
- package/docs/patterns/components-sheet--side-sheet-header-actions.tsx +50 -0
- package/docs/patterns/components-sheet--side-sheet-url-deep-link.tsx +51 -0
- package/docs/patterns/components-sheet--side-sheet.tsx +38 -0
- package/docs/patterns/components-sitenav--brand-and-cta.tsx +1 -1
- package/docs/patterns/components-sitenav--centered-links.tsx +1 -1
- package/docs/patterns/components-sitenav--compact.tsx +1 -1
- package/docs/patterns/components-sitenav--marketing-header.tsx +1 -1
- package/docs/patterns/components-sitenav--mega-menu.tsx +1 -1
- package/docs/patterns/components-stat--hero-stat.tsx +54 -0
- package/docs/patterns/components-stat--insights-row.tsx +1 -1
- package/docs/patterns/components-stepprogress--step-progress-pattern.tsx +1 -1
- package/docs/patterns/components-switch--default.tsx +1 -1
- package/docs/patterns/components-switch--settings-row.tsx +1 -1
- package/docs/patterns/components-tab--settings-pages.tsx +1 -1
- package/docs/patterns/components-table--approvals-with-sheet.tsx +81 -0
- package/docs/patterns/components-table--approvals.tsx +223 -0
- package/docs/patterns/components-table--basic.tsx +60 -0
- package/docs/patterns/components-taskrows--action-details.tsx +1 -1
- package/docs/patterns/components-taskrows--capsules.tsx +1 -1
- package/docs/patterns/components-taskrows--card-with-status-rows.tsx +1 -1
- package/docs/patterns/components-taskrows--controlled-expand.tsx +1 -1
- package/docs/patterns/components-taskrows--custom-slots.tsx +1 -1
- package/docs/patterns/components-taskrows--needs-you-table.tsx +62 -0
- package/docs/patterns/components-taskrows--status-list.tsx +1 -1
- package/docs/patterns/components-taskrows--tag-chips.tsx +1 -1
- package/docs/patterns/components-textarea--save-status.tsx +32 -0
- package/docs/patterns/components-textlink--external-destination.tsx +1 -1
- package/docs/patterns/components-textlink--inline-prose.tsx +1 -1
- package/docs/patterns/components-textlink--quiet-title.tsx +1 -1
- package/docs/patterns/components-textlink--supporting-copy.tsx +1 -1
- package/docs/patterns/components-textsequence--default.tsx +1 -1
- package/docs/patterns/components-textsequence--reduced-motion.tsx +1 -1
- package/docs/patterns/components-textsequence--shapes.tsx +1 -1
- package/docs/patterns/components-tilegrid--masonry.tsx +1 -1
- package/docs/patterns/components-toast--stacked-notifications.tsx +1 -1
- package/docs/patterns/components-tooltip--controlled.tsx +1 -1
- package/docs/patterns/components-tooltip--disabled-control.tsx +1 -1
- package/docs/patterns/components-tooltip--icon-action.tsx +1 -1
- package/docs/patterns/components-tooltip--keyboard-shortcut.tsx +1 -1
- package/docs/patterns/components-tooltip--placement.tsx +1 -1
- package/docs/patterns/foundations-scroll-fade--horizontal-rail.tsx +1 -1
- package/docs/patterns/foundations-scroll-fade--overflowing-list.tsx +1 -1
- package/docs/patterns/guides-chart-explorations--comparison-gallery.tsx +1 -1
- package/docs/patterns/guides-filter-panel--filtered-grid.tsx +1 -1
- package/docs/patterns/guides-filter-panel--filtered-index.tsx +1 -1
- package/docs/patterns/guides-overlay-flows--notification-preferences.tsx +1 -1
- package/docs/patterns/guides-post-page--post-page.tsx +1 -1
- package/docs/patterns/guides-profile-typography--editorial-profile.tsx +1 -1
- package/docs/patterns/guides-resource-detail--detail.tsx +1 -1
- package/docs/patterns/index.json +193 -1
- package/package.json +1 -1
- package/dist/components/organisms/Tab/tabOverflow.d.ts +0 -14
- package/dist/components/organisms/Tab/tabOverflow.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,309 @@ Every release of `@thewhatmatters/wmds` is listed here, newest first.
|
|
|
7
7
|
- **Pasted patterns:** when a release changes a **Pattern — …** story's Show code, it is named under Consumer actions so apps re-copy it.
|
|
8
8
|
- **How to add an entry** (contributors): add your change to the top entry. If that version is already on npm (`npm view @thewhatmatters/wmds versions`), start a new entry above it and bump `version` in `package.json` to match. `npm run check:changelog` enforces the format.
|
|
9
9
|
|
|
10
|
+
## 0.4.28
|
|
11
|
+
|
|
12
|
+
**AlertDialog takes a body, Input takes a rich label, and dialogs get a roomier edge.**
|
|
13
|
+
|
|
14
|
+
- **InlinePill** (new): a pill that sits inside running text and scales with it (em-sized), with an optional picture on its left held in the pill's padding so it never parts from its word. `useInlinePillsPopIn()` pops the pictures in one after another when the text scrolls into view, on the WMDS motion tokens; nothing under reduced motion. Not a control: `Chip` and `Badge` stay UI-sized. Story **Components/InlinePill**, with a 390px long-word test.
|
|
15
|
+
- **AlertDialog** `children` (new) — content between the description and the actions, such as a typed-confirmation **Input**. It renders outside the description `<p>`, so `aria-describedby` still names the description alone. Cancel still takes initial focus. Without `children` the markup is unchanged.
|
|
16
|
+
- **Input** `label` is now `ReactNode` (was `string`), so a label can read "Type **@handle** to confirm". The `<label htmlFor>` association is unchanged, and the whole label is the field's accessible name. A string label behaves as before.
|
|
17
|
+
- **Pattern — alert dialog (typed confirmation delete)** is new on **Components/Dialog**: Delete stays off until the typed value matches the handle (a leading `@`, case, and surrounding spaces are ignored), and the field clears on reopen. **Pattern — rich label** is new on **Components/Input**.
|
|
18
|
+
- New interaction test **Internal/Interactions/Overlay → AlertDialog — typed confirmation**.
|
|
19
|
+
- **Dialog** and **AlertDialog** edge inset is **24px** on every side, phones included (was 16px): header, body, and footer share the same horizontal inset, and the panel's top and bottom are 24px. A pill action now clears the 16px panel radius instead of sitting in the corner. **Sheet**, **Panel**, and **Card** keep their 16px layout. `dialogStyles` adds `dialogPanelInsetXClasses`, `dialogHeaderInsetTopClasses`, `dialogFooterInsetBottomClasses`.
|
|
20
|
+
- **Dialog**, **Sheet**, and **IntakeModal** scroll lock no longer leaves a pale strip at the trailing edge on platforms with classic scrollbars: `html { scrollbar-gutter: stable }` kept that strip after the body stopped scrolling, and the fixed scrim cannot paint into it. While a modal is open the gutter is released and the body takes its width as `padding-right`, so the page behind does not move and the scrim reaches the edge.
|
|
21
|
+
- **Loading keeps keyboard focus.** **Button** `status="loading"` (and `"error"` with `disableOnError`), **IconButton** `loading`, **Checkbox** `loading`, and **Switch** `loading` set `aria-disabled` and ignore presses instead of setting native `disabled`, which dropped focus to `<body>` mid-submit. `disabled` still sets native `disabled`. **FilterPanel** Clear all moves focus to the first group (rail) or the results button (Sheet) as it disables itself. New interaction test **Internal/Interactions/Button → a loading button keeps keyboard focus and ignores presses**.
|
|
22
|
+
- **Field** `description` now reaches the control: Field appends its description id to the single child's `aria-describedby` (keeping the child's own ids) instead of setting it on the wrapper `<div>`, where screen readers ignored it. A multi-control Field keeps it on its `role="group"`. **Select** takes `aria-describedby` (new) and joins it with its own `description`. New interaction test **Internal/Interactions/Form controls → Field — description describes the control**.
|
|
23
|
+
- **AlertDialog** (and **Dialog** with `dismissOnBackdrop={false}`): a press on the scrim no longer moves focus to `<body>`, where the next Tab left the dialog. The interaction test **AlertDialog — confirm and cancel** now presses the scrim and checks focus stays inside.
|
|
24
|
+
- **AppShell** has a skip link (new): the first Tab shows "Skip to content" over the top-start corner, and Enter moves focus to `<main>` without changing the URL. `labels.skipToContent` (new) words it. New interaction test **Internal/Interactions/AppShell → the first Tab reaches a skip link that moves focus to the page**.
|
|
25
|
+
- **SiteNav**: while the compact bar is pinned (`placement="fixed"` or a `scrollContainer`), the page (or that container) gets `scroll-padding-top` of `--site-nav-height` plus 16px, so an element focused with Tab scrolls into view below the bar instead of under it. The previous value comes back when the bar unpins or unmounts. New interaction test **Internal/Interactions/SiteNav → pinned compact bar keeps focused elements out from under it**.
|
|
26
|
+
- **Dialog**, **Sheet**, **IntakeModal**, and **Panel** share one open/close lifecycle (`useOverlayLifecycle` in `lib/dialogOverlay`, internal). What changes for users:
|
|
27
|
+
- **Escape closes only the topmost overlay**, so an AlertDialog over a Sheet no longer closes both. An Escape that a control inside already handled (`defaultPrevented`) is left alone.
|
|
28
|
+
- **Dialog** and **IntakeModal** make the page behind inert, as **Sheet** already did, so a screen reader's virtual cursor stays in the modal.
|
|
29
|
+
- **Panel** moves focus into itself on open (its first control).
|
|
30
|
+
- On close, focus returns to the opener only if it is still in the page and focus has not moved on elsewhere; otherwise it goes to `<main>`. This was Sheet-only before.
|
|
31
|
+
- Stacked modals share one scroll lock, so the page scrolls again however they close (closing them out of order used to leave it locked).
|
|
32
|
+
- New interaction tests in **Internal/Interactions/Overlay lifecycle** cover the shared lifecycle and each of the four overlays.
|
|
33
|
+
- **IntakeModal** `dismissOnEscape` (new, default `true`): set `false` so Escape cannot drop typed answers.
|
|
34
|
+
- **Menus share one behaviour** (`useDropdownMenu`, internal, in the Dropdown folder). **Select**, **MoreMenu**, and **Breadcrumb**'s "…" menu:
|
|
35
|
+
- **Escape** closes only the menu and returns focus to its trigger, so a menu inside a **Dialog** or **Sheet** no longer closes the overlay too. An Escape during IME composition is ignored by menus and overlays.
|
|
36
|
+
- **Tab** closes the menu. A press outside closes it (`pointerdown`, so touch works too). Scroll and resize keep it attached to its trigger.
|
|
37
|
+
- **Keyboard:** ArrowDown/ArrowUp, Home/End, and type-ahead; disabled rows are skipped.
|
|
38
|
+
- **Screen readers hear the active row.** **Select**'s trigger is a select-only `combobox` naming the active option with `aria-activedescendant`. **MoreMenu** and Breadcrumb move real focus onto the rows; Space chooses a row like Enter does.
|
|
39
|
+
- **Tab** and **SiteNav** More menus use it too, through one overflow module (`useOverflowMenu`) that owns measuring, the fit, and the More menu. SiteNav no longer reaches into Tab. More menus now follow their trigger on scroll instead of closing, and stop listening for Escape on the whole page.
|
|
40
|
+
- **SiteNav** More rows are real links (`<a href>`), so Cmd-click and middle-click open a new tab.
|
|
41
|
+
- **Tab**: More now sits beside the `tablist` instead of inside it (a `tablist` may only own tabs), which clears the accessibility violations its overflow stories were waiving.
|
|
42
|
+
- New interaction tests in **Internal/Interactions/Menus** cover the shared behaviour and all five menus, including a Select and a MoreMenu inside a **Dialog** and a Breadcrumb inside a **Sheet**, where Escape closes the menu and leaves the overlay open.
|
|
43
|
+
- **Dropdown.Item** forwards its ref. A row that is both active and selected now shows the active fill, so focus is visible on it.
|
|
44
|
+
- **Theme** sets `color-scheme` (new): `light` on `:root` and `dark` under `[data-theme="dark"]`, so native scrollbars, form controls, and autofill follow the theme instead of staying light in dark mode.
|
|
45
|
+
- **`docs/exports.json`** now lists all 41 lib helpers `src/index.ts` exports (was 12). Nothing was added to or removed from the package. `npm run validate:manifest` now also fails when `src/index.ts` exports a lib helper the manifest does not list.
|
|
46
|
+
|
|
47
|
+
- **Brand kit (new).** A client's brand is now a short `brand.css` of allow-listed knobs (**CONSUMING.md → Brand kit**):
|
|
48
|
+
- **Type:** `--font-display` (every `type-display-*` step; unset follows `--font-family-heading`), `--font-display-weight`, `--font-display-tracking`, `--font-display-case`, and a `--font-serif` slot. `--font-family-heading` is now allow-listed too. With none set, type renders as before.
|
|
49
|
+
- **New display steps:** `type-display-hero` (fluid to 16vw, `--text-display-hero-size` knob) and `type-display-xl` (44px → 136px), both with tight leading and safe word breaks on phones.
|
|
50
|
+
- **Section colours:** `--color-brand-surface` / `--color-on-brand-surface` (default: the navy) and `--color-field` / `--color-on-field` (default: the primary), each with an optional `-muted` step.
|
|
51
|
+
- **Band (new component):** `tone` `default` / `inverse` / `field` sets `data-tone`, which remaps surface, text, border, fill and focus roles so every component inside re-skins. It holds its own `grid-page`; inside a `grid-page` it bleeds to the viewport edges (no horizontal scrollbar) and keeps the page's columns. `spacing` `md` / `lg` / `none`. New stories **Components/Band → Pattern — page in bands** and **Foundations/Brand kit → Theme a client** (the offer-sheet values with a contrast table); new interaction tests **Internal/Interactions/Band**.
|
|
52
|
+
- **wmds-check** `brand-contrast` (new, error): fails an on-colour or muted step under 4.5:1 on its colour, for primary, brand surface and field, when both are hex in the same CSS rule. It now reads the app's `.css` files.
|
|
53
|
+
- **TextSequence** word masks reach 0.08em above and to the sides of each word, so tall caps on tight display leading and outline strokes aren't clipped. Layout is unchanged.
|
|
54
|
+
- **Design knobs for shape (new).** `--radius-control` (default pill) and `--radius-surface` (default 16px) reshape every control and surface. Set them on `:root`, or on a wrapper with Tailwind (`[--radius-control:4px]`), for rectangular buttons, inputs, selects, chips, badges and tabs, and squarer cards and menus. Avatars, dots, spinners, radios, switches and progress bars stay round. Defaults are unchanged: every component renders as before. **CONSUMING.md → Design knobs**; new story **Foundations/Shape → Design knobs — rectangular controls**; new interaction test **Internal/Interactions/Shape**.
|
|
55
|
+
|
|
56
|
+
### Consumer actions
|
|
57
|
+
|
|
58
|
+
None. To ask for a typed confirmation, pass an **Input** as `children` of **AlertDialog** and set `confirmDisabled` until the value matches. Copy **Components/Dialog → Pattern — alert dialog (typed confirmation delete)**. A **Dialog** or **AlertDialog** is 16px wider on each side's content inset and 16px taller (8px top, 8px bottom); check the narrowest phone width. No `className` change is needed. `npx wmds-check` now reads your `.css` files: if `brand-contrast` fails on an existing `--color-on-primary`, pick an ink that clears 4.5:1 (or mark a deliberate exception with `/* wmds-check-ignore brand-contrast <reason> */` on the line above). To carry a client's type and section colours, see **CONSUMING.md → Brand kit** and replace hand-rolled section colours with **Band** `tone`.
|
|
59
|
+
|
|
60
|
+
## 0.4.27
|
|
61
|
+
|
|
62
|
+
**An opt-in counter for the Carousel.**
|
|
63
|
+
|
|
64
|
+
- **Carousel** `counter` (new, default `false`) — a visible "1 of 6" under the row on `type-supporting`, `text-muted`, tabular figures; announced politely (`aria-live="polite"`) as the item the row rests on changes; hidden (keeping its space) while every item fits. `labels.counter(position, count)` words it.
|
|
65
|
+
- **Carousel** `onIndexChange(index, count)` (new) — 0-based; fires once the row has been still for a moment and the item it rests on has changed. At the end of the row the index is the last item.
|
|
66
|
+
- **Carousel** `activeIndex` (new) — controlled: the row moves to that item (0-based) when it changes; the first value is applied without a glide.
|
|
67
|
+
- With none of the new props, the markup and behavior are unchanged.
|
|
68
|
+
- Stories: **Carousel → Review — 390 counter** and **Reference — count outside the row**. See the ADR-0043 addendum.
|
|
69
|
+
|
|
70
|
+
### Consumer actions
|
|
71
|
+
|
|
72
|
+
None. To show the count, pass `counter`; to show it elsewhere, pass `onIndexChange`. No **Pattern — …** Show code changed.
|
|
73
|
+
|
|
74
|
+
## 0.4.26
|
|
75
|
+
|
|
76
|
+
**A side sheet with header actions, a phone layout, and URL state.**
|
|
77
|
+
|
|
78
|
+
- **Sheet** `Sheet.Content` `headerActions` (new) — controls before the close (**IconButton** `size="sm"`: previous / next, share).
|
|
79
|
+
- **Sheet** `side="end"` is **full-screen below `md`** (no inset, no radius; header and footer pinned, body scrolls, footer clears the home indicator). From `md` it is a rail over a lighter scrim (`bg-overlay/50`). The page behind is `inert` while open; closing a deep-linked sheet moves focus to `main`; reduced motion fades instead of sliding.
|
|
80
|
+
- **`useSheetParam(kind, { initialUrl? })`**, **`readSheetParam(url)`**, **`withSheetParam(url, id | null, kind)`** (new) — keep a sheet in `?sheet=<kind>&id=<id>` with the History API, no router. Back closes; a deep link opens on first render; `initialUrl` serves the open sheet from the server.
|
|
81
|
+
- New patterns: **Components/Sheet → Pattern — side sheet**, **Pattern — side sheet header actions**, **Pattern — side sheet URL deep link**; **Components/Table → Pattern — approvals with sheet**.
|
|
82
|
+
- See ADR-0049.
|
|
83
|
+
|
|
84
|
+
### Consumer actions
|
|
85
|
+
|
|
86
|
+
None required. Every `side="end"` **Sheet**, including **SiteNav**'s phone menu, is now full-screen below `md`; check a phone width. To keep a detail in the URL, wire `useSheetParam` as in **Pattern — side sheet URL deep link** (new patterns, nothing to re-copy).
|
|
87
|
+
|
|
88
|
+
## 0.4.24
|
|
89
|
+
|
|
90
|
+
**One focus ring on TextArea and Input.**
|
|
91
|
+
|
|
92
|
+
- **TextArea** and **Input** no longer show a second ring. The shell's ring sat 2px off the border behind a page-colored gap, so it read as a gray ring around the field's own edge. The ring now hugs the border, one `--color-focus-ring` (or status ring) token, for solo and validation shells.
|
|
93
|
+
- The inner field of **TextArea**, **Input**, **PromptBar** and **CopyField** drops its outline and ring with `!important`. A global `:focus-visible { outline }` rule in an app is unlayered and used to beat the component's layered `outline-none`, which painted a blue ring inside the shell.
|
|
94
|
+
- New **Components/TextArea → Focused** story.
|
|
95
|
+
|
|
96
|
+
### Consumer actions
|
|
97
|
+
|
|
98
|
+
None.
|
|
99
|
+
|
|
100
|
+
## 0.4.23
|
|
101
|
+
|
|
102
|
+
**The package loads under Node, and TextArea announces progress.**
|
|
103
|
+
|
|
104
|
+
- **Fixed:** importing `@thewhatmatters/wmds` in Node ESM (a node test runner, or vitest in a DOM environment) threw `SyntaxError: Named export 'Rive' not found`. `@rive-app/react-canvas` is CommonJS, and **RiveHand** imported it by name; Node only exposes `default` for a CommonJS module. **RiveHand** now reads the runtime from the namespace and falls back to `default`, so bundlers (Vite, Next) and Node both load it. The `motion` imports were not the cause. The build now imports `dist/index.js` in Node, and a unit test does the same.
|
|
105
|
+
- **TextArea** `liveStatus` (new) — progress text under the field, in a polite live region (`role="status"`, `aria-live="polite"`). Pass `""` when idle so the region stays mounted and "Saving…" / "Saved" are heard. It is separate from the validation `status` + `message`. `description` and `message` were already tied to the field with `aria-describedby`; tests now cover it.
|
|
106
|
+
- **Pattern — save status** is new on **Components/TextArea**.
|
|
107
|
+
|
|
108
|
+
### Consumer actions
|
|
109
|
+
|
|
110
|
+
None. To announce a save state, pass `liveStatus`. Copy **Components/TextArea → Pattern — save status** (`docs/patterns/components-textarea--save-status.tsx`) for a worked example.
|
|
111
|
+
|
|
112
|
+
## 0.4.22
|
|
113
|
+
|
|
114
|
+
**A pinned sidebar footer, and AppShell on the server.**
|
|
115
|
+
|
|
116
|
+
- **AppShell** `sidebarFooter` / **AppShell.SidebarFooter** (new) — pinned to the bottom of the sidebar column and the phone **Sheet**. The **NavList** scrolls on its own above it, so a theme switch stays visible at 1440x900 and on a phone. No new color, border, radius, or shadow.
|
|
117
|
+
- **Pattern — portal dashboard** now puts a **DisplayControls** `controls="theme"` switch in the footer.
|
|
118
|
+
- Documented: the phone toggle's accessible name is **"Menu"** (`labels.menu`), and the docs now say which parts render on the server, which need a `"use client"` boundary, that `displayThemeNoFlashScript` goes in `<head>`, and a Next.js App Router example (`docs/component-contracts.md → AppShell — server rendering`).
|
|
119
|
+
- See the ADR-0047 addendum.
|
|
120
|
+
|
|
121
|
+
### Consumer actions
|
|
122
|
+
|
|
123
|
+
None for existing shells. To pin a footer, pass `sidebarFooter`. Re-copy **Components/AppShell → Pattern — portal dashboard** (`docs/patterns/components-appshell--portal-dashboard-pattern.tsx`) to get the theme switch in the footer.
|
|
124
|
+
|
|
125
|
+
## 0.4.21
|
|
126
|
+
|
|
127
|
+
**A table.**
|
|
128
|
+
|
|
129
|
+
- **Table** (new, atom) — `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption`. Semantic HTML in a focusable, horizontal-scroll `region`. No new color, radius, shadow, or variant.
|
|
130
|
+
- Patterns: **Components/Table → Pattern — basic table** and **Pattern — approvals queue** (Approve `primary`, Deny `outline`, an inline Confirm deny / Cancel step in the same row, then a success or failed status pill; keyboard focus follows, Escape cancels, and each button's `aria-label` carries the row name). States: deny confirm step, decided, empty, mobile 390.
|
|
131
|
+
- **Table** polish: headers use `ui-label` and the caption `body` (one step up), `sticky` on `TableHead` / `TableCell` pins a column to the start edge (existing `bg-body` surface), and a table with a `colgroup` lays out from its `col` widths (fixed layout) so an empty and a filled table match.
|
|
132
|
+
- **Pattern — approvals queue**: the row keeps one height through pending, confirm, and decided (44px decision slot); focus after a decision lands on the status pill and shows the `focus-ring` only for keyboard focus (no box after a mouse click); the Name column is sticky; a `colgroup` fixes the column widths for the filled and empty states.
|
|
133
|
+
- **Pattern — approvals queue** on a phone (below `sm`, 640px): each row stacks as an outlined **Card** — name, then type and waiting, then **Approve** and **Deny** each taking half the row, all visible without sideways scroll. The deny confirm stays inline (Confirm deny · Cancel in the same slot) and the 44px decision slot keeps the card one height through pending, confirm, and decided. From `sm` the table is unchanged. No new color, radius, shadow, or Button role.
|
|
134
|
+
- `Table` moves out of the planned list.
|
|
135
|
+
- See ADR-0048.
|
|
136
|
+
|
|
137
|
+
### Consumer actions
|
|
138
|
+
|
|
139
|
+
None for existing pages. If you pasted **Pattern — approvals queue** from an earlier draft, re-copy `docs/patterns/components-table--approvals.tsx` (**Pattern — approvals queue** changed: phone card layout; it now also imports `Card`). To adopt it, `import { Table } from "@thewhatmatters/wmds"` and paste a **Components/Table** pattern (`docs/patterns/components-table--basic.tsx`, `docs/patterns/components-table--approvals.tsx`).
|
|
140
|
+
|
|
141
|
+
## 0.4.20
|
|
142
|
+
|
|
143
|
+
**An app shell.**
|
|
144
|
+
|
|
145
|
+
- **AppShell** (new) — a sidebar on the page background and the page as an inset, raised surface with a title bar. It composes **NavList**, **PageHeader** `variant="app"`, **Sheet** (the sidebar below `lg`), and **Panel** (the optional aside). No new color, radius, shadow, or variant.
|
|
146
|
+
- `<AppShell title brand sidebar header aside>` or the slots **AppShell.Brand**, **Sidebar**, **Header**, **Aside**. A `PanelLeft` toggle collapses the sidebar, **Ctrl** / **⌘** + **B** does the same, and `useAppShell()` returns the state.
|
|
147
|
+
- Patterns: **Components/AppShell → Pattern — portal dashboard**. States: collapsed, aside open, phone, phone menu open.
|
|
148
|
+
- **PageHeader** now applies `aria-label` when `title` is also set (it used to drop it), so a page header can have a name that tells it apart from other banners.
|
|
149
|
+
- **Sheet**, **Panel**, **Dialog**, and **AlertDialog** headers carry `role="presentation"`, so an open overlay no longer adds a second `banner` landmark beside the page header.
|
|
150
|
+
- See ADR-0047.
|
|
151
|
+
|
|
152
|
+
### Consumer actions
|
|
153
|
+
|
|
154
|
+
None for existing pages. To adopt it, `import { AppShell } from "@thewhatmatters/wmds"` and paste **Components/AppShell → Pattern — portal dashboard** (`docs/patterns/components-appshell--portal-dashboard-pattern.tsx`).
|
|
155
|
+
|
|
156
|
+
## 0.4.19
|
|
157
|
+
|
|
158
|
+
**A chart beside a stat column.**
|
|
159
|
+
|
|
160
|
+
- **Pattern — revenue by line** composes **Chart.Cartesian** and a column of **Stat** inside a **Card**. No new component.
|
|
161
|
+
- The same pattern includes the unwired state: "Revenue by line is not wired". Do not draw a series until the source exists.
|
|
162
|
+
|
|
163
|
+
### Consumer actions
|
|
164
|
+
|
|
165
|
+
Paste **Components/Chart → Pattern — revenue by line** (`docs/patterns/components-chart--revenue-by-line.tsx`). Keep the unwired card until revenue by line is connected. Drop it once `data` is real.
|
|
166
|
+
|
|
167
|
+
## 0.4.18
|
|
168
|
+
|
|
169
|
+
**A log viewer, and a dark detail pane.**
|
|
170
|
+
|
|
171
|
+
- **LogView** (new) — line numbers, a tabular line count, **Copy**, and **Follow**. `follow` keeps the scroller on the last line. `loading` is a **Skeleton**. An empty log says "No log lines".
|
|
172
|
+
- **Stat** `detail` — one tabular line under the figure, for example "3 new". Hidden while `loading` or `unavailable`. Hero props (`size="lg"`, `context`, `icon`, `action`, `unavailable`) stay.
|
|
173
|
+
- Patterns: **Components/LogView → Pattern — log viewer**, **Pattern — detail pane** (prev/next, tags, a 2×2 stat grid, the log, on `data-theme="dark"`).
|
|
174
|
+
|
|
175
|
+
### Consumer actions
|
|
176
|
+
|
|
177
|
+
`import { LogView } from "@thewhatmatters/wmds"`. Paste **Components/LogView → Pattern — log viewer** (`docs/patterns/components-logview--log-viewer.tsx`) and **Pattern — detail pane** (`docs/patterns/components-logview--detail-pane.tsx`). Pass `detail` on **Stat** for the sub line. Existing hero stats keep `size`, `context`, `icon`, `action`, and `unavailable`.
|
|
178
|
+
|
|
179
|
+
## 0.4.17
|
|
180
|
+
|
|
181
|
+
**Card.Body can be a terminal frame.**
|
|
182
|
+
|
|
183
|
+
- **`Card.Body variant="terminal"`** is a dark embed. `data-theme="dark"` on the frame uses the existing dark tokens, so the surface stays dark in both page themes. No new color.
|
|
184
|
+
- `src` or `srcDoc`, `title`, `height` (`sm` | `md` | `lg` | `fill`), `readOnly` (muted **Read only** badge), `state` (`ready` | `loading` | `disconnected`).
|
|
185
|
+
- `bodyTerminal` is unchanged: it only densifies the shell bottom when Body has no Footer. A terminal frame sits flush to the bottom of a footerless card.
|
|
186
|
+
- Pattern: **Components/Card/Card → Pattern — terminal frame**.
|
|
187
|
+
|
|
188
|
+
### Consumer actions
|
|
189
|
+
|
|
190
|
+
Replace a hand-rolled iframe in **Card.Body** with `variant="terminal"`. Paste **Components/Card/Card → Pattern — terminal frame** (`docs/patterns/components-card-card--terminal-frame.tsx`). `bodyTerminal` still means footerless density — do not use it as the embed.
|
|
191
|
+
|
|
192
|
+
## 0.4.16
|
|
193
|
+
|
|
194
|
+
**DisplayControls can be a theme switch, with a no-flash snippet.**
|
|
195
|
+
|
|
196
|
+
- **DisplayControls** `controls="theme"` drops the grid button. **G** is not captured. **T** still cycles Auto → Light → Dark.
|
|
197
|
+
- `readDisplayTheme`, `persistDisplayTheme`, `applyDisplayTheme`, `commitDisplayTheme`, and `subscribeDisplayTheme` remember the choice under `wmds-theme` and set or clear `data-theme` on `<html>`. `light` clears the attribute. `dark` sets it. `auto` follows `prefers-color-scheme` and keeps listening until the next call. `useSyncExternalStore(subscribeDisplayTheme, readDisplayTheme, …)` reads the saved choice without a hydration mismatch.
|
|
198
|
+
- `displayThemeNoFlashScript` is the head snippet that reads the same key before paint. Paste it before the stylesheet.
|
|
199
|
+
- Pattern: **Components/DisplayControls → Pattern — theme switch**.
|
|
200
|
+
|
|
201
|
+
### Consumer actions
|
|
202
|
+
|
|
203
|
+
Paste `displayThemeNoFlashScript` in `<head>` before the stylesheet (a `<script>` whose text is that string). Render **DisplayControls** with `controls="theme"` and call `commitDisplayTheme` from `onThemeChange`. Paste **Components/DisplayControls → Pattern — theme switch** (`docs/patterns/components-displaycontrols--theme-switch.tsx`).
|
|
204
|
+
|
|
205
|
+
Existing grid + theme pages keep `controls` omitted. **Pattern — page display controls** is unchanged.
|
|
206
|
+
|
|
207
|
+
## 0.4.15
|
|
208
|
+
|
|
209
|
+
**TaskRows: a needs-you table.**
|
|
210
|
+
|
|
211
|
+
- **TaskRows** `variant="table"` (new) — columns of title, waiting, and metric. Not an accordion. `columns` is `{ id: "title" | "waiting" | "metric", header, align?, width? }`. Defaults are Item, Waiting, and Metric.
|
|
212
|
+
- **TaskRows.Row** (new) — `title`, `href` or `render` (a router link; `render` wins), `status` (`tone`, `label` such as "Open · 2d", `pulsing`), `waiting` (a muted **Badge** pill and a detail line), `metric` (`value` is tabular, plus `detail`).
|
|
213
|
+
- `source` — `ready` (default), `loading` (skeleton, `aria-busy`), `error` (the `note` in the destructive color), `unwired` (the `note` as a muted footer line, no fake metric).
|
|
214
|
+
- `empty` — shown when there are no rows. Defaults to "Nothing waiting".
|
|
215
|
+
- Below `md` the headers are visually hidden and each row is two lines: the title, then the status, the waiting detail, and the metric.
|
|
216
|
+
- Pattern: **Components/TaskRows → Pattern — needs you**.
|
|
217
|
+
|
|
218
|
+
### Consumer actions
|
|
219
|
+
|
|
220
|
+
None for existing **TaskRows** lists and capsules.
|
|
221
|
+
|
|
222
|
+
For a needs-you table, paste **Components/TaskRows → Pattern — needs you** (`docs/patterns/components-taskrows--needs-you-table.tsx`). Pass `source="unwired"` and `note` when a source is not connected. Do not invent a zero.
|
|
223
|
+
|
|
224
|
+
## 0.4.14
|
|
225
|
+
|
|
226
|
+
**Chart: a segmented progress ring, a tabular legend, and a SegmentedBar that renders before it is measured.**
|
|
227
|
+
|
|
228
|
+
- **Chart.SegmentedRing** (new) — radial capacity meter. `value` / `max`, `segments` (default 72), `tone`, `centerValue`, `centerLabel`, `size` (`sm` 96px, `md` 160px, `lg` 192px), `animate` (`initial` sweeps on mount, `none` holds the fill), `label`. The figure is tabular. A fixed viewBox, so the ticks are in the server HTML.
|
|
229
|
+
- Over capacity — a value above `max` fills every tick and shows "Over capacity". The arc does not grow past the ring.
|
|
230
|
+
- `unavailable` — `{ reason, label? }` empties the track and replaces the center with muted "Not connected" (or `label`) and the reason. Do not pass a zero for an unwired source.
|
|
231
|
+
- Reduced motion — no sweep. The server render is the settled fill, so hydration matches.
|
|
232
|
+
- **Chart.Legend** `variant="tabular"` (new) — one row: swatch, label, mono value, percent, delta. `config` entries take optional `value`, `percent`, and `delta` (`{ label, direction?: "up" | "down" | "flat" }`). `up` is success, `down` is destructive, `flat` is muted. The default `series` variant is unchanged.
|
|
233
|
+
- **Chart.SegmentedBar** — draws CSS ticks when the host width is still 0 (server HTML and the first client render). The measured SVG, including `fill="velocity"` and `fill="semantic"`, replaces them once the width is known. `animate="initial"` still springs that SVG.
|
|
234
|
+
- Pattern: **Components/Chart → Pattern — segmented ring**.
|
|
235
|
+
|
|
236
|
+
### Consumer actions
|
|
237
|
+
|
|
238
|
+
None for existing **Chart.Legend** rows or **Chart.SegmentedBar** props.
|
|
239
|
+
|
|
240
|
+
**Chart.SegmentedBar** now shows ticks before hydration. A client island is no longer required for the meter to appear. `animate="initial"` still springs once the bar has a width.
|
|
241
|
+
|
|
242
|
+
For a radial meter and a source breakdown, paste **Components/Chart → Pattern — segmented ring** (`docs/patterns/components-chart--segmented-ring-pattern.tsx`). Pass `unavailable={{ reason }}` when the source is unwired.
|
|
243
|
+
|
|
244
|
+
## 0.4.13
|
|
245
|
+
|
|
246
|
+
**NavList: link rows, a trailing mark, a section action, and an app sidebar.**
|
|
247
|
+
|
|
248
|
+
- **NavList** item `href` (new) — the row is an anchor, so a server-rendered app navigates without mapping `onSelect` to a router. `render={<Link href="/tasks/ikea" />}` (new) is the router link. Pass one. If both are set, `render` is used.
|
|
249
|
+
- `trailing` (new) — a **Status** dot or a **Badge** `status` pill at the end of the row. Counts stay tabular beside it.
|
|
250
|
+
- Section `action` (new) — an **IconButton** for filter or more, in the section label row.
|
|
251
|
+
- **Button** `layout="nav"` accepts `render`, the same way a pill does. A selected link sets `aria-current="page"`.
|
|
252
|
+
- Pattern: **Components/NavList → Pattern — app sidebar** — brand row, **Search**, sections, a footer slot, and below `lg` a **Sheet** `side="start"` opened from a menu **IconButton** in **PageHeader** `variant="app"`.
|
|
253
|
+
|
|
254
|
+
### Consumer actions
|
|
255
|
+
|
|
256
|
+
None for existing button rows.
|
|
257
|
+
|
|
258
|
+
To navigate from a row: pass `href` or `render`. Paste **Components/NavList → Pattern — app sidebar** (`docs/patterns/components-navlist--app-sidebar.tsx`) for the sidebar and the phone sheet.
|
|
259
|
+
|
|
260
|
+
## 0.4.12
|
|
261
|
+
|
|
262
|
+
**Badge: a status pill for run state, cadence, and freshness.**
|
|
263
|
+
|
|
264
|
+
- **Badge** `status` (new) — `active` | `idle` | `success` | `failed` | `warning`. A pill at cluster height (28px `sm`, 36px `md`) with tabular figures. `active` uses `bg-brand-tint` and `text-brand-outline` (navy `#011272` in light, the lifted navy in dark). The others use the existing muted fills. Not with `count`, `iconOnly`, or `avatar`.
|
|
265
|
+
- `dot` (new) — an 8px mark on a status pill. It pulses when `status` is `active`. Reduced motion holds it still. The mark is Badge's own dot, not the **Status** component.
|
|
266
|
+
- `icon` on a status pill leads cadence ("Every 2 days") and freshness ("Last checked 1d ago").
|
|
267
|
+
- Plain label badges stay on the 20 / 24px shell.
|
|
268
|
+
- Pattern: **Components/Badge → Pattern — status pill**.
|
|
269
|
+
|
|
270
|
+
### Consumer actions
|
|
271
|
+
|
|
272
|
+
None for existing badges.
|
|
273
|
+
|
|
274
|
+
For a routine's state: `<Badge status="active" dot>Active</Badge>`, and `<Badge status="idle" icon={<Calendar />}>Every 2 days</Badge>`. Paste **Components/Badge → Pattern — status pill** (`docs/patterns/components-badge--status-pill.tsx`).
|
|
275
|
+
|
|
276
|
+
## 0.4.11
|
|
277
|
+
|
|
278
|
+
**Stat: a hero figure with a visible "vs" line.**
|
|
279
|
+
|
|
280
|
+
- **Stat** `size="lg"` (new) — the figure is `type-display-3` with tabular numbers. The label is sentence case. `sm` and `md` are unchanged.
|
|
281
|
+
- `context` (new) — a visible secondary line under the figure, tabular, for "vs $14,850" or "of $1M goal". `trend.label` stays a tooltip on the badge.
|
|
282
|
+
- `icon` (new) — a leading Lucide glyph in a brand-tint tile (**ButtonIcon**). The glyph uses `text-brand-outline`, which is navy (`#011272`) in light and the lifted navy in dark.
|
|
283
|
+
- `action` (new) — label-row slot. Pass **Button** `size="sm"` ("View details").
|
|
284
|
+
- `unavailable` (new) — `{ reason, label? }` replaces the figure with muted "Not connected" (or `label`) and the reason. `trend` and `context` stay hidden. Do not fake a zero.
|
|
285
|
+
- `trend.direction` accepts `flat`, the same muted badge as `neutral`.
|
|
286
|
+
- `loading` still sets `aria-busy` and a **Skeleton**. `lg` adds a third bar for the context line.
|
|
287
|
+
- Pattern: **Components/Stat → Pattern — hero stat**.
|
|
288
|
+
|
|
289
|
+
### Consumer actions
|
|
290
|
+
|
|
291
|
+
None for existing **Stat** tiles.
|
|
292
|
+
|
|
293
|
+
For a hero KPI with a visible comparison: `size="lg"`, `context="vs $14,850"`, optional `icon` and `action`. Paste **Components/Stat → Pattern — hero stat** (`docs/patterns/components-stat--hero-stat.tsx`). When the source is unwired, pass `unavailable={{ reason }}` instead of a zero.
|
|
294
|
+
|
|
295
|
+
## 0.4.10
|
|
296
|
+
|
|
297
|
+
**CopyField: a read-only value with a Copy key button.** For copying an API key. See **ADR-0045**.
|
|
298
|
+
|
|
299
|
+
- **CopyField** (new) — a read-only monospace value and a **Button** `role="secondary"` `size="sm"` labeled "Copy key", seated at the trailing end of **PromptBar**'s pill. **Input** is `inline` `mono` (`type-code`) and truncates, so a long key does not run under the button. A click copies `value` and the label switches to "Copied" for `buttonStatusHoldMs` (2s), announced from the button's polite live region, then returns. The button is in the tab order (Enter and Space). Focusing the value selects it. If the Clipboard API is missing or blocked, the field selects the value, tries `execCommand('copy')`, and shows "Press Cmd/Ctrl+C to copy." in a polite status. The value is never logged.
|
|
300
|
+
- **Input** `mono` (new, optional) — the same monospace face, for a read-only key or id. Existing inputs are unchanged.
|
|
301
|
+
- `state` (`"copied"` | `"fallback"`) pins that appearance for a specimen. Omit it in the app.
|
|
302
|
+
- Patterns: **Components/CopyField → Pattern — copy key**, **Pattern — copied**, **Pattern — clipboard blocked**.
|
|
303
|
+
- **On a navy field** — `data-theme="dark"` does not belong on `bg-brand`. Dark surfaces are `#262626`, about 1:1 on `#011272`, so the pill and a secondary button disappear. The label and the clipboard hint inherit the section color instead of `text-fg` / `text-muted`. The pill stays a surface. **Button** `role="inverse-outline"` is the on-brand hairline (`--color-on-brand`, no new color). The filled action stays `role="inverse"`. See **ADR-0046**. Paste **Components/CopyField → Pattern — on a navy field**.
|
|
304
|
+
|
|
305
|
+
### Consumer actions
|
|
306
|
+
|
|
307
|
+
None for existing apps on a light surface.
|
|
308
|
+
|
|
309
|
+
On a brand-navy section: wrap it in `bg-brand text-on-brand`. Do not set `data-theme="dark"` on that section. Paste **Components/CopyField → Pattern — on a navy field** (`docs/patterns/components-copyfield--on-navy.tsx`): `<CopyField label="API key" value={apiKey} />`, **Button** `role="inverse"` for the filled action, `role="inverse-outline"` for the hairline. Do not log the key, and do not pass `state`.
|
|
310
|
+
|
|
311
|
+
To copy an API key on a light surface: `import { CopyField } from "@thewhatmatters/wmds"` and render `<CopyField label="API key" value={apiKey} />`. Paste **Components/CopyField → Pattern — copy key** (`docs/patterns/components-copyfield--copy-key.tsx`). Do not log the key, and do not pass `state`.
|
|
312
|
+
|
|
10
313
|
## 0.4.9
|
|
11
314
|
|
|
12
315
|
**Fixes from the site's 0.4.8 upgrade: router links in every page pattern, a tighter tile caption, and a checker that no longer skips files.**
|
package/CONSUMING.md
CHANGED
|
@@ -316,6 +316,67 @@ Browse [lucide.dev/icons](https://lucide.dev/icons/).
|
|
|
316
316
|
|
|
317
317
|
## Customization
|
|
318
318
|
|
|
319
|
+
### Design knobs (shape)
|
|
320
|
+
|
|
321
|
+
A site whose design isn't WMDS's default shape (rectangular buttons instead of pills, squarer cards) changes it with **design knobs**: allow-listed CSS variables that every component reads. Don't restyle components with `className`. A knob reshapes every component inside the element it's set on, and keeps focus rings, hit areas and nested radii consistent.
|
|
322
|
+
|
|
323
|
+
| Knob | Default | Reshapes |
|
|
324
|
+
|------|---------|----------|
|
|
325
|
+
| `--radius-control` | pill | Button, IconButton, Input, Search, Select, Chip, PillGroup, Badge, SegmentedControl, Tab, the compact SiteNav bar. Multi-line controls (TextArea, NavList rows) use 12px or the knob, whichever is smaller. |
|
|
326
|
+
| `--radius-surface` | `16px` | Card layout shells and occupants, Dropdown menus, Toast, ChatQa, Carousel. Smaller surfaces scale with it: Stat, LogView and Skeleton at ¾ (12px), a padded Card at ½ (8px). |
|
|
327
|
+
|
|
328
|
+
Avatars, status dots, spinners, radios, switches, progress bars and chart marks stay round.
|
|
329
|
+
|
|
330
|
+
Set knobs for the whole app on `:root` in `brand.css` (below), or for one page or section with a Tailwind arbitrary property on a plain wrapper:
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
<main className="grid-page [--radius-control:4px] [--radius-surface:6px]">…</main>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Derived radii (`--radius-control-inner`, `--radius-control-box`, `--radius-surface-md`, `--radius-surface-sm`, `--radius-card-shell`, `--radius-card-body`) follow the knobs; don't set them. See **Foundations → Shape → Design knobs — rectangular controls**.
|
|
337
|
+
|
|
338
|
+
### Brand kit (type and section colours)
|
|
339
|
+
|
|
340
|
+
A client's brand on WMDS is a short `brand.css` (imported after `styles.css`, unlayered) that sets **allow-listed** knobs. Each knob is read by every component that needs it; nothing else is overridable, so contrast and layout stay guaranteed.
|
|
341
|
+
|
|
342
|
+
| Knob | Default | Reads it |
|
|
343
|
+
|------|---------|----------|
|
|
344
|
+
| `--font-family-heading` | Geist | `type-heading-*`, and every display step when `--font-display` is unset |
|
|
345
|
+
| `--font-display` | unset (the heading face) | every `type-display-*` step (`hero`, `xl`, `1`, `2`, `3`) |
|
|
346
|
+
| `--font-display-weight` | each step's own (700 / 600) | display steps: set `400` for a one-weight face, so nothing is faux-bolded |
|
|
347
|
+
| `--font-display-tracking` | each step's own | display steps: `0` for a condensed face |
|
|
348
|
+
| `--font-display-case` | unset | display steps: `uppercase` for a face set in capitals (the accessible name stays the written text) |
|
|
349
|
+
| `--font-serif` | system serif | a slot: point `--font-display` or `--font-family-heading` at `var(--font-serif)` |
|
|
350
|
+
| `--text-display-hero-size` | `clamp(3.5rem, 16vw, 16rem)` | `type-display-hero` (a condensed face carries more vw) |
|
|
351
|
+
| `--color-brand-surface` / `--color-on-brand-surface` | the navy / white | **Band** `tone="inverse"` |
|
|
352
|
+
| `--color-on-brand-surface-muted` | 72% of the ink on the surface | secondary text on `inverse` |
|
|
353
|
+
| `--color-field` / `--color-on-field` | the primary pair | **Band** `tone="field"` |
|
|
354
|
+
| `--color-on-field-muted` | the ink itself | secondary text on `field` |
|
|
355
|
+
|
|
356
|
+
The font knobs also work on a section wrapper (`[--font-display:…]`); set the colour pairs on `:root` (and `[data-theme="dark"]` if they differ there). Load the face yourself (`next/font` or `@fontsource/*`).
|
|
357
|
+
|
|
358
|
+
**Display steps:** `type-display-hero` (fluid to 16vw, 0.92 leading) and `type-display-xl` (44px → 136px, 0.9 leading) sit above `type-display-1`; both break long words rather than overflow a phone. **TextSequence** word masks keep 0.08em headroom above and to the sides, so tall caps and outline strokes aren't clipped.
|
|
359
|
+
|
|
360
|
+
**Contrast:** every on-colour and muted step needs **4.5:1** on its colour. `npx wmds-check` reads the app's CSS and fails a hex pair under that (`brand-contrast`, an error). Pairs it can't resolve (a `var()` or `color-mix`) aren't checked, so check those yourself.
|
|
361
|
+
|
|
362
|
+
```css
|
|
363
|
+
/* brand.css — the reference client (Foundations → Brand kit → Theme a client) */
|
|
364
|
+
:root {
|
|
365
|
+
--font-display: "Anton", Impact, sans-serif;
|
|
366
|
+
--font-display-weight: 400;
|
|
367
|
+
--font-display-tracking: 0;
|
|
368
|
+
--font-display-case: uppercase;
|
|
369
|
+
--text-display-hero-size: clamp(4rem, 17.5vw, 18rem);
|
|
370
|
+
--color-brand-surface: #120d08;
|
|
371
|
+
--color-on-brand-surface: #f3e3c3;
|
|
372
|
+
--color-on-brand-surface-muted: #a89a80;
|
|
373
|
+
--color-field: #e2522b;
|
|
374
|
+
--color-on-field: #120d08;
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Sections then use **Band** `tone="inverse"` / `"field"`: it re-skins everything inside and bleeds full width on the page's own columns (**Components → Band**).
|
|
379
|
+
|
|
319
380
|
### Brand override (`--color-primary`)
|
|
320
381
|
|
|
321
382
|
The **only** per-app color hook is the primary brand color. The WhatMatters default palette stays in **`src/theme/colors.css`**; an app overrides it by redefining the allowlisted tokens on `:root` (light) and `[data-theme="dark"]` (dark) in a stylesheet imported **after** `@thewhatmatters/wmds/styles.css`.
|
|
@@ -359,4 +420,4 @@ Rules:
|
|
|
359
420
|
- Set both the light and the dark value. Leaving `[data-theme="dark"]` unset falls back to the WhatMatters dark primary, not to your light value.
|
|
360
421
|
- Check contrast for `--color-on-primary` on your primary and for `--color-primary` on `--color-body` in both themes.
|
|
361
422
|
|
|
362
|
-
**Not overridable per app
|
|
423
|
+
**Not overridable per app** (beyond the Brand kit and Design knobs above): brand navy (`--color-brand` `#011272`, `--color-on-brand`, `--color-brand-outline`, `--color-brand-soft`, `--color-on-brand-hover`), accent (`--color-accent*`, `--color-on-accent`), status (`--color-error*`, `--color-success*`, `--color-warning*`, `--color-info*`), chart series, typography (`--font-*`, `--line-height-*`, `type-*`, except the Brand kit knobs), and grid (`--grid-*`, `--spacing`). Those roles are the shared WMDS contract — change them in `src/theme/`, rebuild, and bump the package. Do **not** fork component visuals with per-app utility overrides — extend WMDS via new variants/patterns in the design system repo.
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ npm run validate:composition # molecules/organisms must compose atoms (CI)
|
|
|
48
48
|
| Atoms | `Avatar`, `Badge`, `Button`, `Checkbox`, `IconButton`, `Input`, `Kbd`, `Radio`, `RiveHand`, `Skeleton`, `Status`, `Switch`, `TextArea`, `TextLink`, `Tooltip` |
|
|
49
49
|
| Molecules | `Accordion`, `CalEmbed`, `Card`, `ChatQa`, `CheckboxGroup`, `Chip`, `DisplayControls`, `Dropdown`, `Field`, `FloatingActionButton`, `HeroIntro`, `IntakeForm`, `NavList`, `PageHeader`, `PillGroup`, `PromptBar`, `RadioGroup`, `Search`, `Select`, `SelectableCard`, `SegmentedControl`, `Stat`, `StepProgress`, `TaskRows`, `TextSequence` |
|
|
50
50
|
| Organisms | `Carousel`, `Chart`, `Confetti`, `Dialog`, `FooterReveal`, `HeroTileStack`, `IntakeConfirmation`, `IntakeModal`, `MoreMenu`, `Panel`, `ScrollHorizontal`, `Sheet`, `SiteNav`, `Tab`, `Toast` |
|
|
51
|
-
| Planned, not built | `Pagination
|
|
51
|
+
| Planned, not built | `Pagination` |
|
|
52
52
|
|
|
53
53
|
The list is `src/package.manifest.ts`. `npm run build` fails when it and `src/index.ts` disagree.
|
|
54
54
|
|
package/bin/wmds-check.mjs
CHANGED
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
* content, data, handlers, and exports may change
|
|
19
19
|
* pattern-stale warn a pasted pattern's header names an older package version
|
|
20
20
|
* pattern-removed error a pasted pattern's id no longer ships
|
|
21
|
+
* brand-contrast error a brand-kit pair in the app's CSS under WCAG AA 4.5:1: --color-on-X (and
|
|
22
|
+
* --color-on-X-muted) on --color-X, for primary, brand-surface and field,
|
|
23
|
+
* when both are hex values in the same rule
|
|
21
24
|
*
|
|
22
25
|
* Silence one line with a trailing or preceding comment: `wmds-check-ignore raw-control <reason>`.
|
|
23
26
|
* The checks are line-based heuristics, not a parser: they favor few false negatives, and every
|
|
@@ -50,6 +53,7 @@ try {
|
|
|
50
53
|
|
|
51
54
|
const skipDirs = new Set(["node_modules", ".next", "dist", "build", "out", ".git", "coverage", "storybook-static"]);
|
|
52
55
|
const files = [];
|
|
56
|
+
const cssFiles = [];
|
|
53
57
|
const walk = (dir) => {
|
|
54
58
|
for (const name of readdirSync(dir)) {
|
|
55
59
|
const file = path.join(dir, name);
|
|
@@ -57,6 +61,8 @@ const walk = (dir) => {
|
|
|
57
61
|
if (!skipDirs.has(name)) walk(file);
|
|
58
62
|
} else if (/\.(tsx|jsx|ts|js|mdx)$/.test(name) && !/\.(test|spec|stories)\./.test(name)) {
|
|
59
63
|
files.push(file);
|
|
64
|
+
} else if (/\.css$/.test(name)) {
|
|
65
|
+
cssFiles.push(file);
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
68
|
};
|
|
@@ -259,6 +265,47 @@ for (const file of files) {
|
|
|
259
265
|
}
|
|
260
266
|
}
|
|
261
267
|
|
|
268
|
+
// Brand-kit contrast. Innermost rule blocks only, so a pair inside @media is read as its own rule.
|
|
269
|
+
const brandPairs = ["primary", "brand-surface", "field"];
|
|
270
|
+
const channel = (value) => {
|
|
271
|
+
const c = value / 255;
|
|
272
|
+
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
273
|
+
};
|
|
274
|
+
const luminance = (hex) => {
|
|
275
|
+
let digits = hex.slice(1);
|
|
276
|
+
if (digits.length === 3 || digits.length === 4) digits = [...digits].map((d) => d + d).join("");
|
|
277
|
+
if (digits.length === 8 && digits.slice(6) !== "ff") return null; // translucent: depends on what's behind
|
|
278
|
+
const [r, g, b] = [0, 2, 4].map((i) => parseInt(digits.slice(i, i + 2), 16));
|
|
279
|
+
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
280
|
+
};
|
|
281
|
+
const contrast = (a, b) => {
|
|
282
|
+
const [la, lb] = [luminance(a), luminance(b)];
|
|
283
|
+
if (la === null || lb === null) return null;
|
|
284
|
+
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
285
|
+
};
|
|
286
|
+
for (const file of cssFiles) {
|
|
287
|
+
const source = readFileSync(file, "utf8");
|
|
288
|
+
const lines = source.split("\n");
|
|
289
|
+
for (const block of source.matchAll(/\{([^{}]*)\}/g)) {
|
|
290
|
+
const values = new Map();
|
|
291
|
+
for (const declaration of block[1].matchAll(/(--color-[\w-]+)\s*:\s*(#[0-9a-fA-F]{3,8})\b/g)) {
|
|
292
|
+
const index = block.index + 1 + declaration.index;
|
|
293
|
+
values.set(declaration[1], { hex: declaration[2].toLowerCase(), line: source.slice(0, index).split("\n").length });
|
|
294
|
+
}
|
|
295
|
+
for (const name of brandPairs) {
|
|
296
|
+
const background = values.get(`--color-${name}`);
|
|
297
|
+
if (!background) continue;
|
|
298
|
+
for (const ink of [`--color-on-${name}`, `--color-on-${name}-muted`]) {
|
|
299
|
+
const on = values.get(ink);
|
|
300
|
+
const ratio = on && contrast(on.hex, background.hex);
|
|
301
|
+
if (ratio && ratio < 4.5 && !ignored(lines, on.line - 1, "brand-contrast")) {
|
|
302
|
+
report(file, on.line, "brand-contrast", "error", `${ink} ${on.hex} on --color-${name} ${background.hex} is ${ratio.toFixed(2)}:1 — text needs 4.5:1`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
262
309
|
const errors = findings.filter((finding) => finding.severity === "error").length;
|
|
263
310
|
const warnings = findings.length - errors;
|
|
264
311
|
|
package/dist/colors.css
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* Brand actions: primary / on-primary (WhatMatters — separate from accent).
|
|
10
10
|
*/
|
|
11
11
|
:root {
|
|
12
|
+
/* Native UI (scrollbars, form controls, autofill) follows the theme. */
|
|
13
|
+
color-scheme: light;
|
|
14
|
+
|
|
12
15
|
/* Surface hierarchy — body/muted lightened for softer inset wells (test) */
|
|
13
16
|
--color-background-body: #f8f8f8;
|
|
14
17
|
--color-background-surface: #ffffff;
|
|
@@ -102,6 +105,23 @@
|
|
|
102
105
|
*/
|
|
103
106
|
--color-brand-tint: color-mix(in srgb, var(--color-brand) 10%, transparent);
|
|
104
107
|
|
|
108
|
+
/*
|
|
109
|
+
* Brand kit colour roles (apps may set these on :root, like --color-primary; CONSUMING.md → Brand kit).
|
|
110
|
+
* Read by **Band** `tone` (the `[data-tone]` scopes at the end of this file). Two pairs only, so every
|
|
111
|
+
* on-colour stays checkable: `npx wmds-check` fails a pair under 4.5:1.
|
|
112
|
+
* - brand surface: a dark section in the brand's own colour (`tone="inverse"`). Default: the navy.
|
|
113
|
+
* - field: a saturated section colour (`tone="field"`). Default: the primary.
|
|
114
|
+
* `*-muted` is the secondary text step on that colour; it must clear 4.5:1 too.
|
|
115
|
+
*/
|
|
116
|
+
--color-brand-surface: var(--color-brand);
|
|
117
|
+
--color-on-brand-surface: var(--color-on-brand);
|
|
118
|
+
--color-field: var(--color-primary);
|
|
119
|
+
--color-on-field: var(--color-on-primary);
|
|
120
|
+
/*
|
|
121
|
+
* The muted steps (`--color-on-brand-surface-muted`, `--color-on-field-muted`) are optional and read
|
|
122
|
+
* with fallbacks in the tone scopes below, so they follow their pair wherever it's set.
|
|
123
|
+
*/
|
|
124
|
+
|
|
105
125
|
/* Derived from --color-primary — 62% clears WCAG 3:1 on white and #f8f8f8 */
|
|
106
126
|
--color-focus-ring: color-mix(in srgb, var(--color-primary) 62%, transparent);
|
|
107
127
|
|
|
@@ -155,6 +175,7 @@
|
|
|
155
175
|
}
|
|
156
176
|
|
|
157
177
|
[data-theme="dark"] {
|
|
178
|
+
color-scheme: dark;
|
|
158
179
|
--color-background-body: #1b1b1b;
|
|
159
180
|
--color-background-surface: #262626;
|
|
160
181
|
--color-background-card: #1b1b1b;
|
|
@@ -290,3 +311,74 @@
|
|
|
290
311
|
|
|
291
312
|
--color-skeleton: color-mix(in srgb, var(--color-text-primary) 8%, var(--color-background-muted));
|
|
292
313
|
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Band tones — `[data-tone]` remaps the surface, text, border and fill roles to a brand-kit pair,
|
|
317
|
+
* the way `[data-theme]` swaps the palette, so every component inside re-skins. Set by **Band**.
|
|
318
|
+
* `inverse` also carries `data-theme="dark"`, so status, chart, shadow and focus roles take their dark
|
|
319
|
+
* values, and this block (later in the file) overrides the surfaces and text on the same element.
|
|
320
|
+
* Status colours are not remapped on `field`: keep validation messages off a field band.
|
|
321
|
+
*/
|
|
322
|
+
[data-tone="inverse"] {
|
|
323
|
+
--tone-bg: var(--color-brand-surface);
|
|
324
|
+
--tone-fg: var(--color-on-brand-surface);
|
|
325
|
+
/* Default: 72% of the ink on the surface (on the navy, about #b8bdd8, 8.7:1). */
|
|
326
|
+
--tone-muted: var(
|
|
327
|
+
--color-on-brand-surface-muted,
|
|
328
|
+
color-mix(in srgb, var(--color-on-brand-surface) 72%, var(--color-brand-surface))
|
|
329
|
+
);
|
|
330
|
+
/* Cards lift 6% toward the ink; the muted step keeps its contrast on the light side. */
|
|
331
|
+
--tone-raised: color-mix(in srgb, var(--tone-fg) 6%, var(--tone-bg));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
[data-tone="field"] {
|
|
335
|
+
--tone-bg: var(--color-field);
|
|
336
|
+
--tone-fg: var(--color-on-field);
|
|
337
|
+
/* Default: the ink itself, since a lighter step on a mid-tone field would fall under 4.5:1. */
|
|
338
|
+
--tone-muted: var(--color-on-field-muted, var(--color-on-field));
|
|
339
|
+
/* A tinted card would pull a mid-tone field under 4.5:1, so cards sit flat on the field. */
|
|
340
|
+
--tone-raised: var(--tone-bg);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
[data-tone="inverse"],
|
|
344
|
+
[data-tone="field"] {
|
|
345
|
+
--color-background-body: var(--tone-bg);
|
|
346
|
+
--color-background-surface: var(--tone-raised);
|
|
347
|
+
--color-background-card: var(--tone-raised);
|
|
348
|
+
--color-background-popover: var(--tone-raised);
|
|
349
|
+
--color-background-muted: var(--tone-bg);
|
|
350
|
+
|
|
351
|
+
--color-text-primary: var(--tone-fg);
|
|
352
|
+
--color-text-secondary: var(--tone-muted);
|
|
353
|
+
--color-text-disabled: color-mix(in srgb, var(--tone-fg) 45%, var(--tone-bg));
|
|
354
|
+
--color-text-accent: var(--tone-fg);
|
|
355
|
+
|
|
356
|
+
--color-accent: var(--tone-fg);
|
|
357
|
+
--color-accent-muted: color-mix(in srgb, var(--tone-fg) 10%, var(--tone-bg));
|
|
358
|
+
--color-on-accent: var(--tone-bg);
|
|
359
|
+
|
|
360
|
+
--color-neutral: color-mix(in srgb, var(--tone-fg) 10%, transparent);
|
|
361
|
+
--color-overlay-hover: color-mix(in srgb, var(--tone-fg) 8%, transparent);
|
|
362
|
+
--color-overlay-pressed: color-mix(in srgb, var(--tone-fg) 14%, transparent);
|
|
363
|
+
/* stateColors.css derives these on :root; derive them again here so they follow the tone. */
|
|
364
|
+
--color-ghost-foreground: var(--tone-fg);
|
|
365
|
+
--color-secondary-hover: var(--color-overlay-hover);
|
|
366
|
+
--color-secondary-active: var(--color-overlay-pressed);
|
|
367
|
+
--color-ghost-hover: var(--color-overlay-hover);
|
|
368
|
+
--color-ghost-active: var(--color-overlay-pressed);
|
|
369
|
+
|
|
370
|
+
--color-border: color-mix(in srgb, var(--tone-fg) 16%, transparent);
|
|
371
|
+
--color-border-emphasized: color-mix(in srgb, var(--tone-fg) 32%, transparent);
|
|
372
|
+
/* Control edges need 3:1; the muted text step clears 4.5:1. */
|
|
373
|
+
--color-border-control: var(--tone-muted);
|
|
374
|
+
--color-fill-selected: color-mix(in srgb, var(--tone-fg) 14%, var(--tone-bg));
|
|
375
|
+
--color-elevation-edge: color-mix(in srgb, var(--tone-fg) 16%, transparent);
|
|
376
|
+
--color-elevation-surface: var(--tone-raised);
|
|
377
|
+
|
|
378
|
+
--color-skeleton: color-mix(in srgb, var(--tone-fg) 12%, var(--tone-bg));
|
|
379
|
+
/* The ink itself: a primary-tinted ring can vanish on a saturated field. */
|
|
380
|
+
--color-focus-ring: var(--tone-fg);
|
|
381
|
+
|
|
382
|
+
background-color: var(--color-background-body);
|
|
383
|
+
color: var(--color-text-primary);
|
|
384
|
+
}
|