@mediaradar/design-components 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -17,6 +17,9 @@ import { VariantProps } from 'class-variance-authority';
17
17
  * not just a decorative hover effect. Both modes close on Escape and on any
18
18
  * outside click. The bubble is portaled to document.body and repositions
19
19
  * itself to stay clear of the right viewport edge.
20
+ * @a11y The trigger is a <button> reporting aria-expanded; the bubble is
21
+ * role="tooltip" and Escape dismisses it. It describes its anchor rather
22
+ * than naming it, so the anchor still needs its own accessible name.
20
23
  * @usage
21
24
  * ```tsx
22
25
  * <InfoTip text="Trims time periods to data that has finalized across the selected media formats." />
@@ -61,6 +64,10 @@ declare function InfoTip({ text, size, label, trigger }: InfoTipProps): react.JS
61
64
  * other close path discards the draft, so the trigger label never previews
62
65
  * an unapplied change. The pinned "selected" section persists across
63
66
  * searches and collapses when the search is cleared.
67
+ * @a11y Trigger is a <button> with aria-expanded and aria-haspopup; the menu is
68
+ * role="dialog", closed on Escape. Option rows are role="checkbox" with
69
+ * aria-checked, so each row is itself the control -- don't wrap them in
70
+ * another clickable element.
64
71
  * @usage
65
72
  * ```tsx
66
73
  * <AdvertiserQuickFilter options={advertisers} selected={selected} onChange={setSelected} />
@@ -139,6 +146,78 @@ interface AdvertiserSearchPromptProps {
139
146
  }
140
147
  declare function AdvertiserSearchPrompt({ label }: AdvertiserSearchPromptProps): react.JSX.Element;
141
148
 
149
+ /**
150
+ * Fill and ink. `tone` answers "what kind of thing is this", never "what
151
+ * colour do I want" -- a caller reaching for `danger` because red looks good
152
+ * is the start of the drift this component replaces.
153
+ */
154
+ type BadgeTone = "neutral" | "brand" | "success" | "warning" | "danger" | "ai" | "overlay";
155
+ /**
156
+ * Shape and case. Orthogonal to tone: any tone can be any kind, which is what
157
+ * keeps this two small props instead of one list of every combination the
158
+ * product happens to use today.
159
+ */
160
+ type BadgeKind = "label" | "count" | "eyebrow";
161
+ interface BadgeProps {
162
+ /** What kind of thing this marks. Not a colour choice. */
163
+ tone?: BadgeTone;
164
+ /** Shape and case. `count` for a number, `eyebrow` for a category label. */
165
+ kind?: BadgeKind;
166
+ /** Text, or an icon plus text. Keep it to a few words -- a badge annotates, it does not explain. */
167
+ children: ReactNode;
168
+ className?: string;
169
+ }
170
+ /**
171
+ * @pattern Badge
172
+ * @description The one small label that annotates something else: a count on
173
+ * a filter, a status, a category, an AI marker, a flag over artwork. Two
174
+ * props -- `tone` (what kind of thing) and `kind` (shape and case) -- which
175
+ * between them cover every badge treatment in the product.
176
+ * @props
177
+ * tone?: 'neutral' | 'brand' | 'success' | 'warning' | 'danger' | 'ai' | 'overlay' (default 'neutral')
178
+ * kind?: 'label' | 'count' | 'eyebrow' (default 'label')
179
+ * children: ReactNode
180
+ * @tokens text-micro, rounded-full, bg-subtle, bg-brand-primarySoft,
181
+ * bg-status-success/15, bg-status-warning/30, bg-status-danger/15, bg-scrim,
182
+ * text-text-secondary, text-brand-primaryAction, text-text-success,
183
+ * text-text-warning, text-text-danger, text-ai-ontint, text-brand-onprimary,
184
+ * border-transparent
185
+ * @behavior Not interactive and carries no role: a badge is a label on
186
+ * something else, so it is read as part of that thing rather than announced
187
+ * separately. Anything clickable is a Button or a filter chip, not a Badge.
188
+ * Every badge is one size -- the `micro` step (11px) -- because a badge
189
+ * that competes with the content it annotates has stopped being a badge.
190
+ * @variant neutral - Default. A category, a count of something unremarkable, a quiet flag.
191
+ * @variant brand - This item is special in a way the product cares about -- primary, selected, yours.
192
+ * @variant success - A finished or healthy state. Never just "green because it is good news".
193
+ * @variant warning - Needs attention but is not broken. Partial data, an approaching limit.
194
+ * @variant danger - Failed, expired, or over a hard limit. Not merely "important".
195
+ * @variant ai - Generated or AI-assisted content, so the reader knows where it came from.
196
+ * @variant overlay - Sits on top of an image or video still, where no theme surface exists behind it.
197
+ * @use when you need to annotate something else -- a status on a row, a count
198
+ * on a filter tab, a category on a card, a flag over a thumbnail.
199
+ * @use when the label is a few words at most and the reader does not need to
200
+ * act on it.
201
+ * @avoid when the thing is clickable -- use Button, or a filter chip. A badge
202
+ * has no role, no focus state and no hit target, so making one clickable
203
+ * hides the action from keyboard and screen-reader users entirely.
204
+ * @avoid when the text is a sentence, or needs to wrap. A badge that wraps
205
+ * has become a callout; use one.
206
+ * @avoid when you are choosing the tone by colour rather than by meaning. A
207
+ * red badge that does not mean failure teaches readers to ignore red.
208
+ * @avoid when several sit in a row and all say something different -- that is
209
+ * a definition list wearing badges, and reads as noise.
210
+ * @usage
211
+ * ```tsx
212
+ * <Badge>Financial Services</Badge>
213
+ * <Badge tone="brand" kind="count">{selected.length}</Badge>
214
+ * <Badge tone="warning">Partial month</Badge>
215
+ * <Badge kind="eyebrow" tone="neutral">Saved view</Badge>
216
+ * <Badge tone="brand"><Check size={11} aria-hidden="true" />Primary</Badge>
217
+ * ```
218
+ */
219
+ declare function Badge({ tone, kind, children, className }: BadgeProps): react.JSX.Element;
220
+
142
221
  /**
143
222
  * @pattern CheckBox
144
223
  * @description Tri-state checkbox rendered as a `<button>` (not a native
@@ -159,6 +238,13 @@ declare function AdvertiserSearchPrompt({ label }: AdvertiserSearchPromptProps):
159
238
  * `stopPropagation` before firing `onChange` by default, so nesting this
160
239
  * inside a row that toggles on its own click can't fire the toggle twice;
161
240
  * pass `stopPropagation={false}` where the row should also see the click.
241
+ * @a11y The <button> itself carries role="checkbox" and aria-checked ("mixed"
242
+ * when indeterminate), so it IS the control -- never nest it inside
243
+ * another button or a row that is itself clickable. `ariaLabel` is
244
+ * required because the box renders no text. Where the whole row should be
245
+ * the control, use CheckBoxBox inside your own <button role="checkbox">
246
+ * instead: a <button> inside a <button> is invalid markup, and a <div
247
+ * onClick> wrapper is not keyboard-reachable at all.
162
248
  * @usage
163
249
  * ```tsx
164
250
  * <CheckBox checked={selected} ariaLabel="Select row" onChange={toggle} />
@@ -221,6 +307,10 @@ declare function CheckBox({ checked, indeterminate, onChange, ariaLabel, disable
221
307
  * the control has no correct option: nesting the interactive `CheckBox`
222
308
  * produces a button inside a button, and dropping to a `<div>` with a click
223
309
  * handler loses keyboard access entirely.
310
+ * @a11y Presentational only: aria-hidden, no focus stop, no click handling. The
311
+ * ancestor owns role="checkbox", aria-checked, the accessible name and the
312
+ * click. Use it exactly when that ancestor is itself the control; use
313
+ * CheckBox when the box is the control.
224
314
  * @usage
225
315
  * ```tsx
226
316
  * <button type="button" role="checkbox" aria-checked={on} onClick={toggle}>
@@ -257,6 +347,9 @@ declare function CheckBoxBox({ checked, indeterminate, disabled, className }: Ch
257
347
  * values, so a previous unsaved edit never lingers into the next open. Only
258
348
  * the Apply button commits -- Cancel, Escape, and outside-click all discard
259
349
  * the draft silently.
350
+ * @a11y Trigger is a <button> with aria-expanded and an aria-label from the
351
+ * caption; the panel is role="dialog" and Escape closes it. Focus is not
352
+ * moved into the panel, so it is reached by tabbing.
260
353
  * @usage
261
354
  * ```tsx
262
355
  * <DateRangeFilter caption="Date Range" defaultValue="Rolling 6 months" onChange={setRange} />
@@ -327,6 +420,11 @@ declare function DateRangeFilter({ caption, defaultValue, value: controlledValue
327
420
  * The text input carries an `aria-label` derived from `placeholder` — the
328
421
  * wrapping `<label>` has no visible text content (just an icon and the
329
422
  * input), so it grants the input no accessible name on its own.
423
+ * @a11y The <input> takes its accessible name from `ariaLabel`. The match-mode
424
+ * popover is a radio group (role="radio" + aria-checked) and its trigger
425
+ * reports aria-expanded. Escape closes the popover only, leaving focus in
426
+ * the field -- a consumer nesting this inside another dismissible layer
427
+ * must sequence the two dismissals itself.
330
428
  * @usage
331
429
  * ```tsx
332
430
  * <SearchBar value={query} onChange={setQuery} />
@@ -390,6 +488,9 @@ declare function SearchBar({ placeholder, value, onChange, variant, matchValue,
390
488
  * anchor rather than a framework router Link — this package has no router
391
489
  * peer dependency, and the two consuming apps disagree on one (react-router-dom
392
490
  * vs next/link); a plain anchor is the one thing that works unmodified in both.
491
+ * @a11y Rail items are buttons and take their accessible name from `ariaLabel`,
492
+ * since several render icon-only. Without it they announce as unnamed
493
+ * buttons.
393
494
  * @usage
394
495
  * ```tsx
395
496
  * <SectionRail title="Sports" subtitle="Your tracked leagues" viewAllLabel="View all" viewAllHref="/sports">
@@ -432,6 +533,10 @@ declare function SectionRail({ title, titleSerif, subtitle, viewAllLabel, viewAl
432
533
  * invalid HTML that breaks click targeting (confirmed against how
433
534
  * mr-clientapps-insights-studio's own equivalent, `ChipOption`, already
434
535
  * avoids exactly this by rendering its InfoTip as a sibling, not a child).
536
+ * @a11y Trigger is a <button> with aria-expanded and aria-haspopup; the menu is
537
+ * role="listbox" with role="option" rows carrying aria-selected. Escape
538
+ * closes it. Use the listbox semantics as given -- adding a second role to
539
+ * the wrapper breaks the option/listbox relationship.
435
540
  * @usage
436
541
  * ```tsx
437
542
  * <DataScopeFilter value={finalOnly} onChange={setFinalOnly} />
@@ -467,6 +572,11 @@ declare function DataScopeFilter({ value, onChange, align }: DataScopeFilterProp
467
572
  * `onChange` ever fires, so nothing would ever get selected. Selecting an
468
573
  * option commits immediately and closes -- there's no separate draft/Apply
469
574
  * step; Escape and outside-click just close without side effects.
575
+ * @a11y Trigger is a <button>; the popover is role="listbox" with role="option"
576
+ * rows carrying aria-selected. The trigger owns the accessible name via
577
+ * the caption. The popover is not a focus trap and does not move focus on
578
+ * open, so keyboard users tab into it; a consumer rendering it inside
579
+ * another dismissible layer has to sequence the two dismissals itself.
470
580
  * @usage
471
581
  * ```tsx
472
582
  * <SelectFilter caption="Rank" value={rankFilter} onChange={setRankFilter} options={['All', 'VP / Director', 'Manager', 'Coordinator']} />
@@ -598,6 +708,10 @@ declare const QUIET_ACTION_CLASS = "rounded-control text-[13px] font-medium text
598
708
  * recovery buttons (e.g. "Switch workspace") after the two named ones, for
599
709
  * empty states whose fix isn't about filters at all -- primary gets the
600
710
  * filled treatment, everything else the quiet one.
711
+ * @a11y The optional action is a real <button> with visible text as its
712
+ * accessible name; the illustration is aria-hidden. Nothing here announces
713
+ * automatically, so a consumer replacing results should manage its own
714
+ * live region if the change needs announcing.
601
715
  * @usage
602
716
  * ```tsx
603
717
  * <NoResultsEmptyState hasFilters={hasFilters} onClearFilters={clearFilters} onEditFilters={openFilterTray} />
@@ -689,6 +803,8 @@ interface SummaryGroup {
689
803
  * increase, including one added to an already-open or already
690
804
  * manually-collapsed nonempty group, silently undoing the user's own
691
805
  * collapse. Sub-groups render indented beneath their parent's own items.
806
+ * @a11y The card's toggle is a button reporting aria-expanded, with an aria-
807
+ * label since it renders icon-only. Decorative marks are aria-hidden.
692
808
  * @usage
693
809
  * ```tsx
694
810
  * <WorkspaceSelectedCard groups={[{ id: 'advertisers', label: 'Advertisers', items: selected, onRemove: removeOne, onRemoveAll: clearAll }]} />
@@ -731,6 +847,9 @@ declare function WorkspaceSelectedCard({ groups, testIdPrefix }: WorkspaceSelect
731
847
  * `aria-hidden="true"` throughout, since none of the boxes carry real
732
848
  * content for assistive tech to announce. The bar count and profile are a
733
849
  * fixed shape, not driven by `height`.
850
+ * @a11y role="presentation" with aria-hidden marks: a visual stand-in with no
851
+ * data, deliberately invisible to assistive tech. Don't add a caption
852
+ * expecting it to be announced.
734
853
  * @usage
735
854
  * ```tsx
736
855
  * <BarChartPlaceholder />
@@ -756,6 +875,9 @@ declare function BarChartPlaceholder({ height }: BarChartPlaceholderProps): reac
756
875
  * @behavior Purely presentational -- `role="presentation"` and
757
876
  * `aria-hidden="true"` throughout (inherited from `AxisFrame`). The series
758
877
  * count and shapes are fixed; `height` is the only variable.
878
+ * @a11y role="presentation" with aria-hidden marks: a visual stand-in with no
879
+ * data, deliberately invisible to assistive tech. Don't add a caption
880
+ * expecting it to be announced.
759
881
  * @usage
760
882
  * ```tsx
761
883
  * <LineChartPlaceholder />
@@ -780,6 +902,9 @@ declare function LineChartPlaceholder({ height }: LineChartPlaceholderProps): re
780
902
  * @behavior Purely presentational -- `role="presentation"` and
781
903
  * `aria-hidden="true"` throughout. `legendRows` controls the blank legend
782
904
  * only; the disc's seam count is a fixed shape.
905
+ * @a11y role="presentation" with aria-hidden marks: it is a visual stand-in with
906
+ * no data, deliberately invisible to assistive tech. Don't add a caption
907
+ * expecting it to be announced.
783
908
  * @usage
784
909
  * ```tsx
785
910
  * <PieChartPlaceholder />
@@ -809,6 +934,8 @@ declare function PieChartPlaceholder({ height, legendRows }: PieChartPlaceholder
809
934
  * `bg-subtle` fill the default-strength seam disappears and the stack reads
810
935
  * as one undivided block. Legend rows are reversed (bottom-up) when `axis`
811
936
  * is off, matching a bare stack's usual bottom-anchored reading order.
937
+ * @a11y role="presentation" with aria-hidden marks: a visual stand-in with no
938
+ * data, deliberately invisible to assistive tech.
812
939
  * @usage
813
940
  * ```tsx
814
941
  * <StackedColumnPlaceholder />
@@ -858,6 +985,10 @@ declare function StackedColumnPlaceholder({ height, width, legendRows, axis, }:
858
985
  * right-aligned to the anchor's right edge -- both meant for `anchorRef`
859
986
  * anchors near the viewport's right edge, where a centred box would run
860
987
  * off-screen or read awkwardly beside a narrow icon.
988
+ * @a11y Renders role="tooltip" and is wired to its anchor by aria-describedby,
989
+ * so the anchor must exist and keep a stable id. It describes rather than
990
+ * labels: it is announced in addition to the anchor's own name, not
991
+ * instead of it, so never put the only label for a control in here.
861
992
  * @usage
862
993
  * ```tsx
863
994
  * <Tooltip label="Full advertiser name" whenTruncated>
@@ -959,6 +1090,10 @@ declare function AdvertiserNoResults({ query, hint, }: AdvertiserNoResultsProps)
959
1090
  * between "Select all" and "Deselect" based on `allSelected`.
960
1091
  * @tokens rounded-control, text-text-link, hover:bg-subtle
961
1092
  * @dependencies lucide-react
1093
+ * @a11y A plain <button>, not a checkbox -- it performs a bulk action rather
1094
+ * than holding a checked state, so it deliberately carries no
1095
+ * role="checkbox" or aria-checked. Its icon is aria-hidden and the visible
1096
+ * text is the accessible name.
962
1097
  * @usage
963
1098
  * ```tsx
964
1099
  * {results.length > 5 && (
@@ -992,6 +1127,9 @@ declare function SelectAllVisible({ count, allSelected, onToggle, testIdPrefix }
992
1127
  * need. Self-hides at `count === 0` so a caller can render it unconditionally.
993
1128
  * @tokens rounded-control, text-text-tertiary, hover:text-text-primary
994
1129
  * @dependencies lucide-react
1130
+ * @a11y The disclosure header is a button reporting aria-expanded, and the
1131
+ * chevron is aria-hidden. It is a disclosure, not a selection control --
1132
+ * the rows inside own their own semantics.
995
1133
  * @usage
996
1134
  * ```tsx
997
1135
  * <SelectedSection count={selected.length} open={open} onToggle={() => setOpen((o) => !o)}>
@@ -1147,6 +1285,10 @@ interface SelectionPickerProps<T extends PickerRow> {
1147
1285
  * with both an `onChange` and the row's own `onClick` -- `CheckBox` stops
1148
1286
  * click propagation before firing `onChange`, which is exactly what lets it
1149
1287
  * sit inside a row that toggles on its own click without double-firing.
1288
+ * @a11y Search is an <input>; result rows and the account-list menu
1289
+ * (role="menu") are buttons. Escape closes the menu. Loading, empty and
1290
+ * error states announce through role="status", so a consumer should NOT
1291
+ * wrap this in its own live region -- that would double-announce.
1150
1292
  * @usage
1151
1293
  * ```tsx
1152
1294
  * <SelectionPicker
@@ -1234,6 +1376,9 @@ interface PublisherBreakdownListProps {
1234
1376
  * the kind of forked-with-extra-indirection outcome this package's README
1235
1377
  * calls out as not actually shared. Per that guidance, this component stays
1236
1378
  * the flat-list case only; mr-clientapps' accordion stays where it is, un-migrated.
1379
+ * @a11y Rows are buttons. One row manages tabIndex directly and a row tooltip
1380
+ * uses role="tooltip", dismissed on Escape. The tooltip describes rather
1381
+ * than names, so never put a row's only label in it.
1237
1382
  * @usage
1238
1383
  * ```tsx
1239
1384
  * <PublisherBreakdownList rows={publishers} defaultVisibleCount={10} />
@@ -1242,7 +1387,7 @@ interface PublisherBreakdownListProps {
1242
1387
  declare function PublisherBreakdownList({ rows, defaultVisibleCount, padded, viewAllHref, renderLogo }: PublisherBreakdownListProps): react.JSX.Element;
1243
1388
 
1244
1389
  declare const buttonVariants: (props?: ({
1245
- variant?: "link" | "primary" | "secondary" | "ghost" | "danger" | null | undefined;
1390
+ variant?: "link" | "danger" | "primary" | "secondary" | "ghost" | null | undefined;
1246
1391
  size?: "icon" | "sm" | "md" | "lg" | null | undefined;
1247
1392
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1248
1393
  interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
@@ -1275,6 +1420,9 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantPr
1275
1420
  * and relies on its per-variant hover/brightness treatment instead. `loading`
1276
1421
  * implies `disabled` (and sets `aria-busy`), since a control mid-submit
1277
1422
  * shouldn't accept another click.
1423
+ * @a11y Renders a native <button>, so it keeps native keyboard behaviour.
1424
+ * `loading` sets aria-busy, and the spinner is aria-hidden. Icon-only
1425
+ * usage needs an aria-label from the caller -- nothing here supplies one.
1278
1426
  * @usage
1279
1427
  * ```tsx
1280
1428
  * <Button variant="primary" size="md">Save changes</Button>
@@ -1300,6 +1448,9 @@ interface FilterDropdownFooterProps {
1300
1448
  * visible. Shared by the Media/Advertiser/Date/Group By/Time Increment quick
1301
1449
  * filters on Sports Dashboard, Publisher Intel, and Opportunities.
1302
1450
  * @tokens border-border-default
1451
+ * @a11y Clear and Apply are real Buttons with visible text as their accessible
1452
+ * names, in that DOM order. It adds no roles or live regions of its own --
1453
+ * the menu that contains it owns dismissal and focus.
1303
1454
  * @usage
1304
1455
  * ```tsx
1305
1456
  * <FilterDropdownFooter onApply={handleApply} onClear={handleClear} />
@@ -1343,12 +1494,16 @@ interface NameListModalProps {
1343
1494
  * checkboxes under it. Shared by the Account Lists table and the list detail
1344
1495
  * page so create/rename look identical; `fieldLabel`/`placeholder` relabel it
1345
1496
  * for dialogs that name something other than a list.
1346
- * @tokens rounded-card, border-border-default, bg-surface, shadow-overlay, text-text-primary, text-text-secondary, rounded-control, bg-input, focus:border-brand-primaryAction
1497
+ * @tokens bg-scrim, rounded-card, border-border-default, bg-surface, shadow-overlay, text-text-primary, text-text-secondary, rounded-control, bg-input, focus:border-brand-primaryAction
1347
1498
  * @behavior Escape closes the dialog via `onCancel`, same as clicking the
1348
1499
  * scrim backdrop. Submit is disabled until the name is non-empty (trimmed).
1349
1500
  * When `options` are given they render as CheckBox rows below the field, and
1350
1501
  * their state comes back as `onSubmit`'s second argument -- a handler that
1351
1502
  * only takes the name stays valid.
1503
+ * @a11y role="dialog" with aria-modal, labelled by its title. The name field
1504
+ * takes focus on open and Escape closes via onCancel. Focus is NOT trapped
1505
+ * and NOT restored to the opener on close -- confirmed in the source -- so
1506
+ * a consumer that needs either must supply it around this component.
1352
1507
  * @usage
1353
1508
  * ```tsx
1354
1509
  * <NameListModal title="Rename list" initialName={list.name} submitLabel="Save" onCancel={close} onSubmit={rename} />
@@ -1379,10 +1534,6 @@ interface ChipContentProps {
1379
1534
  * @description Shared foundation for filter-bar chip triggers — pairs
1380
1535
  * `chipTriggerClass` with `ChipContent` for the caption/value/icon layout, so
1381
1536
  * every dropdown chip across the app matches.
1382
- * @props
1383
- * caption: string -- small uppercase label above the value
1384
- * value: ReactNode -- the current selection, shown below the caption
1385
- * icon?: ReactNode -- optional leading icon rendered before the caption/value stack
1386
1537
  * @tokens rounded-control, bg-input, border-border-default, border-border-strong, text-text-secondary, text-text-tertiary, text-text-primary, hover:bg-subtle, focus-visible:outline-border-focus, hover:text-text-primary
1387
1538
  * @behavior `chipTriggerClass` takes a `ChipVariant`: `box` (default, the
1388
1539
  * boxed two-line chip on the default border) or `inline` (same fill, darker
@@ -1392,6 +1543,9 @@ interface ChipContentProps {
1392
1543
  * treatment every dropdown trigger depends on — open/close, Escape, and
1393
1544
  * outside-click dismissal all live in each consumer, not here. A new dropdown
1394
1545
  * built on `chipTriggerClass` has to implement its own dismissal handling.
1546
+ * @a11y `chipTriggerClass` supplies only the shared focus-visible outline.
1547
+ * Roles, aria-expanded and dismissal all belong to the consumer's own
1548
+ * trigger -- this styles a control, it does not make one.
1395
1549
  * @usage
1396
1550
  * ```tsx
1397
1551
  * <button className={chipTriggerClass("box")}>
@@ -1429,6 +1583,9 @@ interface FilterChipBarProps<T extends ChipLike> {
1429
1583
  * @behavior A parked chip (key in `parkedKeys`) renders with no fill and a
1430
1584
  * dashed edge instead of the solid applied style, and its hover tooltip
1431
1585
  * reads "Not in this workspace" instead of the filter's group label.
1586
+ * @a11y Each chip's remove control is a button with its own aria-label naming
1587
+ * what it clears; the chip's icon is aria-hidden. An overflow tooltip uses
1588
+ * role="tooltip", which describes rather than labels.
1432
1589
  * @usage
1433
1590
  * ```tsx
1434
1591
  * <FilterChipBar chips={activeChips} onRemove={removeChip} onClearAll={clearAll} />
@@ -1482,6 +1639,9 @@ interface SortButtonProps<K extends string> {
1482
1639
  * standard three-state cycle — sort, reverse, then unsorted. `disabled` keeps
1483
1640
  * the column's inherited header color rather than dimming it — the column
1484
1641
  * isn't unavailable, there's just no data under it.
1642
+ * @a11y Renders a <button> and sets aria-sort on it. Intended for a table header
1643
+ * cell -- put it inside the <th> rather than replacing it, so the sort
1644
+ * state stays associated with the column for screen readers.
1485
1645
  * @usage
1486
1646
  * ```tsx
1487
1647
  * const [sort, setSort] = useState<SortState<'spend'>>(null)
@@ -1503,6 +1663,9 @@ interface PagerProps {
1503
1663
  * ellipsis gaps. Shared by My Exports and Account Matcher.
1504
1664
  * @tokens rounded-control, text-text-secondary, bg-brand-primaryAction, text-brand-onprimary, hover:bg-subtle, hover:text-text-primary, text-text-tertiary
1505
1665
  * @dependencies lucide-react
1666
+ * @a11y Page controls are buttons with their own aria-labels, and the current
1667
+ * page is marked with aria-current. Don't relabel them with visible text
1668
+ * alone -- the labels are what a screen reader announces.
1506
1669
  * @usage
1507
1670
  * ```tsx
1508
1671
  * <Pager page={page} totalPages={totalPages} onPage={setPage} />
@@ -1571,6 +1734,10 @@ declare function Tag({ children, className }: TagProps): react.JSX.Element;
1571
1734
  * Changing the per-page count reports it and leaves the host to reset to page
1572
1735
  * 1. With one page of results the pager still renders, so the bar's height
1573
1736
  * and the range readout don't come and go as filters narrow.
1737
+ * @a11y A <nav aria-label="Pagination"> wrapping <button> controls, each with
1738
+ * its own aria-label (Previous/Next/Last). The chevron icons are aria-
1739
+ * hidden, so the labels are the only accessible names -- don't override
1740
+ * them with visible text alone.
1574
1741
  * @usage
1575
1742
  * ```tsx
1576
1743
  * <TablePagination
@@ -1655,6 +1822,10 @@ interface MultiSelectGroup {
1655
1822
  * selection floor by silently ignoring any toggle that would drop below it —
1656
1823
  * there's no error state, the click just does nothing. Group parents are
1657
1824
  * tri-state and toggle all of their children in one action.
1825
+ * @a11y Trigger is a <button> with aria-expanded; the menu is role="dialog" and
1826
+ * Escape closes it. Option rows carry their own checkbox semantics. Focus
1827
+ * is not moved into the menu on open, so keyboard users tab into it in DOM
1828
+ * order.
1658
1829
  * @usage
1659
1830
  * ```tsx
1660
1831
  * <MultiSelectFilter caption="Media" options={mediaOptions} selected={selected} onChange={setSelected} />
@@ -1755,6 +1926,10 @@ interface ToggleProps {
1755
1926
  * need (usually label left, switch pushed right).
1756
1927
  * @variant md - Settings panels and modals, where the switch is the row's subject.
1757
1928
  * @variant sm - Dense rows next to 13px body text, where the md track overpowers its own label.
1929
+ * @a11y The <button> carries role="switch" and aria-checked, so it is the
1930
+ * control -- don't nest it in another interactive element. `label`
1931
+ * supplies the accessible name; without it the switch is announced
1932
+ * unlabelled.
1758
1933
  * @usage
1759
1934
  * ```tsx
1760
1935
  * <Toggle checked={enabled} onChange={setEnabled} label="Email news alerts" />
@@ -1790,6 +1965,9 @@ interface FreshnessProps {
1790
1965
  * timestamps stops reading as a wall of identical text. Color carries the
1791
1966
  * urgency signal at a glance.
1792
1967
  * @tokens bg-status-success, bg-status-warning, bg-text-tertiary
1968
+ * @a11y role="img" with an aria-label per level, so the dot has a text
1969
+ * alternative rather than announcing as decoration. The label is the only
1970
+ * thing a screen reader gets -- the colour carries no meaning on its own.
1793
1971
  * @usage
1794
1972
  * ```tsx
1795
1973
  * <Freshness level="fresh" />
@@ -1797,4 +1975,4 @@ interface FreshnessProps {
1797
1975
  */
1798
1976
  declare function Freshness({ level }: FreshnessProps): react.JSX.Element;
1799
1977
 
1800
- export { AISpark, type AISparkProps, AdvertiserNoResults, type AdvertiserNoResultsProps, AdvertiserQuickFilter, type AdvertiserQuickFilterProps, AdvertiserSearchPrompt, type AdvertiserSearchPromptProps, BarChartPlaceholder, type BarChartPlaceholderProps, Button, type ButtonProps, Card, type CardProps, CheckBox, CheckBoxBox, type CheckBoxBoxProps, type CheckBoxProps, ChipContent, type ChipContentProps, type ChipVariant, DEFAULT_SEARCH_MATCH, DataScopeFilter, type DataScopeFilterProps, type DateGranularity, DateRangeFilter, type DateRangeFilterProps, DateRangePicker, type DateRangePickerProps, FilterChipBar, type FilterChipBarProps, FilterDropdownFooter, type FilterDropdownFooterProps, Freshness, type FreshnessLevel, type FreshnessProps, InfoTip, type InfoTipProps, LineChartPlaceholder, type LineChartPlaceholderProps, MultiSelectFilter, type MultiSelectFilterProps, type MultiSelectGroup, NO_RESULTS_BODY, NO_RESULTS_TITLE, NameListModal, type NameListModalProps, type NoResultsAction, NoResultsEmptyState, type NoResultsEmptyStateProps, PRIMARY_ACTION_CLASS, Pager, type PagerProps, type PickerColumn, type PickerFilter, type PickerRow, PieChartPlaceholder, type PieChartPlaceholderProps, type PubBreak, PublisherBreakdownList, type PublisherBreakdownListProps, QUIET_ACTION_CLASS, SearchBar, type SearchBarProps, type SearchBarVariant, type SearchMatchMode, type SearchMatchValue, SectionRail, type SectionRailProps, SelectAllVisible, type SelectAllVisibleProps, SelectFilter, type SelectFilterProps, SelectedSection, type SelectedSectionProps, SelectionPicker, type SelectionPickerAccountList, type SelectionPickerProps, SortButton, type SortButtonProps, type SortDir, type SortState, StackedColumnPlaceholder, type StackedColumnPlaceholderProps, type SummaryGroup, type SummaryGroupItemValue, TablePagination, type TablePaginationProps, Tag, type TagProps, Toggle, type ToggleProps, Tooltip, type TooltipProps, WorkspaceAvatar, type WorkspaceAvatarProps, WorkspaceSelectedCard, type WorkspaceSelectedCardProps, buttonVariants, chipTriggerClass, cycleSort, noResultsBody, resolveRange, sortProp };
1978
+ export { AISpark, type AISparkProps, AdvertiserNoResults, type AdvertiserNoResultsProps, AdvertiserQuickFilter, type AdvertiserQuickFilterProps, AdvertiserSearchPrompt, type AdvertiserSearchPromptProps, Badge, type BadgeKind, type BadgeProps, type BadgeTone, BarChartPlaceholder, type BarChartPlaceholderProps, Button, type ButtonProps, Card, type CardProps, CheckBox, CheckBoxBox, type CheckBoxBoxProps, type CheckBoxProps, ChipContent, type ChipContentProps, type ChipVariant, DEFAULT_SEARCH_MATCH, DataScopeFilter, type DataScopeFilterProps, type DateGranularity, DateRangeFilter, type DateRangeFilterProps, DateRangePicker, type DateRangePickerProps, FilterChipBar, type FilterChipBarProps, FilterDropdownFooter, type FilterDropdownFooterProps, Freshness, type FreshnessLevel, type FreshnessProps, InfoTip, type InfoTipProps, LineChartPlaceholder, type LineChartPlaceholderProps, MultiSelectFilter, type MultiSelectFilterProps, type MultiSelectGroup, NO_RESULTS_BODY, NO_RESULTS_TITLE, NameListModal, type NameListModalProps, type NoResultsAction, NoResultsEmptyState, type NoResultsEmptyStateProps, PRIMARY_ACTION_CLASS, Pager, type PagerProps, type PickerColumn, type PickerFilter, type PickerRow, PieChartPlaceholder, type PieChartPlaceholderProps, type PubBreak, PublisherBreakdownList, type PublisherBreakdownListProps, QUIET_ACTION_CLASS, SearchBar, type SearchBarProps, type SearchBarVariant, type SearchMatchMode, type SearchMatchValue, SectionRail, type SectionRailProps, SelectAllVisible, type SelectAllVisibleProps, SelectFilter, type SelectFilterProps, SelectedSection, type SelectedSectionProps, SelectionPicker, type SelectionPickerAccountList, type SelectionPickerProps, SortButton, type SortButtonProps, type SortDir, type SortState, StackedColumnPlaceholder, type StackedColumnPlaceholderProps, type SummaryGroup, type SummaryGroupItemValue, TablePagination, type TablePaginationProps, Tag, type TagProps, Toggle, type ToggleProps, Tooltip, type TooltipProps, WorkspaceAvatar, type WorkspaceAvatarProps, WorkspaceSelectedCard, type WorkspaceSelectedCardProps, buttonVariants, chipTriggerClass, cycleSort, noResultsBody, resolveRange, sortProp };