@thewhatmatters/wmds 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/CHANGELOG.md +87 -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/CheckboxGroup/CheckboxGroupItem.d.ts +5 -1
  66. package/dist/components/molecules/CheckboxGroup/CheckboxGroupItem.d.ts.map +1 -1
  67. package/dist/components/molecules/CheckboxGroup/CheckboxGroupItem.js +12 -10
  68. package/dist/components/molecules/DescriptionList/DescriptionList.d.ts +37 -0
  69. package/dist/components/molecules/DescriptionList/DescriptionList.d.ts.map +1 -0
  70. package/dist/components/molecules/DescriptionList/DescriptionList.js +50 -0
  71. package/dist/components/molecules/DescriptionList/descriptionListStyles.d.ts +21 -0
  72. package/dist/components/molecules/DescriptionList/descriptionListStyles.d.ts.map +1 -0
  73. package/dist/components/molecules/DescriptionList/descriptionListStyles.js +27 -0
  74. package/dist/components/molecules/IndexList/IndexList.d.ts +58 -0
  75. package/dist/components/molecules/IndexList/IndexList.d.ts.map +1 -0
  76. package/dist/components/molecules/IndexList/IndexList.js +96 -0
  77. package/dist/components/molecules/IndexList/indexListStyles.d.ts +29 -0
  78. package/dist/components/molecules/IndexList/indexListStyles.d.ts.map +1 -0
  79. package/dist/components/molecules/IndexList/indexListStyles.js +24 -0
  80. package/dist/components/molecules/PromptBar/PromptBar.d.ts +11 -1
  81. package/dist/components/molecules/PromptBar/PromptBar.d.ts.map +1 -1
  82. package/dist/components/molecules/PromptBar/PromptBar.js +54 -43
  83. package/dist/components/molecules/PromptBar/promptBarStyles.d.ts +16 -0
  84. package/dist/components/molecules/PromptBar/promptBarStyles.d.ts.map +1 -1
  85. package/dist/components/molecules/PromptBar/promptBarStyles.js +5 -2
  86. package/dist/components/organisms/ChatDock/ChatDock.d.ts +203 -0
  87. package/dist/components/organisms/ChatDock/ChatDock.d.ts.map +1 -0
  88. package/dist/components/organisms/ChatDock/ChatDock.js +697 -0
  89. package/dist/components/organisms/ChatDock/ChatDockGate.d.ts +52 -0
  90. package/dist/components/organisms/ChatDock/ChatDockGate.d.ts.map +1 -0
  91. package/dist/components/organisms/ChatDock/ChatDockGate.js +161 -0
  92. package/dist/components/organisms/ChatDock/chatDockGateStyles.d.ts +16 -0
  93. package/dist/components/organisms/ChatDock/chatDockGateStyles.d.ts.map +1 -0
  94. package/dist/components/organisms/ChatDock/chatDockGateStyles.js +8 -0
  95. package/dist/components/organisms/ChatDock/chatDockStyles.d.ts +98 -0
  96. package/dist/components/organisms/ChatDock/chatDockStyles.d.ts.map +1 -0
  97. package/dist/components/organisms/ChatDock/chatDockStyles.js +21 -0
  98. package/dist/components/organisms/ChatDock/chatDockViewport.d.ts +21 -0
  99. package/dist/components/organisms/ChatDock/chatDockViewport.d.ts.map +1 -0
  100. package/dist/components/organisms/ChatDock/chatDockViewport.js +46 -0
  101. package/dist/components/organisms/Dialog/AlertDialog.js +11 -11
  102. package/dist/components/organisms/Dialog/Dialog.js +44 -44
  103. package/dist/components/organisms/Sheet/Sheet.js +38 -38
  104. package/dist/components/organisms/Toast/Toast.js +5 -5
  105. package/dist/index.d.ts +7 -2
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +122 -110
  108. package/dist/lib/compositionAudit.d.ts.map +1 -1
  109. package/dist/lib/typography.d.ts +2 -2
  110. package/dist/lib/typography.d.ts.map +1 -1
  111. package/dist/lib/typography.js +27 -1
  112. package/dist/styles.css +1 -1
  113. package/dist/theme.css +1 -0
  114. package/dist/typography.css +34 -0
  115. package/docs/README.md +1 -1
  116. package/docs/component-contracts.md +16 -1
  117. package/docs/components.md +16 -11
  118. package/docs/exports.json +81 -4
  119. package/docs/patterns/components-accordion--flush.tsx +29 -0
  120. package/docs/patterns/components-avatar--dropdown-row.tsx +1 -1
  121. package/docs/patterns/components-badge--outline-tags.tsx +25 -0
  122. package/docs/patterns/components-badge--with-avatar.tsx +1 -1
  123. package/docs/patterns/components-button-button--copy-button.tsx +39 -0
  124. package/docs/patterns/components-button-button--destructive-action.tsx +1 -1
  125. package/docs/patterns/components-button-button--external-link.tsx +30 -0
  126. package/docs/patterns/components-button-button--ghost-action.tsx +1 -1
  127. package/docs/patterns/components-button-button--inverse-action.tsx +1 -1
  128. package/docs/patterns/components-button-button--link-render.tsx +1 -1
  129. package/docs/patterns/components-button-button--multi-state-badge.tsx +1 -1
  130. package/docs/patterns/components-button-button--outline-mono.tsx +1 -1
  131. package/docs/patterns/components-button-button--primary-action.tsx +1 -1
  132. package/docs/patterns/components-button-button--row-hug.tsx +1 -1
  133. package/docs/patterns/components-button-button--row-layout.tsx +1 -1
  134. package/docs/patterns/components-button-button--secondary-action.tsx +1 -1
  135. package/docs/patterns/components-button-button--suggestion-pills.tsx +1 -1
  136. package/docs/patterns/components-button-button--with-count.tsx +1 -1
  137. package/docs/patterns/components-button-button--with-icon.tsx +1 -1
  138. package/docs/patterns/components-button-floatingactionbutton--vertical-actions.tsx +1 -1
  139. package/docs/patterns/components-calembed--pattern-skip-in-footer.tsx +1 -1
  140. package/docs/patterns/components-card-card--body-slot-occupancy-history.tsx +1 -1
  141. package/docs/patterns/components-card-card--headerless-media.tsx +1 -1
  142. package/docs/patterns/components-card-card--outlined-layout.tsx +1 -1
  143. package/docs/patterns/components-card-card--pinned-height.tsx +1 -1
  144. package/docs/patterns/components-card-selectablecard--selectable-cards-pattern.tsx +1 -1
  145. package/docs/patterns/components-chart--audience-activity-heatmap.tsx +1 -1
  146. package/docs/patterns/components-chart--cartesian-no-data-gaps.tsx +1 -1
  147. package/docs/patterns/components-chart--occupancy-history-in-card.tsx +1 -1
  148. package/docs/patterns/components-chart--occupancy-in-card.tsx +1 -1
  149. package/docs/patterns/components-chart--post-reach-distribution.tsx +1 -1
  150. package/docs/patterns/components-chart--ranked-audience-breakdown.tsx +1 -1
  151. package/docs/patterns/components-chart--unit-grid-composition.tsx +1 -1
  152. package/docs/patterns/components-chatdock--pattern-chat-dock.tsx +158 -0
  153. package/docs/patterns/components-chatdock--pattern-start-project-gate.tsx +180 -0
  154. package/docs/patterns/components-chatqa--pattern-qa-with-reply.tsx +1 -1
  155. package/docs/patterns/components-checkbox-checkbox--default.tsx +1 -1
  156. package/docs/patterns/components-checkbox-checkbox--with-count.tsx +19 -0
  157. package/docs/patterns/components-checkbox-checkbox--with-description.tsx +1 -1
  158. package/docs/patterns/components-checkbox-checkboxgroup--default.tsx +1 -1
  159. package/docs/patterns/components-checkbox-checkboxgroup--option-counts.tsx +17 -0
  160. package/docs/patterns/components-descriptionlist--details.tsx +21 -0
  161. package/docs/patterns/components-descriptionlist--post-metadata.tsx +52 -0
  162. package/docs/patterns/components-dialog--alert-dialog-destructive.tsx +1 -1
  163. package/docs/patterns/components-dialog--default-dialog.tsx +1 -1
  164. package/docs/patterns/components-dialog--form-dialog.tsx +1 -1
  165. package/docs/patterns/components-displaycontrols--page-utilities.tsx +1 -1
  166. package/docs/patterns/components-field--horizontal-input.tsx +1 -1
  167. package/docs/patterns/components-field--horizontal-text-area.tsx +1 -1
  168. package/docs/patterns/components-field--multi-control.tsx +1 -1
  169. package/docs/patterns/components-footerreveal--marketing-hero-pattern.tsx +1 -1
  170. package/docs/patterns/components-footerreveal--marketing-hero-ruled-pattern.tsx +1 -1
  171. package/docs/patterns/components-footerreveal--marketing-hero-with-gallery-intro.tsx +1 -1
  172. package/docs/patterns/components-footerreveal--marketing-page-pattern.tsx +1 -1
  173. package/docs/patterns/components-footerreveal--ruled-grid-footer-pattern.tsx +1 -1
  174. package/docs/patterns/components-footerreveal--sequenced-marketing-hero.tsx +1 -1
  175. package/docs/patterns/components-herointro--two-line-intro.tsx +1 -1
  176. package/docs/patterns/components-herotilestack--marketing-hero-pattern.tsx +1 -1
  177. package/docs/patterns/components-herotilestack--marketing-hero-text-sequence.tsx +1 -1
  178. package/docs/patterns/components-herotilestack--marketing-hero-with-gallery-intro.tsx +1 -1
  179. package/docs/patterns/components-indexlist--post-index.tsx +36 -0
  180. package/docs/patterns/components-intakeconfirmation--booked-pattern.tsx +1 -1
  181. package/docs/patterns/components-intakeconfirmation--emailed-pattern.tsx +1 -1
  182. package/docs/patterns/components-intakeform--about-you-pattern.tsx +1 -1
  183. package/docs/patterns/components-intakemodal--intake-modal-pattern.tsx +1 -1
  184. package/docs/patterns/components-kbd--command-row.tsx +1 -1
  185. package/docs/patterns/components-kbd--numbered-choices.tsx +1 -1
  186. package/docs/patterns/components-kbd--shortcut.tsx +1 -1
  187. package/docs/patterns/components-moremenu--action-menu.tsx +1 -1
  188. package/docs/patterns/components-moremenu--in-card-header.tsx +1 -1
  189. package/docs/patterns/components-navlist--side-nav-settings.tsx +1 -1
  190. package/docs/patterns/components-pageheader--app-header-pattern.tsx +1 -1
  191. package/docs/patterns/components-pageheader--page-section-header.tsx +1 -1
  192. package/docs/patterns/components-pageheader--toolbar-header.tsx +1 -1
  193. package/docs/patterns/components-panel--end-detail-rail.tsx +1 -1
  194. package/docs/patterns/components-pillgroup--pill-group-pattern.tsx +1 -1
  195. package/docs/patterns/components-promptbar--pattern-prompt-bar.tsx +1 -1
  196. package/docs/patterns/components-prose--article-body.tsx +11 -0
  197. package/docs/patterns/components-radio-radiogroup--default.tsx +1 -1
  198. package/docs/patterns/components-scrollhorizontal--project-gallery-pattern.tsx +1 -1
  199. package/docs/patterns/components-scrollhorizontal--with-intro.tsx +1 -1
  200. package/docs/patterns/components-sectioncaption--column-caption.tsx +16 -0
  201. package/docs/patterns/components-sectioncaption--with-action.tsx +19 -0
  202. package/docs/patterns/components-sheet--bottom-sheet.tsx +1 -1
  203. package/docs/patterns/components-sheet--end-sheet.tsx +1 -1
  204. package/docs/patterns/components-sitenav--brand-and-cta.tsx +1 -1
  205. package/docs/patterns/components-sitenav--centered-links.tsx +1 -1
  206. package/docs/patterns/components-sitenav--compact.tsx +1 -1
  207. package/docs/patterns/components-sitenav--marketing-header.tsx +1 -1
  208. package/docs/patterns/components-sitenav--mega-menu.tsx +1 -1
  209. package/docs/patterns/components-stat--insights-row.tsx +1 -1
  210. package/docs/patterns/components-stepprogress--step-progress-pattern.tsx +1 -1
  211. package/docs/patterns/components-switch--default.tsx +1 -1
  212. package/docs/patterns/components-switch--settings-row.tsx +1 -1
  213. package/docs/patterns/components-tab--settings-pages.tsx +1 -1
  214. package/docs/patterns/components-taskrows--action-details.tsx +1 -1
  215. package/docs/patterns/components-taskrows--capsules.tsx +1 -1
  216. package/docs/patterns/components-taskrows--card-with-status-rows.tsx +1 -1
  217. package/docs/patterns/components-taskrows--controlled-expand.tsx +1 -1
  218. package/docs/patterns/components-taskrows--custom-slots.tsx +1 -1
  219. package/docs/patterns/components-taskrows--status-list.tsx +1 -1
  220. package/docs/patterns/components-taskrows--tag-chips.tsx +1 -1
  221. package/docs/patterns/components-textlink--external-destination.tsx +1 -1
  222. package/docs/patterns/components-textlink--inline-prose.tsx +1 -1
  223. package/docs/patterns/components-textlink--quiet-title.tsx +15 -0
  224. package/docs/patterns/components-textlink--supporting-copy.tsx +1 -1
  225. package/docs/patterns/components-textsequence--default.tsx +1 -1
  226. package/docs/patterns/components-textsequence--reduced-motion.tsx +1 -1
  227. package/docs/patterns/components-textsequence--shapes.tsx +1 -1
  228. package/docs/patterns/components-toast--stacked-notifications.tsx +1 -1
  229. package/docs/patterns/components-tooltip--controlled.tsx +1 -1
  230. package/docs/patterns/components-tooltip--disabled-control.tsx +1 -1
  231. package/docs/patterns/components-tooltip--icon-action.tsx +1 -1
  232. package/docs/patterns/components-tooltip--keyboard-shortcut.tsx +1 -1
  233. package/docs/patterns/components-tooltip--placement.tsx +1 -1
  234. package/docs/patterns/foundations-scroll-fade--horizontal-rail.tsx +1 -1
  235. package/docs/patterns/foundations-scroll-fade--overflowing-list.tsx +1 -1
  236. package/docs/patterns/guides-chart-explorations--comparison-gallery.tsx +1 -1
  237. package/docs/patterns/guides-filter-panel--filtered-index.tsx +174 -0
  238. package/docs/patterns/guides-overlay-flows--notification-preferences.tsx +1 -1
  239. package/docs/patterns/guides-post-page--post-page.tsx +143 -0
  240. package/docs/patterns/guides-profile-typography--editorial-profile.tsx +1 -1
  241. package/docs/patterns/index.json +137 -9
  242. package/package.json +1 -1
  243. package/docs/patterns/components-promptbar--pattern-marketing-composer.tsx +0 -37
@@ -0,0 +1,174 @@
1
+ // @thewhatmatters/wmds@0.4.1 · 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.3.0 · Pattern — notification preferences
1
+ // @thewhatmatters/wmds@0.4.1 · 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,143 @@
1
+ // @thewhatmatters/wmds@0.4.1 · 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
+ Button,
10
+ DescriptionList,
11
+ Prose,
12
+ SectionCaption,
13
+ buttonStatusHoldMs,
14
+ type ButtonStatus,
15
+ } from "@thewhatmatters/wmds";
16
+
17
+ export interface PostCategory {
18
+ label: string;
19
+ href: string;
20
+ }
21
+
22
+ export interface PostPageData {
23
+ title: string;
24
+ description: string;
25
+ /** ISO date, for the time element. */
26
+ date: string;
27
+ /** The date as readers see it, for example "Sep 14, 2026". */
28
+ dateLabel: string;
29
+ author: string;
30
+ /** For example "6 min". */
31
+ readingTime: string;
32
+ categories: PostCategory[];
33
+ /** The post's own address, for the share links. */
34
+ url: string;
35
+ /** The post's markdown source, for Copy for LLM. */
36
+ markdown: string;
37
+ /** The post as plain markdown, for View as Markdown. */
38
+ markdownHref: string;
39
+ }
40
+
41
+ function CopyButton({ text, label }: { text: string; label: string }) {
42
+ const [status, setStatus] = useState<ButtonStatus>("idle");
43
+
44
+ useEffect(() => {
45
+ if (status !== "success" && status !== "error") return;
46
+ const timer = window.setTimeout(() => setStatus("idle"), buttonStatusHoldMs);
47
+ return () => window.clearTimeout(timer);
48
+ }, [status]);
49
+
50
+ async function copy() {
51
+ try {
52
+ await navigator.clipboard.writeText(text);
53
+ setStatus("success");
54
+ } catch {
55
+ setStatus("error");
56
+ }
57
+ }
58
+
59
+ return (
60
+ <Button
61
+ role="secondary"
62
+ size="sm"
63
+ icon={<Copy />}
64
+ status={status}
65
+ statusLabels={{ success: "Copied", error: "Couldn't copy" }}
66
+ onClick={copy}
67
+ >
68
+ {label}
69
+ </Button>
70
+ );
71
+ }
72
+
73
+ /** A blog post: the title, a metadata panel, and the article, which is the rendered markdown. */
74
+ export function PostPage({ post, children }: { post: PostPageData; children: ReactNode }) {
75
+ const metadataHeadingId = useId();
76
+ const encodedUrl = encodeURIComponent(post.url);
77
+ const encodedTitle = encodeURIComponent(post.title);
78
+
79
+ return (
80
+ <main className="grid-page bg-body">
81
+ <article className="band py-6 sm:py-10 lg:py-14">
82
+ <header className="col-span-full mb-4 flex flex-col gap-3 sm:mb-8 lg:col-span-10">
83
+ <h1 className="type-display-2 text-balance text-fg">{post.title}</h1>
84
+ <p className="type-reading max-w-[40rem] text-muted">{post.description}</p>
85
+ </header>
86
+
87
+ <aside
88
+ aria-labelledby={metadataHeadingId}
89
+ className="col-span-full lg:sticky lg:top-[calc(var(--site-nav-height)+1rem)] lg:col-span-4"
90
+ >
91
+ <SectionCaption id={metadataHeadingId}>Metadata</SectionCaption>
92
+ <DescriptionList variant="mono" rule="dotted">
93
+ <DescriptionList.Item name="Date">
94
+ <time dateTime={post.date}>{post.dateLabel}</time>
95
+ </DescriptionList.Item>
96
+ <DescriptionList.Item name="Author">
97
+ <Badge emphasis="outline" mono>
98
+ {post.author}
99
+ </Badge>
100
+ </DescriptionList.Item>
101
+ <DescriptionList.Item name="Reading time">{post.readingTime}</DescriptionList.Item>
102
+ <DescriptionList.Item name="Categories">
103
+ {post.categories.map((category) => (
104
+ <Badge key={category.href} emphasis="outline" mono render={<a href={category.href} />}>
105
+ {category.label}
106
+ </Badge>
107
+ ))}
108
+ </DescriptionList.Item>
109
+ <DescriptionList.Item name="Agents" layout="stacked">
110
+ <CopyButton text={post.markdown} label="Copy for LLM" />
111
+ <Button role="secondary" size="sm" icon={<FileText />} render={<a href={post.markdownHref} />}>
112
+ View as Markdown
113
+ </Button>
114
+ </DescriptionList.Item>
115
+ <DescriptionList.Item name="Share" layout="stacked">
116
+ <Button
117
+ role="secondary"
118
+ size="sm"
119
+ external
120
+ render={<a href={"https://x.com/intent/post?url=" + encodedUrl + "&text=" + encodedTitle} />}
121
+ >
122
+ Twitter/X
123
+ </Button>
124
+ <Button
125
+ role="secondary"
126
+ size="sm"
127
+ external
128
+ render={<a href={"https://www.linkedin.com/sharing/share-offsite/?url=" + encodedUrl} />}
129
+ >
130
+ LinkedIn
131
+ </Button>
132
+ </DescriptionList.Item>
133
+ </DescriptionList>
134
+ </aside>
135
+
136
+ <div className="col-span-full mt-6 lg:col-span-8 lg:mt-0">
137
+ <SectionCaption as="p">Article</SectionCaption>
138
+ <Prose className="mt-6">{children}</Prose>
139
+ </div>
140
+ </article>
141
+ </main>
142
+ );
143
+ }
@@ -1,4 +1,4 @@
1
- // @thewhatmatters/wmds@0.3.0 · Pattern — editorial profile
1
+ // @thewhatmatters/wmds@0.4.1 · 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.3.0",
3
+ "version": "0.4.1",
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,14 @@
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-button-button--copy-button",
39
+ "title": "Components/Button/Button",
40
+ "name": "Pattern — copy button",
41
+ "file": "patterns/components-button-button--copy-button.tsx",
42
+ "storybook": "?path=/story/components-button-button--copy-button",
43
+ "source": "src/components/atoms/Button/Button.stories.tsx"
44
+ },
21
45
  {
22
46
  "id": "components-button-button--destructive-action",
23
47
  "title": "Components/Button/Button",
@@ -26,6 +50,14 @@
26
50
  "storybook": "?path=/story/components-button-button--destructive-action",
27
51
  "source": "src/components/atoms/Button/Button.stories.tsx"
28
52
  },
53
+ {
54
+ "id": "components-button-button--external-link",
55
+ "title": "Components/Button/Button",
56
+ "name": "Pattern — external link",
57
+ "file": "patterns/components-button-button--external-link.tsx",
58
+ "storybook": "?path=/story/components-button-button--external-link",
59
+ "source": "src/components/atoms/Button/Button.stories.tsx"
60
+ },
29
61
  {
30
62
  "id": "components-button-button--ghost-action",
31
63
  "title": "Components/Button/Button",
@@ -234,6 +266,22 @@
234
266
  "storybook": "?path=/story/components-chart--unit-grid-composition",
235
267
  "source": "src/components/organisms/Chart/Chart.stories.tsx"
236
268
  },
269
+ {
270
+ "id": "components-chatdock--pattern-chat-dock",
271
+ "title": "Components/ChatDock",
272
+ "name": "Pattern — chat dock",
273
+ "file": "patterns/components-chatdock--pattern-chat-dock.tsx",
274
+ "storybook": "?path=/story/components-chatdock--pattern-chat-dock",
275
+ "source": "src/components/organisms/ChatDock/ChatDock.stories.tsx"
276
+ },
277
+ {
278
+ "id": "components-chatdock--pattern-start-project-gate",
279
+ "title": "Components/ChatDock",
280
+ "name": "Pattern — start a project gate",
281
+ "file": "patterns/components-chatdock--pattern-start-project-gate.tsx",
282
+ "storybook": "?path=/story/components-chatdock--pattern-start-project-gate",
283
+ "source": "src/components/organisms/ChatDock/ChatDock.stories.tsx"
284
+ },
237
285
  {
238
286
  "id": "components-chatqa--pattern-qa-with-reply",
239
287
  "title": "Components/ChatQa",
@@ -250,6 +298,14 @@
250
298
  "storybook": "?path=/story/components-checkbox-checkbox--default",
251
299
  "source": "src/components/atoms/Checkbox/Checkbox.stories.tsx"
252
300
  },
301
+ {
302
+ "id": "components-checkbox-checkbox--with-count",
303
+ "title": "Components/Checkbox/Checkbox",
304
+ "name": "Pattern — with count",
305
+ "file": "patterns/components-checkbox-checkbox--with-count.tsx",
306
+ "storybook": "?path=/story/components-checkbox-checkbox--with-count",
307
+ "source": "src/components/atoms/Checkbox/Checkbox.stories.tsx"
308
+ },
253
309
  {
254
310
  "id": "components-checkbox-checkbox--with-description",
255
311
  "title": "Components/Checkbox/Checkbox",
@@ -266,6 +322,30 @@
266
322
  "storybook": "?path=/story/components-checkbox-checkboxgroup--default",
267
323
  "source": "src/components/molecules/CheckboxGroup/CheckboxGroup.stories.tsx"
268
324
  },
325
+ {
326
+ "id": "components-checkbox-checkboxgroup--option-counts",
327
+ "title": "Components/Checkbox/CheckboxGroup",
328
+ "name": "Pattern — option counts",
329
+ "file": "patterns/components-checkbox-checkboxgroup--option-counts.tsx",
330
+ "storybook": "?path=/story/components-checkbox-checkboxgroup--option-counts",
331
+ "source": "src/components/molecules/CheckboxGroup/CheckboxGroup.stories.tsx"
332
+ },
333
+ {
334
+ "id": "components-descriptionlist--details",
335
+ "title": "Components/DescriptionList",
336
+ "name": "Pattern — details",
337
+ "file": "patterns/components-descriptionlist--details.tsx",
338
+ "storybook": "?path=/story/components-descriptionlist--details",
339
+ "source": "src/components/molecules/DescriptionList/DescriptionList.stories.tsx"
340
+ },
341
+ {
342
+ "id": "components-descriptionlist--post-metadata",
343
+ "title": "Components/DescriptionList",
344
+ "name": "Pattern — post metadata",
345
+ "file": "patterns/components-descriptionlist--post-metadata.tsx",
346
+ "storybook": "?path=/story/components-descriptionlist--post-metadata",
347
+ "source": "src/components/molecules/DescriptionList/DescriptionList.stories.tsx"
348
+ },
269
349
  {
270
350
  "id": "components-dialog--alert-dialog-destructive",
271
351
  "title": "Components/Dialog",
@@ -402,6 +482,14 @@
402
482
  "storybook": "?path=/story/components-herotilestack--marketing-hero-with-gallery-intro",
403
483
  "source": "src/components/organisms/HeroTileStack/HeroTileStack.galleryIntro.stories.tsx"
404
484
  },
485
+ {
486
+ "id": "components-indexlist--post-index",
487
+ "title": "Components/IndexList",
488
+ "name": "Pattern — post index",
489
+ "file": "patterns/components-indexlist--post-index.tsx",
490
+ "storybook": "?path=/story/components-indexlist--post-index",
491
+ "source": "src/components/molecules/IndexList/IndexList.stories.tsx"
492
+ },
405
493
  {
406
494
  "id": "components-intakeconfirmation--booked-pattern",
407
495
  "title": "Components/IntakeConfirmation",
@@ -522,14 +610,6 @@
522
610
  "storybook": "?path=/story/components-pillgroup--pill-group-pattern",
523
611
  "source": "src/components/molecules/PillGroup/PillGroup.stories.tsx"
524
612
  },
525
- {
526
- "id": "components-promptbar--pattern-marketing-composer",
527
- "title": "Components/PromptBar",
528
- "name": "Pattern — marketing composer",
529
- "file": "patterns/components-promptbar--pattern-marketing-composer.tsx",
530
- "storybook": "?path=/story/components-promptbar--pattern-marketing-composer",
531
- "source": "src/components/molecules/PromptBar/PromptBar.stories.tsx"
532
- },
533
613
  {
534
614
  "id": "components-promptbar--pattern-prompt-bar",
535
615
  "title": "Components/PromptBar",
@@ -538,6 +618,14 @@
538
618
  "storybook": "?path=/story/components-promptbar--pattern-prompt-bar",
539
619
  "source": "src/components/molecules/PromptBar/PromptBar.stories.tsx"
540
620
  },
621
+ {
622
+ "id": "components-prose--article-body",
623
+ "title": "Components/Prose",
624
+ "name": "Pattern — article body",
625
+ "file": "patterns/components-prose--article-body.tsx",
626
+ "storybook": "?path=/story/components-prose--article-body",
627
+ "source": "src/components/atoms/Prose/Prose.stories.tsx"
628
+ },
541
629
  {
542
630
  "id": "components-radio-radiogroup--default",
543
631
  "title": "Components/Radio/RadioGroup",
@@ -562,6 +650,22 @@
562
650
  "storybook": "?path=/story/components-scrollhorizontal--with-intro",
563
651
  "source": "src/components/organisms/ScrollHorizontal/ScrollHorizontal.stories.tsx"
564
652
  },
653
+ {
654
+ "id": "components-sectioncaption--column-caption",
655
+ "title": "Components/SectionCaption",
656
+ "name": "Pattern — column caption",
657
+ "file": "patterns/components-sectioncaption--column-caption.tsx",
658
+ "storybook": "?path=/story/components-sectioncaption--column-caption",
659
+ "source": "src/components/atoms/SectionCaption/SectionCaption.stories.tsx"
660
+ },
661
+ {
662
+ "id": "components-sectioncaption--with-action",
663
+ "title": "Components/SectionCaption",
664
+ "name": "Pattern — caption with an action",
665
+ "file": "patterns/components-sectioncaption--with-action.tsx",
666
+ "storybook": "?path=/story/components-sectioncaption--with-action",
667
+ "source": "src/components/atoms/SectionCaption/SectionCaption.stories.tsx"
668
+ },
565
669
  {
566
670
  "id": "components-sheet--bottom-sheet",
567
671
  "title": "Components/Sheet",
@@ -730,6 +834,14 @@
730
834
  "storybook": "?path=/story/components-textlink--inline-prose",
731
835
  "source": "src/components/atoms/TextLink/TextLink.stories.tsx"
732
836
  },
837
+ {
838
+ "id": "components-textlink--quiet-title",
839
+ "title": "Components/TextLink",
840
+ "name": "Pattern — quiet title link",
841
+ "file": "patterns/components-textlink--quiet-title.tsx",
842
+ "storybook": "?path=/story/components-textlink--quiet-title",
843
+ "source": "src/components/atoms/TextLink/TextLink.stories.tsx"
844
+ },
733
845
  {
734
846
  "id": "components-textlink--supporting-copy",
735
847
  "title": "Components/TextLink",
@@ -834,6 +946,14 @@
834
946
  "storybook": "?path=/story/guides-chart-explorations--comparison-gallery",
835
947
  "source": "src/guides/ChartExplorations/ChartExplorations.stories.tsx"
836
948
  },
949
+ {
950
+ "id": "guides-filter-panel--filtered-index",
951
+ "title": "Guides/Filter panel",
952
+ "name": "Pattern — filtered index",
953
+ "file": "patterns/guides-filter-panel--filtered-index.tsx",
954
+ "storybook": "?path=/story/guides-filter-panel--filtered-index",
955
+ "source": "src/guides/FilterPanel/FilterPanel.stories.tsx"
956
+ },
837
957
  {
838
958
  "id": "guides-overlay-flows--notification-preferences",
839
959
  "title": "Guides/Overlay flows",
@@ -842,6 +962,14 @@
842
962
  "storybook": "?path=/story/guides-overlay-flows--notification-preferences",
843
963
  "source": "src/guides/OverlayFlows/OverlayFlows.stories.tsx"
844
964
  },
965
+ {
966
+ "id": "guides-post-page--post-page",
967
+ "title": "Guides/Post page",
968
+ "name": "Pattern — post page",
969
+ "file": "patterns/guides-post-page--post-page.tsx",
970
+ "storybook": "?path=/story/guides-post-page--post-page",
971
+ "source": "src/guides/PostPage/PostPage.stories.tsx"
972
+ },
845
973
  {
846
974
  "id": "guides-profile-typography--editorial-profile",
847
975
  "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.3.0",
4
+ "version": "0.4.1",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -1,37 +0,0 @@
1
- // @thewhatmatters/wmds@0.3.0 · Pattern — marketing composer
2
- // Storybook: Components/PromptBar → Pattern — marketing composer (?path=/story/components-promptbar--pattern-marketing-composer)
3
- // Show code — copy verbatim and keep this header; upgrades find pasted patterns by it.
4
-
5
- import { useState } from "react";
6
- import { PromptBar } from "@thewhatmatters/wmds";
7
-
8
- /**
9
- * Marketing homepage composer, pinned to the bottom of the viewport on the page grid.
10
- * Sending hands off to the app's ask page: route to /ask?q=<prompt>, and the ask page
11
- * starts the conversation with `q` as its first prompt.
12
- * Next.js: onHandOff={(prompt) => router.push(`/ask?q=${encodeURIComponent(prompt)}`)}
13
- */
14
- export function MarketingComposer({ onHandOff }: { onHandOff: (prompt: string) => void }) {
15
- const [draft, setDraft] = useState("");
16
-
17
- return (
18
- <div className="pointer-events-none fixed inset-x-0 bottom-0 z-40 pb-[max(1rem,env(safe-area-inset-bottom))]">
19
- <div className="grid-page [--grid-max:40rem]">
20
- <div className="band">
21
- <div className="pointer-events-auto col-span-full">
22
- <PromptBar
23
- value={draft}
24
- onValueChange={setDraft}
25
- onSend={(value) => {
26
- const prompt = value.trim();
27
- if (prompt.length === 0) return;
28
- setDraft("");
29
- onHandOff(prompt);
30
- }}
31
- />
32
- </div>
33
- </div>
34
- </div>
35
- </div>
36
- );
37
- }