@thewhatmatters/wmds 0.4.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 (236) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/CONSUMING.md +8 -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/promptBarStyles.d.ts +3 -2
  81. package/dist/components/molecules/PromptBar/promptBarStyles.d.ts.map +1 -1
  82. package/dist/components/molecules/PromptBar/promptBarStyles.js +1 -1
  83. package/dist/components/organisms/ChatDock/ChatDock.d.ts +120 -10
  84. package/dist/components/organisms/ChatDock/ChatDock.d.ts.map +1 -1
  85. package/dist/components/organisms/ChatDock/ChatDock.js +654 -212
  86. package/dist/components/organisms/ChatDock/ChatDockGate.d.ts +52 -0
  87. package/dist/components/organisms/ChatDock/ChatDockGate.d.ts.map +1 -0
  88. package/dist/components/organisms/ChatDock/ChatDockGate.js +161 -0
  89. package/dist/components/organisms/ChatDock/chatDockGateStyles.d.ts +16 -0
  90. package/dist/components/organisms/ChatDock/chatDockGateStyles.d.ts.map +1 -0
  91. package/dist/components/organisms/ChatDock/chatDockGateStyles.js +8 -0
  92. package/dist/components/organisms/ChatDock/chatDockStyles.d.ts +60 -10
  93. package/dist/components/organisms/ChatDock/chatDockStyles.d.ts.map +1 -1
  94. package/dist/components/organisms/ChatDock/chatDockStyles.js +15 -9
  95. package/dist/components/organisms/ChatDock/chatDockViewport.d.ts +21 -0
  96. package/dist/components/organisms/ChatDock/chatDockViewport.d.ts.map +1 -0
  97. package/dist/components/organisms/ChatDock/chatDockViewport.js +46 -0
  98. package/dist/components/organisms/Toast/Toast.js +5 -5
  99. package/dist/index.d.ts +7 -3
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +122 -112
  102. package/dist/lib/compositionAudit.d.ts.map +1 -1
  103. package/dist/lib/typography.d.ts +2 -2
  104. package/dist/lib/typography.d.ts.map +1 -1
  105. package/dist/lib/typography.js +27 -1
  106. package/dist/styles.css +1 -1
  107. package/dist/theme.css +1 -0
  108. package/dist/typography.css +34 -0
  109. package/docs/README.md +1 -1
  110. package/docs/component-contracts.md +15 -0
  111. package/docs/components.md +15 -11
  112. package/docs/exports.json +69 -4
  113. package/docs/patterns/components-accordion--flush.tsx +29 -0
  114. package/docs/patterns/components-avatar--dropdown-row.tsx +1 -1
  115. package/docs/patterns/components-badge--outline-tags.tsx +25 -0
  116. package/docs/patterns/components-badge--with-avatar.tsx +1 -1
  117. package/docs/patterns/components-button-button--copy-button.tsx +39 -0
  118. package/docs/patterns/components-button-button--destructive-action.tsx +1 -1
  119. package/docs/patterns/components-button-button--external-link.tsx +30 -0
  120. package/docs/patterns/components-button-button--ghost-action.tsx +1 -1
  121. package/docs/patterns/components-button-button--inverse-action.tsx +1 -1
  122. package/docs/patterns/components-button-button--link-render.tsx +1 -1
  123. package/docs/patterns/components-button-button--multi-state-badge.tsx +1 -1
  124. package/docs/patterns/components-button-button--outline-mono.tsx +1 -1
  125. package/docs/patterns/components-button-button--primary-action.tsx +1 -1
  126. package/docs/patterns/components-button-button--row-hug.tsx +1 -1
  127. package/docs/patterns/components-button-button--row-layout.tsx +1 -1
  128. package/docs/patterns/components-button-button--secondary-action.tsx +1 -1
  129. package/docs/patterns/components-button-button--suggestion-pills.tsx +1 -1
  130. package/docs/patterns/components-button-button--with-count.tsx +1 -1
  131. package/docs/patterns/components-button-button--with-icon.tsx +1 -1
  132. package/docs/patterns/components-button-floatingactionbutton--vertical-actions.tsx +1 -1
  133. package/docs/patterns/components-calembed--pattern-skip-in-footer.tsx +1 -1
  134. package/docs/patterns/components-card-card--body-slot-occupancy-history.tsx +1 -1
  135. package/docs/patterns/components-card-card--headerless-media.tsx +1 -1
  136. package/docs/patterns/components-card-card--outlined-layout.tsx +1 -1
  137. package/docs/patterns/components-card-card--pinned-height.tsx +1 -1
  138. package/docs/patterns/components-card-selectablecard--selectable-cards-pattern.tsx +1 -1
  139. package/docs/patterns/components-chart--audience-activity-heatmap.tsx +1 -1
  140. package/docs/patterns/components-chart--cartesian-no-data-gaps.tsx +1 -1
  141. package/docs/patterns/components-chart--occupancy-history-in-card.tsx +1 -1
  142. package/docs/patterns/components-chart--occupancy-in-card.tsx +1 -1
  143. package/docs/patterns/components-chart--post-reach-distribution.tsx +1 -1
  144. package/docs/patterns/components-chart--ranked-audience-breakdown.tsx +1 -1
  145. package/docs/patterns/components-chart--unit-grid-composition.tsx +1 -1
  146. package/docs/patterns/components-chatdock--pattern-chat-dock.tsx +101 -11
  147. package/docs/patterns/components-chatdock--pattern-start-project-gate.tsx +180 -0
  148. package/docs/patterns/components-chatqa--pattern-qa-with-reply.tsx +1 -1
  149. package/docs/patterns/components-checkbox-checkbox--default.tsx +1 -1
  150. package/docs/patterns/components-checkbox-checkbox--with-count.tsx +19 -0
  151. package/docs/patterns/components-checkbox-checkbox--with-description.tsx +1 -1
  152. package/docs/patterns/components-checkbox-checkboxgroup--default.tsx +1 -1
  153. package/docs/patterns/components-checkbox-checkboxgroup--option-counts.tsx +17 -0
  154. package/docs/patterns/components-descriptionlist--details.tsx +21 -0
  155. package/docs/patterns/components-descriptionlist--post-metadata.tsx +52 -0
  156. package/docs/patterns/components-dialog--alert-dialog-destructive.tsx +1 -1
  157. package/docs/patterns/components-dialog--default-dialog.tsx +1 -1
  158. package/docs/patterns/components-dialog--form-dialog.tsx +1 -1
  159. package/docs/patterns/components-displaycontrols--page-utilities.tsx +1 -1
  160. package/docs/patterns/components-field--horizontal-input.tsx +1 -1
  161. package/docs/patterns/components-field--horizontal-text-area.tsx +1 -1
  162. package/docs/patterns/components-field--multi-control.tsx +1 -1
  163. package/docs/patterns/components-footerreveal--marketing-hero-pattern.tsx +1 -1
  164. package/docs/patterns/components-footerreveal--marketing-hero-ruled-pattern.tsx +1 -1
  165. package/docs/patterns/components-footerreveal--marketing-hero-with-gallery-intro.tsx +1 -1
  166. package/docs/patterns/components-footerreveal--marketing-page-pattern.tsx +1 -1
  167. package/docs/patterns/components-footerreveal--ruled-grid-footer-pattern.tsx +1 -1
  168. package/docs/patterns/components-footerreveal--sequenced-marketing-hero.tsx +1 -1
  169. package/docs/patterns/components-herointro--two-line-intro.tsx +1 -1
  170. package/docs/patterns/components-herotilestack--marketing-hero-pattern.tsx +1 -1
  171. package/docs/patterns/components-herotilestack--marketing-hero-text-sequence.tsx +1 -1
  172. package/docs/patterns/components-herotilestack--marketing-hero-with-gallery-intro.tsx +1 -1
  173. package/docs/patterns/components-indexlist--post-index.tsx +36 -0
  174. package/docs/patterns/components-intakeconfirmation--booked-pattern.tsx +1 -1
  175. package/docs/patterns/components-intakeconfirmation--emailed-pattern.tsx +1 -1
  176. package/docs/patterns/components-intakeform--about-you-pattern.tsx +1 -1
  177. package/docs/patterns/components-intakemodal--intake-modal-pattern.tsx +1 -1
  178. package/docs/patterns/components-kbd--command-row.tsx +1 -1
  179. package/docs/patterns/components-kbd--numbered-choices.tsx +1 -1
  180. package/docs/patterns/components-kbd--shortcut.tsx +1 -1
  181. package/docs/patterns/components-moremenu--action-menu.tsx +1 -1
  182. package/docs/patterns/components-moremenu--in-card-header.tsx +1 -1
  183. package/docs/patterns/components-navlist--side-nav-settings.tsx +1 -1
  184. package/docs/patterns/components-pageheader--app-header-pattern.tsx +1 -1
  185. package/docs/patterns/components-pageheader--page-section-header.tsx +1 -1
  186. package/docs/patterns/components-pageheader--toolbar-header.tsx +1 -1
  187. package/docs/patterns/components-panel--end-detail-rail.tsx +1 -1
  188. package/docs/patterns/components-pillgroup--pill-group-pattern.tsx +1 -1
  189. package/docs/patterns/components-promptbar--pattern-prompt-bar.tsx +1 -1
  190. package/docs/patterns/components-prose--article-body.tsx +11 -0
  191. package/docs/patterns/components-radio-radiogroup--default.tsx +1 -1
  192. package/docs/patterns/components-scrollhorizontal--project-gallery-pattern.tsx +1 -1
  193. package/docs/patterns/components-scrollhorizontal--with-intro.tsx +1 -1
  194. package/docs/patterns/components-sectioncaption--column-caption.tsx +16 -0
  195. package/docs/patterns/components-sectioncaption--with-action.tsx +19 -0
  196. package/docs/patterns/components-sheet--bottom-sheet.tsx +1 -1
  197. package/docs/patterns/components-sheet--end-sheet.tsx +1 -1
  198. package/docs/patterns/components-sitenav--brand-and-cta.tsx +1 -1
  199. package/docs/patterns/components-sitenav--centered-links.tsx +1 -1
  200. package/docs/patterns/components-sitenav--compact.tsx +1 -1
  201. package/docs/patterns/components-sitenav--marketing-header.tsx +1 -1
  202. package/docs/patterns/components-sitenav--mega-menu.tsx +1 -1
  203. package/docs/patterns/components-stat--insights-row.tsx +1 -1
  204. package/docs/patterns/components-stepprogress--step-progress-pattern.tsx +1 -1
  205. package/docs/patterns/components-switch--default.tsx +1 -1
  206. package/docs/patterns/components-switch--settings-row.tsx +1 -1
  207. package/docs/patterns/components-tab--settings-pages.tsx +1 -1
  208. package/docs/patterns/components-taskrows--action-details.tsx +1 -1
  209. package/docs/patterns/components-taskrows--capsules.tsx +1 -1
  210. package/docs/patterns/components-taskrows--card-with-status-rows.tsx +1 -1
  211. package/docs/patterns/components-taskrows--controlled-expand.tsx +1 -1
  212. package/docs/patterns/components-taskrows--custom-slots.tsx +1 -1
  213. package/docs/patterns/components-taskrows--status-list.tsx +1 -1
  214. package/docs/patterns/components-taskrows--tag-chips.tsx +1 -1
  215. package/docs/patterns/components-textlink--external-destination.tsx +1 -1
  216. package/docs/patterns/components-textlink--inline-prose.tsx +1 -1
  217. package/docs/patterns/components-textlink--quiet-title.tsx +15 -0
  218. package/docs/patterns/components-textlink--supporting-copy.tsx +1 -1
  219. package/docs/patterns/components-textsequence--default.tsx +1 -1
  220. package/docs/patterns/components-textsequence--reduced-motion.tsx +1 -1
  221. package/docs/patterns/components-textsequence--shapes.tsx +1 -1
  222. package/docs/patterns/components-toast--stacked-notifications.tsx +1 -1
  223. package/docs/patterns/components-tooltip--controlled.tsx +1 -1
  224. package/docs/patterns/components-tooltip--disabled-control.tsx +1 -1
  225. package/docs/patterns/components-tooltip--icon-action.tsx +1 -1
  226. package/docs/patterns/components-tooltip--keyboard-shortcut.tsx +1 -1
  227. package/docs/patterns/components-tooltip--placement.tsx +1 -1
  228. package/docs/patterns/foundations-scroll-fade--horizontal-rail.tsx +1 -1
  229. package/docs/patterns/foundations-scroll-fade--overflowing-list.tsx +1 -1
  230. package/docs/patterns/guides-chart-explorations--comparison-gallery.tsx +1 -1
  231. package/docs/patterns/guides-filter-panel--filtered-index.tsx +174 -0
  232. package/docs/patterns/guides-overlay-flows--notification-preferences.tsx +1 -1
  233. package/docs/patterns/guides-post-page--post-page.tsx +143 -0
  234. package/docs/patterns/guides-profile-typography--editorial-profile.tsx +1 -1
  235. package/docs/patterns/index.json +129 -1
  236. package/package.json +1 -1
@@ -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.4.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.4.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.4.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",
@@ -242,6 +274,14 @@
242
274
  "storybook": "?path=/story/components-chatdock--pattern-chat-dock",
243
275
  "source": "src/components/organisms/ChatDock/ChatDock.stories.tsx"
244
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
+ },
245
285
  {
246
286
  "id": "components-chatqa--pattern-qa-with-reply",
247
287
  "title": "Components/ChatQa",
@@ -258,6 +298,14 @@
258
298
  "storybook": "?path=/story/components-checkbox-checkbox--default",
259
299
  "source": "src/components/atoms/Checkbox/Checkbox.stories.tsx"
260
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
+ },
261
309
  {
262
310
  "id": "components-checkbox-checkbox--with-description",
263
311
  "title": "Components/Checkbox/Checkbox",
@@ -274,6 +322,30 @@
274
322
  "storybook": "?path=/story/components-checkbox-checkboxgroup--default",
275
323
  "source": "src/components/molecules/CheckboxGroup/CheckboxGroup.stories.tsx"
276
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
+ },
277
349
  {
278
350
  "id": "components-dialog--alert-dialog-destructive",
279
351
  "title": "Components/Dialog",
@@ -410,6 +482,14 @@
410
482
  "storybook": "?path=/story/components-herotilestack--marketing-hero-with-gallery-intro",
411
483
  "source": "src/components/organisms/HeroTileStack/HeroTileStack.galleryIntro.stories.tsx"
412
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
+ },
413
493
  {
414
494
  "id": "components-intakeconfirmation--booked-pattern",
415
495
  "title": "Components/IntakeConfirmation",
@@ -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.4.0",
4
+ "version": "0.4.1",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {