@thewhatmatters/wmds 0.4.0 → 0.4.2

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.
Files changed (250) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/CONSUMING.md +9 -4
  3. package/dist/collapse.css +9 -1
  4. package/dist/colors.css +7 -0
  5. package/dist/components/atoms/Badge/Badge.d.ts +16 -4
  6. package/dist/components/atoms/Badge/Badge.d.ts.map +1 -1
  7. package/dist/components/atoms/Badge/Badge.js +54 -42
  8. package/dist/components/atoms/Badge/badgeStyles.d.ts +12 -2
  9. package/dist/components/atoms/Badge/badgeStyles.d.ts.map +1 -1
  10. package/dist/components/atoms/Badge/badgeStyles.js +24 -13
  11. package/dist/components/atoms/Button/Button.d.ts +11 -3
  12. package/dist/components/atoms/Button/Button.d.ts.map +1 -1
  13. package/dist/components/atoms/Button/Button.js +118 -98
  14. package/dist/components/atoms/Button/ButtonStatusButton.d.ts +3 -1
  15. package/dist/components/atoms/Button/ButtonStatusButton.d.ts.map +1 -1
  16. package/dist/components/atoms/Button/ButtonStatusButton.js +19 -18
  17. package/dist/components/atoms/Button/ButtonStatusContent.d.ts +7 -1
  18. package/dist/components/atoms/Button/ButtonStatusContent.d.ts.map +1 -1
  19. package/dist/components/atoms/Button/ButtonStatusContent.js +9 -6
  20. package/dist/components/atoms/Button/buttonStatusStyles.d.ts +5 -0
  21. package/dist/components/atoms/Button/buttonStatusStyles.d.ts.map +1 -1
  22. package/dist/components/atoms/Button/buttonStatusStyles.js +10 -10
  23. package/dist/components/atoms/Button/buttonStyles.d.ts +2 -0
  24. package/dist/components/atoms/Button/buttonStyles.d.ts.map +1 -1
  25. package/dist/components/atoms/Button/buttonStyles.js +12 -12
  26. package/dist/components/atoms/Checkbox/Checkbox.d.ts +7 -0
  27. package/dist/components/atoms/Checkbox/Checkbox.d.ts.map +1 -1
  28. package/dist/components/atoms/Checkbox/Checkbox.js +86 -69
  29. package/dist/components/atoms/Checkbox/checkboxStyles.d.ts +6 -0
  30. package/dist/components/atoms/Checkbox/checkboxStyles.d.ts.map +1 -1
  31. package/dist/components/atoms/Checkbox/checkboxStyles.js +11 -11
  32. package/dist/components/atoms/IconButton/IconButton.d.ts +5 -0
  33. package/dist/components/atoms/IconButton/IconButton.d.ts.map +1 -1
  34. package/dist/components/atoms/IconButton/IconButton.js +73 -68
  35. package/dist/components/atoms/IconButton/iconButtonStyles.d.ts +5 -0
  36. package/dist/components/atoms/IconButton/iconButtonStyles.d.ts.map +1 -1
  37. package/dist/components/atoms/IconButton/iconButtonStyles.js +4 -4
  38. package/dist/components/atoms/Kbd/useKbdChoiceKeys.d.ts +13 -4
  39. package/dist/components/atoms/Kbd/useKbdChoiceKeys.d.ts.map +1 -1
  40. package/dist/components/atoms/Kbd/useKbdChoiceKeys.js +32 -13
  41. package/dist/components/atoms/Prose/Prose.d.ts +23 -0
  42. package/dist/components/atoms/Prose/Prose.d.ts.map +1 -0
  43. package/dist/components/atoms/Prose/Prose.js +15 -0
  44. package/dist/components/atoms/Prose/proseStyles.d.ts +17 -0
  45. package/dist/components/atoms/Prose/proseStyles.d.ts.map +1 -0
  46. package/dist/components/atoms/Prose/proseStyles.js +16 -0
  47. package/dist/components/atoms/SectionCaption/SectionCaption.d.ts +26 -0
  48. package/dist/components/atoms/SectionCaption/SectionCaption.d.ts.map +1 -0
  49. package/dist/components/atoms/SectionCaption/SectionCaption.js +25 -0
  50. package/dist/components/atoms/SectionCaption/sectionCaptionStyles.d.ts +11 -0
  51. package/dist/components/atoms/SectionCaption/sectionCaptionStyles.d.ts.map +1 -0
  52. package/dist/components/atoms/SectionCaption/sectionCaptionStyles.js +12 -0
  53. package/dist/components/atoms/TextLink/TextLink.d.ts +10 -2
  54. package/dist/components/atoms/TextLink/TextLink.d.ts.map +1 -1
  55. package/dist/components/atoms/TextLink/TextLink.js +15 -15
  56. package/dist/components/atoms/TextLink/textLinkStyles.d.ts +13 -0
  57. package/dist/components/atoms/TextLink/textLinkStyles.d.ts.map +1 -1
  58. package/dist/components/atoms/TextLink/textLinkStyles.js +7 -3
  59. package/dist/components/molecules/Accordion/Accordion.d.ts +6 -1
  60. package/dist/components/molecules/Accordion/Accordion.d.ts.map +1 -1
  61. package/dist/components/molecules/Accordion/Accordion.js +57 -50
  62. package/dist/components/molecules/Accordion/accordionStyles.d.ts +9 -0
  63. package/dist/components/molecules/Accordion/accordionStyles.d.ts.map +1 -1
  64. package/dist/components/molecules/Accordion/accordionStyles.js +2 -2
  65. package/dist/components/molecules/Breadcrumb/Breadcrumb.d.ts +59 -0
  66. package/dist/components/molecules/Breadcrumb/Breadcrumb.d.ts.map +1 -0
  67. package/dist/components/molecules/Breadcrumb/Breadcrumb.js +188 -0
  68. package/dist/components/molecules/Breadcrumb/breadcrumbStyles.d.ts +21 -0
  69. package/dist/components/molecules/Breadcrumb/breadcrumbStyles.d.ts.map +1 -0
  70. package/dist/components/molecules/Breadcrumb/breadcrumbStyles.js +11 -0
  71. package/dist/components/molecules/CheckboxGroup/CheckboxGroupItem.d.ts +5 -1
  72. package/dist/components/molecules/CheckboxGroup/CheckboxGroupItem.d.ts.map +1 -1
  73. package/dist/components/molecules/CheckboxGroup/CheckboxGroupItem.js +12 -10
  74. package/dist/components/molecules/DescriptionList/DescriptionList.d.ts +37 -0
  75. package/dist/components/molecules/DescriptionList/DescriptionList.d.ts.map +1 -0
  76. package/dist/components/molecules/DescriptionList/DescriptionList.js +50 -0
  77. package/dist/components/molecules/DescriptionList/descriptionListStyles.d.ts +21 -0
  78. package/dist/components/molecules/DescriptionList/descriptionListStyles.d.ts.map +1 -0
  79. package/dist/components/molecules/DescriptionList/descriptionListStyles.js +27 -0
  80. package/dist/components/molecules/Dropdown/DropdownItem.d.ts +7 -2
  81. package/dist/components/molecules/Dropdown/DropdownItem.d.ts.map +1 -1
  82. package/dist/components/molecules/Dropdown/DropdownItem.js +40 -24
  83. package/dist/components/molecules/IndexList/IndexList.d.ts +58 -0
  84. package/dist/components/molecules/IndexList/IndexList.d.ts.map +1 -0
  85. package/dist/components/molecules/IndexList/IndexList.js +96 -0
  86. package/dist/components/molecules/IndexList/indexListStyles.d.ts +29 -0
  87. package/dist/components/molecules/IndexList/indexListStyles.d.ts.map +1 -0
  88. package/dist/components/molecules/IndexList/indexListStyles.js +24 -0
  89. package/dist/components/molecules/PromptBar/promptBarStyles.d.ts +3 -2
  90. package/dist/components/molecules/PromptBar/promptBarStyles.d.ts.map +1 -1
  91. package/dist/components/molecules/PromptBar/promptBarStyles.js +1 -1
  92. package/dist/components/organisms/ChatDock/ChatDock.d.ts +120 -10
  93. package/dist/components/organisms/ChatDock/ChatDock.d.ts.map +1 -1
  94. package/dist/components/organisms/ChatDock/ChatDock.js +654 -212
  95. package/dist/components/organisms/ChatDock/ChatDockGate.d.ts +52 -0
  96. package/dist/components/organisms/ChatDock/ChatDockGate.d.ts.map +1 -0
  97. package/dist/components/organisms/ChatDock/ChatDockGate.js +161 -0
  98. package/dist/components/organisms/ChatDock/chatDockGateStyles.d.ts +16 -0
  99. package/dist/components/organisms/ChatDock/chatDockGateStyles.d.ts.map +1 -0
  100. package/dist/components/organisms/ChatDock/chatDockGateStyles.js +8 -0
  101. package/dist/components/organisms/ChatDock/chatDockStyles.d.ts +60 -10
  102. package/dist/components/organisms/ChatDock/chatDockStyles.d.ts.map +1 -1
  103. package/dist/components/organisms/ChatDock/chatDockStyles.js +15 -9
  104. package/dist/components/organisms/ChatDock/chatDockViewport.d.ts +21 -0
  105. package/dist/components/organisms/ChatDock/chatDockViewport.d.ts.map +1 -0
  106. package/dist/components/organisms/ChatDock/chatDockViewport.js +46 -0
  107. package/dist/components/organisms/SiteNav/SiteNav.js +86 -86
  108. package/dist/components/organisms/Toast/Toast.js +5 -5
  109. package/dist/index.d.ts +8 -3
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +124 -112
  112. package/dist/lib/compositionAudit.d.ts.map +1 -1
  113. package/dist/lib/typography.d.ts +2 -2
  114. package/dist/lib/typography.d.ts.map +1 -1
  115. package/dist/lib/typography.js +27 -1
  116. package/dist/styles.css +1 -1
  117. package/dist/theme.css +1 -0
  118. package/dist/typography.css +34 -0
  119. package/docs/README.md +1 -1
  120. package/docs/component-contracts.md +15 -0
  121. package/docs/components.md +17 -12
  122. package/docs/exports.json +85 -4
  123. package/docs/patterns/components-accordion--flush.tsx +29 -0
  124. package/docs/patterns/components-avatar--dropdown-row.tsx +1 -1
  125. package/docs/patterns/components-badge--outline-tags.tsx +25 -0
  126. package/docs/patterns/components-badge--with-avatar.tsx +1 -1
  127. package/docs/patterns/components-breadcrumb--editorial.tsx +9 -0
  128. package/docs/patterns/components-breadcrumb--long-path.tsx +9 -0
  129. package/docs/patterns/components-breadcrumb--path.tsx +9 -0
  130. package/docs/patterns/components-breadcrumb--router-links.tsx +10 -0
  131. package/docs/patterns/components-button-button--copy-button.tsx +39 -0
  132. package/docs/patterns/components-button-button--destructive-action.tsx +1 -1
  133. package/docs/patterns/components-button-button--external-link.tsx +30 -0
  134. package/docs/patterns/components-button-button--ghost-action.tsx +1 -1
  135. package/docs/patterns/components-button-button--inverse-action.tsx +1 -1
  136. package/docs/patterns/components-button-button--link-render.tsx +1 -1
  137. package/docs/patterns/components-button-button--multi-state-badge.tsx +1 -1
  138. package/docs/patterns/components-button-button--outline-mono.tsx +1 -1
  139. package/docs/patterns/components-button-button--primary-action.tsx +1 -1
  140. package/docs/patterns/components-button-button--row-hug.tsx +1 -1
  141. package/docs/patterns/components-button-button--row-layout.tsx +1 -1
  142. package/docs/patterns/components-button-button--secondary-action.tsx +1 -1
  143. package/docs/patterns/components-button-button--suggestion-pills.tsx +1 -1
  144. package/docs/patterns/components-button-button--with-count.tsx +1 -1
  145. package/docs/patterns/components-button-button--with-icon.tsx +1 -1
  146. package/docs/patterns/components-button-floatingactionbutton--vertical-actions.tsx +1 -1
  147. package/docs/patterns/components-calembed--pattern-skip-in-footer.tsx +1 -1
  148. package/docs/patterns/components-card-card--body-slot-occupancy-history.tsx +1 -1
  149. package/docs/patterns/components-card-card--headerless-media.tsx +1 -1
  150. package/docs/patterns/components-card-card--outlined-layout.tsx +1 -1
  151. package/docs/patterns/components-card-card--pinned-height.tsx +1 -1
  152. package/docs/patterns/components-card-selectablecard--selectable-cards-pattern.tsx +1 -1
  153. package/docs/patterns/components-chart--audience-activity-heatmap.tsx +1 -1
  154. package/docs/patterns/components-chart--cartesian-no-data-gaps.tsx +1 -1
  155. package/docs/patterns/components-chart--occupancy-history-in-card.tsx +1 -1
  156. package/docs/patterns/components-chart--occupancy-in-card.tsx +1 -1
  157. package/docs/patterns/components-chart--post-reach-distribution.tsx +1 -1
  158. package/docs/patterns/components-chart--ranked-audience-breakdown.tsx +1 -1
  159. package/docs/patterns/components-chart--unit-grid-composition.tsx +1 -1
  160. package/docs/patterns/components-chatdock--pattern-chat-dock.tsx +101 -11
  161. package/docs/patterns/components-chatdock--pattern-start-project-gate.tsx +180 -0
  162. package/docs/patterns/components-chatqa--pattern-qa-with-reply.tsx +1 -1
  163. package/docs/patterns/components-checkbox-checkbox--default.tsx +1 -1
  164. package/docs/patterns/components-checkbox-checkbox--with-count.tsx +19 -0
  165. package/docs/patterns/components-checkbox-checkbox--with-description.tsx +1 -1
  166. package/docs/patterns/components-checkbox-checkboxgroup--default.tsx +1 -1
  167. package/docs/patterns/components-checkbox-checkboxgroup--option-counts.tsx +17 -0
  168. package/docs/patterns/components-descriptionlist--details.tsx +21 -0
  169. package/docs/patterns/components-descriptionlist--post-metadata.tsx +52 -0
  170. package/docs/patterns/components-dialog--alert-dialog-destructive.tsx +1 -1
  171. package/docs/patterns/components-dialog--default-dialog.tsx +1 -1
  172. package/docs/patterns/components-dialog--form-dialog.tsx +1 -1
  173. package/docs/patterns/components-displaycontrols--page-utilities.tsx +1 -1
  174. package/docs/patterns/components-field--horizontal-input.tsx +1 -1
  175. package/docs/patterns/components-field--horizontal-text-area.tsx +1 -1
  176. package/docs/patterns/components-field--multi-control.tsx +1 -1
  177. package/docs/patterns/components-footerreveal--marketing-hero-pattern.tsx +1 -1
  178. package/docs/patterns/components-footerreveal--marketing-hero-ruled-pattern.tsx +1 -1
  179. package/docs/patterns/components-footerreveal--marketing-hero-with-gallery-intro.tsx +1 -1
  180. package/docs/patterns/components-footerreveal--marketing-page-pattern.tsx +1 -1
  181. package/docs/patterns/components-footerreveal--ruled-grid-footer-pattern.tsx +1 -1
  182. package/docs/patterns/components-footerreveal--sequenced-marketing-hero.tsx +1 -1
  183. package/docs/patterns/components-herointro--two-line-intro.tsx +1 -1
  184. package/docs/patterns/components-herotilestack--marketing-hero-pattern.tsx +1 -1
  185. package/docs/patterns/components-herotilestack--marketing-hero-text-sequence.tsx +1 -1
  186. package/docs/patterns/components-herotilestack--marketing-hero-with-gallery-intro.tsx +1 -1
  187. package/docs/patterns/components-indexlist--post-index.tsx +36 -0
  188. package/docs/patterns/components-intakeconfirmation--booked-pattern.tsx +1 -1
  189. package/docs/patterns/components-intakeconfirmation--emailed-pattern.tsx +1 -1
  190. package/docs/patterns/components-intakeform--about-you-pattern.tsx +1 -1
  191. package/docs/patterns/components-intakemodal--intake-modal-pattern.tsx +1 -1
  192. package/docs/patterns/components-kbd--command-row.tsx +1 -1
  193. package/docs/patterns/components-kbd--numbered-choices.tsx +1 -1
  194. package/docs/patterns/components-kbd--shortcut.tsx +1 -1
  195. package/docs/patterns/components-moremenu--action-menu.tsx +1 -1
  196. package/docs/patterns/components-moremenu--in-card-header.tsx +1 -1
  197. package/docs/patterns/components-navlist--side-nav-settings.tsx +1 -1
  198. package/docs/patterns/components-pageheader--app-header-pattern.tsx +1 -1
  199. package/docs/patterns/components-pageheader--page-section-header.tsx +1 -1
  200. package/docs/patterns/components-pageheader--toolbar-header.tsx +1 -1
  201. package/docs/patterns/components-panel--end-detail-rail.tsx +1 -1
  202. package/docs/patterns/components-pillgroup--pill-group-pattern.tsx +1 -1
  203. package/docs/patterns/components-promptbar--pattern-prompt-bar.tsx +1 -1
  204. package/docs/patterns/components-prose--article-body.tsx +11 -0
  205. package/docs/patterns/components-radio-radiogroup--default.tsx +1 -1
  206. package/docs/patterns/components-scrollhorizontal--project-gallery-pattern.tsx +1 -1
  207. package/docs/patterns/components-scrollhorizontal--with-intro.tsx +1 -1
  208. package/docs/patterns/components-sectioncaption--column-caption.tsx +16 -0
  209. package/docs/patterns/components-sectioncaption--with-action.tsx +19 -0
  210. package/docs/patterns/components-sheet--bottom-sheet.tsx +1 -1
  211. package/docs/patterns/components-sheet--end-sheet.tsx +1 -1
  212. package/docs/patterns/components-sitenav--brand-and-cta.tsx +1 -1
  213. package/docs/patterns/components-sitenav--centered-links.tsx +1 -1
  214. package/docs/patterns/components-sitenav--compact.tsx +1 -1
  215. package/docs/patterns/components-sitenav--marketing-header.tsx +1 -1
  216. package/docs/patterns/components-sitenav--mega-menu.tsx +1 -1
  217. package/docs/patterns/components-stat--insights-row.tsx +1 -1
  218. package/docs/patterns/components-stepprogress--step-progress-pattern.tsx +1 -1
  219. package/docs/patterns/components-switch--default.tsx +1 -1
  220. package/docs/patterns/components-switch--settings-row.tsx +1 -1
  221. package/docs/patterns/components-tab--settings-pages.tsx +1 -1
  222. package/docs/patterns/components-taskrows--action-details.tsx +1 -1
  223. package/docs/patterns/components-taskrows--capsules.tsx +1 -1
  224. package/docs/patterns/components-taskrows--card-with-status-rows.tsx +1 -1
  225. package/docs/patterns/components-taskrows--controlled-expand.tsx +1 -1
  226. package/docs/patterns/components-taskrows--custom-slots.tsx +1 -1
  227. package/docs/patterns/components-taskrows--status-list.tsx +1 -1
  228. package/docs/patterns/components-taskrows--tag-chips.tsx +1 -1
  229. package/docs/patterns/components-textlink--external-destination.tsx +1 -1
  230. package/docs/patterns/components-textlink--inline-prose.tsx +1 -1
  231. package/docs/patterns/components-textlink--quiet-title.tsx +15 -0
  232. package/docs/patterns/components-textlink--supporting-copy.tsx +1 -1
  233. package/docs/patterns/components-textsequence--default.tsx +1 -1
  234. package/docs/patterns/components-textsequence--reduced-motion.tsx +1 -1
  235. package/docs/patterns/components-textsequence--shapes.tsx +1 -1
  236. package/docs/patterns/components-toast--stacked-notifications.tsx +1 -1
  237. package/docs/patterns/components-tooltip--controlled.tsx +1 -1
  238. package/docs/patterns/components-tooltip--disabled-control.tsx +1 -1
  239. package/docs/patterns/components-tooltip--icon-action.tsx +1 -1
  240. package/docs/patterns/components-tooltip--keyboard-shortcut.tsx +1 -1
  241. package/docs/patterns/components-tooltip--placement.tsx +1 -1
  242. package/docs/patterns/foundations-scroll-fade--horizontal-rail.tsx +1 -1
  243. package/docs/patterns/foundations-scroll-fade--overflowing-list.tsx +1 -1
  244. package/docs/patterns/guides-chart-explorations--comparison-gallery.tsx +1 -1
  245. package/docs/patterns/guides-filter-panel--filtered-index.tsx +174 -0
  246. package/docs/patterns/guides-overlay-flows--notification-preferences.tsx +1 -1
  247. package/docs/patterns/guides-post-page--post-page.tsx +148 -0
  248. package/docs/patterns/guides-profile-typography--editorial-profile.tsx +1 -1
  249. package/docs/patterns/index.json +161 -1
  250. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- // @thewhatmatters/wmds@0.4.0 · Pattern — placement
1
+ // @thewhatmatters/wmds@0.4.2 · Pattern — placement
2
2
  // Storybook: Components/Tooltip → Pattern — placement (?path=/story/components-tooltip--placement)
3
3
  // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
4
 
@@ -1,4 +1,4 @@
1
- // @thewhatmatters/wmds@0.4.0 · Pattern — horizontal rail
1
+ // @thewhatmatters/wmds@0.4.2 · Pattern — horizontal rail
2
2
  // Storybook: Foundations/Scroll fade → Pattern — horizontal rail (?path=/story/foundations-scroll-fade--horizontal-rail)
3
3
  // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
4
 
@@ -1,4 +1,4 @@
1
- // @thewhatmatters/wmds@0.4.0 · Pattern — overflowing list
1
+ // @thewhatmatters/wmds@0.4.2 · Pattern — overflowing list
2
2
  // Storybook: Foundations/Scroll fade → Pattern — overflowing list (?path=/story/foundations-scroll-fade--overflowing-list)
3
3
  // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
4
 
@@ -1,4 +1,4 @@
1
- // @thewhatmatters/wmds@0.4.0 · Pattern — chart comparison gallery
1
+ // @thewhatmatters/wmds@0.4.2 · Pattern — chart comparison gallery
2
2
  // Storybook: Guides/Chart explorations → Pattern — chart comparison gallery (?path=/story/guides-chart-explorations--comparison-gallery)
3
3
  // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
4
 
@@ -0,0 +1,174 @@
1
+ // @thewhatmatters/wmds@0.4.2 · Pattern — filtered index
2
+ // Storybook: Guides/Filter panel → Pattern — filtered index (?path=/story/guides-filter-panel--filtered-index)
3
+ // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
+
5
+ import { useId, useState } from "react";
6
+ import { SlidersHorizontal } from "lucide-react";
7
+ import { Accordion, Button, CheckboxGroup, IndexList, SectionCaption, Sheet } from "@thewhatmatters/wmds";
8
+
9
+ export interface FilterGroupDef {
10
+ id: string;
11
+ label: string;
12
+ options: { value: string; label: string }[];
13
+ }
14
+
15
+ export interface FilteredPost {
16
+ slug: string;
17
+ title: string;
18
+ href: string;
19
+ /** ISO date, for the time element. */
20
+ date: string;
21
+ /** The date as readers see it, for example "Sep 14, 2026". */
22
+ dateLabel: string;
23
+ description: string;
24
+ /** The post's option in each filter group, by group id — for example { topic: "guides" }. */
25
+ facets: Record<string, string>;
26
+ }
27
+
28
+ type Selection = Record<string, string[]>;
29
+
30
+ function postsLabel(count: number) {
31
+ return count === 1 ? "1 post" : `${count} posts`;
32
+ }
33
+
34
+ function matches(post: FilteredPost, groups: FilterGroupDef[], selection: Selection) {
35
+ return groups.every((group) => {
36
+ const values = selection[group.id] ?? [];
37
+ return values.length === 0 || values.includes(post.facets[group.id] ?? "");
38
+ });
39
+ }
40
+
41
+ function FilterGroups({
42
+ groups,
43
+ posts,
44
+ selection,
45
+ onGroupChange,
46
+ }: {
47
+ groups: FilterGroupDef[];
48
+ posts: FilteredPost[];
49
+ selection: Selection;
50
+ onGroupChange: (groupId: string, values: string[]) => void;
51
+ }) {
52
+ return (
53
+ <Accordion variant="plain" flush>
54
+ {groups.map((group) => (
55
+ <Accordion.Item key={group.id} label={group.label} defaultOpen>
56
+ <CheckboxGroup
57
+ label={group.label}
58
+ labelHidden
59
+ size="sm"
60
+ values={selection[group.id] ?? []}
61
+ onValuesChange={(values) => onGroupChange(group.id, values)}
62
+ >
63
+ {group.options.map((option) => {
64
+ const count = posts.filter((post) => post.facets[group.id] === option.value).length;
65
+ return (
66
+ <CheckboxGroup.Item
67
+ key={option.value}
68
+ value={option.value}
69
+ label={option.label}
70
+ count={count}
71
+ countLabel={postsLabel(count)}
72
+ />
73
+ );
74
+ })}
75
+ </CheckboxGroup>
76
+ </Accordion.Item>
77
+ ))}
78
+ </Accordion>
79
+ );
80
+ }
81
+
82
+ export function FilteredIndex({
83
+ title,
84
+ posts,
85
+ groups,
86
+ }: {
87
+ title: string;
88
+ posts: FilteredPost[];
89
+ groups: FilterGroupDef[];
90
+ }) {
91
+ const filtersHeadingId = useId();
92
+ const [selection, setSelection] = useState<Selection>({});
93
+ const [sheetOpen, setSheetOpen] = useState(false);
94
+ const activeCount = Object.values(selection).reduce((total, values) => total + values.length, 0);
95
+ const shown = posts.filter((post) => matches(post, groups, selection));
96
+
97
+ function setGroup(groupId: string, values: string[]) {
98
+ setSelection((current) => ({ ...current, [groupId]: values }));
99
+ }
100
+
101
+ return (
102
+ <main className="grid-page bg-body">
103
+ <div className="band py-6 sm:py-10 lg:py-14">
104
+ <header className="col-span-full mb-4 flex items-end justify-between gap-4 sm:mb-8">
105
+ <h1 className="type-display-2 text-fg">
106
+ {title} <span className="tabular-nums text-muted">{shown.length}</span>
107
+ </h1>
108
+ <Button
109
+ role="secondary"
110
+ icon={<SlidersHorizontal />}
111
+ count={activeCount > 0 ? activeCount : undefined}
112
+ className="md:hidden"
113
+ onClick={() => setSheetOpen(true)}
114
+ >
115
+ Filters
116
+ </Button>
117
+ </header>
118
+ <p role="status" className="sr-only">
119
+ {postsLabel(shown.length)}
120
+ </p>
121
+
122
+ <aside aria-labelledby={filtersHeadingId} className="hidden md:col-span-3 md:block">
123
+ <SectionCaption
124
+ id={filtersHeadingId}
125
+ end={
126
+ <Button role="ghost" size="xs" disabled={activeCount === 0} onClick={() => setSelection({})}>
127
+ Clear all
128
+ </Button>
129
+ }
130
+ >
131
+ Filters
132
+ </SectionCaption>
133
+ <FilterGroups groups={groups} posts={posts} selection={selection} onGroupChange={setGroup} />
134
+ </aside>
135
+
136
+ <div className="col-span-full md:col-span-5 lg:col-span-9">
137
+ <IndexList
138
+ aria-label="Posts"
139
+ captions={{ meta: "Date", title: "Name" }}
140
+ empty={<p className="type-body text-muted">No posts match these filters.</p>}
141
+ >
142
+ {shown.map((post) => (
143
+ <IndexList.Item
144
+ key={post.slug}
145
+ title={post.title}
146
+ href={post.href}
147
+ meta={<time dateTime={post.date}>{post.dateLabel}</time>}
148
+ preview={<p>{post.description}</p>}
149
+ />
150
+ ))}
151
+ </IndexList>
152
+ </div>
153
+ </div>
154
+
155
+ <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
156
+ <Sheet.Content
157
+ title="Filters"
158
+ footer={
159
+ <div className="flex items-center justify-end gap-2">
160
+ <Button role="secondary" size="sm" disabled={activeCount === 0} onClick={() => setSelection({})}>
161
+ Clear all
162
+ </Button>
163
+ <Button role="primary" size="sm" onClick={() => setSheetOpen(false)}>
164
+ Show {postsLabel(shown.length)}
165
+ </Button>
166
+ </div>
167
+ }
168
+ >
169
+ <FilterGroups groups={groups} posts={posts} selection={selection} onGroupChange={setGroup} />
170
+ </Sheet.Content>
171
+ </Sheet>
172
+ </main>
173
+ );
174
+ }
@@ -1,4 +1,4 @@
1
- // @thewhatmatters/wmds@0.4.0 · Pattern — notification preferences
1
+ // @thewhatmatters/wmds@0.4.2 · Pattern — notification preferences
2
2
  // Storybook: Guides/Overlay flows → Pattern — notification preferences (?path=/story/guides-overlay-flows--notification-preferences)
3
3
  // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
4
 
@@ -0,0 +1,148 @@
1
+ // @thewhatmatters/wmds@0.4.2 · Pattern — post page
2
+ // Storybook: Guides/Post page → Pattern — post page (?path=/story/guides-post-page--post-page)
3
+ // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
+
5
+ import { useEffect, useId, useState, type ReactNode } from "react";
6
+ import { Copy, FileText } from "lucide-react";
7
+ import {
8
+ Badge,
9
+ Breadcrumb,
10
+ Button,
11
+ DescriptionList,
12
+ Prose,
13
+ SectionCaption,
14
+ buttonStatusHoldMs,
15
+ type BreadcrumbItemDef,
16
+ type ButtonStatus,
17
+ } from "@thewhatmatters/wmds";
18
+
19
+ export interface PostCategory {
20
+ label: string;
21
+ href: string;
22
+ }
23
+
24
+ export interface PostPageData {
25
+ /** The path to this post, ending with it — for example Home, Blog, Guides, this post. */
26
+ breadcrumb: BreadcrumbItemDef[];
27
+ title: string;
28
+ description: string;
29
+ /** ISO date, for the time element. */
30
+ date: string;
31
+ /** The date as readers see it, for example "Sep 14, 2026". */
32
+ dateLabel: string;
33
+ author: string;
34
+ /** For example "6 min". */
35
+ readingTime: string;
36
+ categories: PostCategory[];
37
+ /** The post's own address, for the share links. */
38
+ url: string;
39
+ /** The post's markdown source, for Copy for LLM. */
40
+ markdown: string;
41
+ /** The post as plain markdown, for View as Markdown. */
42
+ markdownHref: string;
43
+ }
44
+
45
+ function CopyButton({ text, label }: { text: string; label: string }) {
46
+ const [status, setStatus] = useState<ButtonStatus>("idle");
47
+
48
+ useEffect(() => {
49
+ if (status !== "success" && status !== "error") return;
50
+ const timer = window.setTimeout(() => setStatus("idle"), buttonStatusHoldMs);
51
+ return () => window.clearTimeout(timer);
52
+ }, [status]);
53
+
54
+ async function copy() {
55
+ try {
56
+ await navigator.clipboard.writeText(text);
57
+ setStatus("success");
58
+ } catch {
59
+ setStatus("error");
60
+ }
61
+ }
62
+
63
+ return (
64
+ <Button
65
+ role="secondary"
66
+ size="sm"
67
+ icon={<Copy />}
68
+ status={status}
69
+ statusLabels={{ success: "Copied", error: "Couldn't copy" }}
70
+ onClick={copy}
71
+ >
72
+ {label}
73
+ </Button>
74
+ );
75
+ }
76
+
77
+ /** A blog post: the title, a metadata panel, and the article, which is the rendered markdown. */
78
+ export function PostPage({ post, children }: { post: PostPageData; children: ReactNode }) {
79
+ const metadataHeadingId = useId();
80
+ const encodedUrl = encodeURIComponent(post.url);
81
+ const encodedTitle = encodeURIComponent(post.title);
82
+
83
+ return (
84
+ <main className="grid-page bg-body">
85
+ <article className="band py-6 sm:py-10 lg:py-14">
86
+ <header className="col-span-full mb-4 flex flex-col gap-3 sm:mb-8 lg:col-span-10">
87
+ <Breadcrumb items={post.breadcrumb} variant="mono" separator="slash" className="mb-3" />
88
+ <h1 className="type-display-2 text-balance text-fg">{post.title}</h1>
89
+ <p className="type-reading max-w-[40rem] text-muted">{post.description}</p>
90
+ </header>
91
+
92
+ <aside
93
+ aria-labelledby={metadataHeadingId}
94
+ className="col-span-full lg:sticky lg:top-[calc(var(--site-nav-height)+1rem)] lg:col-span-4"
95
+ >
96
+ <SectionCaption id={metadataHeadingId}>Metadata</SectionCaption>
97
+ <DescriptionList variant="mono" rule="dotted">
98
+ <DescriptionList.Item name="Date">
99
+ <time dateTime={post.date}>{post.dateLabel}</time>
100
+ </DescriptionList.Item>
101
+ <DescriptionList.Item name="Author">
102
+ <Badge emphasis="outline" mono>
103
+ {post.author}
104
+ </Badge>
105
+ </DescriptionList.Item>
106
+ <DescriptionList.Item name="Reading time">{post.readingTime}</DescriptionList.Item>
107
+ <DescriptionList.Item name="Categories">
108
+ {post.categories.map((category) => (
109
+ <Badge key={category.href} emphasis="outline" mono render={<a href={category.href} />}>
110
+ {category.label}
111
+ </Badge>
112
+ ))}
113
+ </DescriptionList.Item>
114
+ <DescriptionList.Item name="Agents" layout="stacked">
115
+ <CopyButton text={post.markdown} label="Copy for LLM" />
116
+ <Button role="secondary" size="sm" icon={<FileText />} render={<a href={post.markdownHref} />}>
117
+ View as Markdown
118
+ </Button>
119
+ </DescriptionList.Item>
120
+ <DescriptionList.Item name="Share" layout="stacked">
121
+ <Button
122
+ role="secondary"
123
+ size="sm"
124
+ external
125
+ render={<a href={"https://x.com/intent/post?url=" + encodedUrl + "&text=" + encodedTitle} />}
126
+ >
127
+ Twitter/X
128
+ </Button>
129
+ <Button
130
+ role="secondary"
131
+ size="sm"
132
+ external
133
+ render={<a href={"https://www.linkedin.com/sharing/share-offsite/?url=" + encodedUrl} />}
134
+ >
135
+ LinkedIn
136
+ </Button>
137
+ </DescriptionList.Item>
138
+ </DescriptionList>
139
+ </aside>
140
+
141
+ <div className="col-span-full mt-6 lg:col-span-8 lg:mt-0">
142
+ <SectionCaption as="p">Article</SectionCaption>
143
+ <Prose className="mt-6">{children}</Prose>
144
+ </div>
145
+ </article>
146
+ </main>
147
+ );
148
+ }
@@ -1,4 +1,4 @@
1
- // @thewhatmatters/wmds@0.4.0 · Pattern — editorial profile
1
+ // @thewhatmatters/wmds@0.4.2 · Pattern — editorial profile
2
2
  // Storybook: Guides/Profile typography → Pattern — editorial profile (?path=/story/guides-profile-typography--editorial-profile)
3
3
  // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
4
 
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "package": "@thewhatmatters/wmds",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "patterns": [
5
+ {
6
+ "id": "components-accordion--flush",
7
+ "title": "Components/Accordion",
8
+ "name": "Pattern — flush",
9
+ "file": "patterns/components-accordion--flush.tsx",
10
+ "storybook": "?path=/story/components-accordion--flush",
11
+ "source": "src/components/molecules/Accordion/Accordion.stories.tsx"
12
+ },
5
13
  {
6
14
  "id": "components-avatar--dropdown-row",
7
15
  "title": "Components/Avatar",
@@ -10,6 +18,14 @@
10
18
  "storybook": "?path=/story/components-avatar--dropdown-row",
11
19
  "source": "src/components/atoms/Avatar/Avatar.stories.tsx"
12
20
  },
21
+ {
22
+ "id": "components-badge--outline-tags",
23
+ "title": "Components/Badge",
24
+ "name": "Pattern — outline tags",
25
+ "file": "patterns/components-badge--outline-tags.tsx",
26
+ "storybook": "?path=/story/components-badge--outline-tags",
27
+ "source": "src/components/atoms/Badge/Badge.stories.tsx"
28
+ },
13
29
  {
14
30
  "id": "components-badge--with-avatar",
15
31
  "title": "Components/Badge",
@@ -18,6 +34,46 @@
18
34
  "storybook": "?path=/story/components-badge--with-avatar",
19
35
  "source": "src/components/atoms/Badge/Badge.stories.tsx"
20
36
  },
37
+ {
38
+ "id": "components-breadcrumb--editorial",
39
+ "title": "Components/Breadcrumb",
40
+ "name": "Pattern — editorial",
41
+ "file": "patterns/components-breadcrumb--editorial.tsx",
42
+ "storybook": "?path=/story/components-breadcrumb--editorial",
43
+ "source": "src/components/molecules/Breadcrumb/Breadcrumb.stories.tsx"
44
+ },
45
+ {
46
+ "id": "components-breadcrumb--long-path",
47
+ "title": "Components/Breadcrumb",
48
+ "name": "Pattern — long path",
49
+ "file": "patterns/components-breadcrumb--long-path.tsx",
50
+ "storybook": "?path=/story/components-breadcrumb--long-path",
51
+ "source": "src/components/molecules/Breadcrumb/Breadcrumb.stories.tsx"
52
+ },
53
+ {
54
+ "id": "components-breadcrumb--path",
55
+ "title": "Components/Breadcrumb",
56
+ "name": "Pattern — path",
57
+ "file": "patterns/components-breadcrumb--path.tsx",
58
+ "storybook": "?path=/story/components-breadcrumb--path",
59
+ "source": "src/components/molecules/Breadcrumb/Breadcrumb.stories.tsx"
60
+ },
61
+ {
62
+ "id": "components-breadcrumb--router-links",
63
+ "title": "Components/Breadcrumb",
64
+ "name": "Pattern — router links",
65
+ "file": "patterns/components-breadcrumb--router-links.tsx",
66
+ "storybook": "?path=/story/components-breadcrumb--router-links",
67
+ "source": "src/components/molecules/Breadcrumb/Breadcrumb.stories.tsx"
68
+ },
69
+ {
70
+ "id": "components-button-button--copy-button",
71
+ "title": "Components/Button/Button",
72
+ "name": "Pattern — copy button",
73
+ "file": "patterns/components-button-button--copy-button.tsx",
74
+ "storybook": "?path=/story/components-button-button--copy-button",
75
+ "source": "src/components/atoms/Button/Button.stories.tsx"
76
+ },
21
77
  {
22
78
  "id": "components-button-button--destructive-action",
23
79
  "title": "Components/Button/Button",
@@ -26,6 +82,14 @@
26
82
  "storybook": "?path=/story/components-button-button--destructive-action",
27
83
  "source": "src/components/atoms/Button/Button.stories.tsx"
28
84
  },
85
+ {
86
+ "id": "components-button-button--external-link",
87
+ "title": "Components/Button/Button",
88
+ "name": "Pattern — external link",
89
+ "file": "patterns/components-button-button--external-link.tsx",
90
+ "storybook": "?path=/story/components-button-button--external-link",
91
+ "source": "src/components/atoms/Button/Button.stories.tsx"
92
+ },
29
93
  {
30
94
  "id": "components-button-button--ghost-action",
31
95
  "title": "Components/Button/Button",
@@ -242,6 +306,14 @@
242
306
  "storybook": "?path=/story/components-chatdock--pattern-chat-dock",
243
307
  "source": "src/components/organisms/ChatDock/ChatDock.stories.tsx"
244
308
  },
309
+ {
310
+ "id": "components-chatdock--pattern-start-project-gate",
311
+ "title": "Components/ChatDock",
312
+ "name": "Pattern — start a project gate",
313
+ "file": "patterns/components-chatdock--pattern-start-project-gate.tsx",
314
+ "storybook": "?path=/story/components-chatdock--pattern-start-project-gate",
315
+ "source": "src/components/organisms/ChatDock/ChatDock.stories.tsx"
316
+ },
245
317
  {
246
318
  "id": "components-chatqa--pattern-qa-with-reply",
247
319
  "title": "Components/ChatQa",
@@ -258,6 +330,14 @@
258
330
  "storybook": "?path=/story/components-checkbox-checkbox--default",
259
331
  "source": "src/components/atoms/Checkbox/Checkbox.stories.tsx"
260
332
  },
333
+ {
334
+ "id": "components-checkbox-checkbox--with-count",
335
+ "title": "Components/Checkbox/Checkbox",
336
+ "name": "Pattern — with count",
337
+ "file": "patterns/components-checkbox-checkbox--with-count.tsx",
338
+ "storybook": "?path=/story/components-checkbox-checkbox--with-count",
339
+ "source": "src/components/atoms/Checkbox/Checkbox.stories.tsx"
340
+ },
261
341
  {
262
342
  "id": "components-checkbox-checkbox--with-description",
263
343
  "title": "Components/Checkbox/Checkbox",
@@ -274,6 +354,30 @@
274
354
  "storybook": "?path=/story/components-checkbox-checkboxgroup--default",
275
355
  "source": "src/components/molecules/CheckboxGroup/CheckboxGroup.stories.tsx"
276
356
  },
357
+ {
358
+ "id": "components-checkbox-checkboxgroup--option-counts",
359
+ "title": "Components/Checkbox/CheckboxGroup",
360
+ "name": "Pattern — option counts",
361
+ "file": "patterns/components-checkbox-checkboxgroup--option-counts.tsx",
362
+ "storybook": "?path=/story/components-checkbox-checkboxgroup--option-counts",
363
+ "source": "src/components/molecules/CheckboxGroup/CheckboxGroup.stories.tsx"
364
+ },
365
+ {
366
+ "id": "components-descriptionlist--details",
367
+ "title": "Components/DescriptionList",
368
+ "name": "Pattern — details",
369
+ "file": "patterns/components-descriptionlist--details.tsx",
370
+ "storybook": "?path=/story/components-descriptionlist--details",
371
+ "source": "src/components/molecules/DescriptionList/DescriptionList.stories.tsx"
372
+ },
373
+ {
374
+ "id": "components-descriptionlist--post-metadata",
375
+ "title": "Components/DescriptionList",
376
+ "name": "Pattern — post metadata",
377
+ "file": "patterns/components-descriptionlist--post-metadata.tsx",
378
+ "storybook": "?path=/story/components-descriptionlist--post-metadata",
379
+ "source": "src/components/molecules/DescriptionList/DescriptionList.stories.tsx"
380
+ },
277
381
  {
278
382
  "id": "components-dialog--alert-dialog-destructive",
279
383
  "title": "Components/Dialog",
@@ -410,6 +514,14 @@
410
514
  "storybook": "?path=/story/components-herotilestack--marketing-hero-with-gallery-intro",
411
515
  "source": "src/components/organisms/HeroTileStack/HeroTileStack.galleryIntro.stories.tsx"
412
516
  },
517
+ {
518
+ "id": "components-indexlist--post-index",
519
+ "title": "Components/IndexList",
520
+ "name": "Pattern — post index",
521
+ "file": "patterns/components-indexlist--post-index.tsx",
522
+ "storybook": "?path=/story/components-indexlist--post-index",
523
+ "source": "src/components/molecules/IndexList/IndexList.stories.tsx"
524
+ },
413
525
  {
414
526
  "id": "components-intakeconfirmation--booked-pattern",
415
527
  "title": "Components/IntakeConfirmation",
@@ -538,6 +650,14 @@
538
650
  "storybook": "?path=/story/components-promptbar--pattern-prompt-bar",
539
651
  "source": "src/components/molecules/PromptBar/PromptBar.stories.tsx"
540
652
  },
653
+ {
654
+ "id": "components-prose--article-body",
655
+ "title": "Components/Prose",
656
+ "name": "Pattern — article body",
657
+ "file": "patterns/components-prose--article-body.tsx",
658
+ "storybook": "?path=/story/components-prose--article-body",
659
+ "source": "src/components/atoms/Prose/Prose.stories.tsx"
660
+ },
541
661
  {
542
662
  "id": "components-radio-radiogroup--default",
543
663
  "title": "Components/Radio/RadioGroup",
@@ -562,6 +682,22 @@
562
682
  "storybook": "?path=/story/components-scrollhorizontal--with-intro",
563
683
  "source": "src/components/organisms/ScrollHorizontal/ScrollHorizontal.stories.tsx"
564
684
  },
685
+ {
686
+ "id": "components-sectioncaption--column-caption",
687
+ "title": "Components/SectionCaption",
688
+ "name": "Pattern — column caption",
689
+ "file": "patterns/components-sectioncaption--column-caption.tsx",
690
+ "storybook": "?path=/story/components-sectioncaption--column-caption",
691
+ "source": "src/components/atoms/SectionCaption/SectionCaption.stories.tsx"
692
+ },
693
+ {
694
+ "id": "components-sectioncaption--with-action",
695
+ "title": "Components/SectionCaption",
696
+ "name": "Pattern — caption with an action",
697
+ "file": "patterns/components-sectioncaption--with-action.tsx",
698
+ "storybook": "?path=/story/components-sectioncaption--with-action",
699
+ "source": "src/components/atoms/SectionCaption/SectionCaption.stories.tsx"
700
+ },
565
701
  {
566
702
  "id": "components-sheet--bottom-sheet",
567
703
  "title": "Components/Sheet",
@@ -730,6 +866,14 @@
730
866
  "storybook": "?path=/story/components-textlink--inline-prose",
731
867
  "source": "src/components/atoms/TextLink/TextLink.stories.tsx"
732
868
  },
869
+ {
870
+ "id": "components-textlink--quiet-title",
871
+ "title": "Components/TextLink",
872
+ "name": "Pattern — quiet title link",
873
+ "file": "patterns/components-textlink--quiet-title.tsx",
874
+ "storybook": "?path=/story/components-textlink--quiet-title",
875
+ "source": "src/components/atoms/TextLink/TextLink.stories.tsx"
876
+ },
733
877
  {
734
878
  "id": "components-textlink--supporting-copy",
735
879
  "title": "Components/TextLink",
@@ -834,6 +978,14 @@
834
978
  "storybook": "?path=/story/guides-chart-explorations--comparison-gallery",
835
979
  "source": "src/guides/ChartExplorations/ChartExplorations.stories.tsx"
836
980
  },
981
+ {
982
+ "id": "guides-filter-panel--filtered-index",
983
+ "title": "Guides/Filter panel",
984
+ "name": "Pattern — filtered index",
985
+ "file": "patterns/guides-filter-panel--filtered-index.tsx",
986
+ "storybook": "?path=/story/guides-filter-panel--filtered-index",
987
+ "source": "src/guides/FilterPanel/FilterPanel.stories.tsx"
988
+ },
837
989
  {
838
990
  "id": "guides-overlay-flows--notification-preferences",
839
991
  "title": "Guides/Overlay flows",
@@ -842,6 +994,14 @@
842
994
  "storybook": "?path=/story/guides-overlay-flows--notification-preferences",
843
995
  "source": "src/guides/OverlayFlows/OverlayFlows.stories.tsx"
844
996
  },
997
+ {
998
+ "id": "guides-post-page--post-page",
999
+ "title": "Guides/Post page",
1000
+ "name": "Pattern — post page",
1001
+ "file": "patterns/guides-post-page--post-page.tsx",
1002
+ "storybook": "?path=/story/guides-post-page--post-page",
1003
+ "source": "src/guides/PostPage/PostPage.stories.tsx"
1004
+ },
845
1005
  {
846
1006
  "id": "guides-profile-typography--editorial-profile",
847
1007
  "title": "Guides/Profile typography",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@thewhatmatters/wmds",
3
3
  "description": "WhatMatters Design System — React components, Tailwind v4 theme, and Storybook patterns",
4
- "version": "0.4.0",
4
+ "version": "0.4.2",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {