From e674f6e20cabdb20e956511dcce876b9a2f98915 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:44:08 +0800 Subject: [PATCH 1/8] fix(design-system): close the open COMPONENTS section 0.4 defect rows (#263) Track A4. Eleven named defects across ten registered components, plus the generated adoption/design-sync artefacts they move. - Button: forward `ref` to the underlying element. React 19 passes `ref` as an ordinary prop for function components, but `ButtonHTMLAttributes` does not carry it, so no caller could anchor a popover or drive focus off the node. Adds `testId` alongside it: `@types/react@19` gives components no `data-${string}` index signature, so a bare `data-testid` cannot be passed. - Progress: drive the indeterminate sweep from the `animate-shimmer` theme token instead of a hardcoded `animate-[shimmer_1.4s_ease-in-out_infinite]`, which pinned both the duration and a second easing. - StageList: clamp the step index to >= 1 (nothing-started announced "step 0 of N"), and move announcement off the `
    `. `aria-live` there made the whole list the live region, so advancing one stage re-read every label and detail; an sr-only `role="status"` sibling now carries the one sentence that changed. A sibling, not a child, so a five-stage job does not announce as "list, 6 items". - StatusMark: declare `DocumentStatus` here rather than aliasing it off `ClinicalSourceMetadata["document_status"]`. The alias inverted the dependency - a fifth row value would have widened the component's contract to a state it has no shape for and fallen silently through to `unknown` styling. The relationship is kept as a compile-time conformance assertion in the DOM suite. - PageHeader: floor the title column at `minmax(20ch,1fr)` and let the actions track give way. The existing wrap decided where the actions sat, not how much width they took, so the old track pair still sized actions to full max-content first and starved a long clinical title. - Disclosure: `print:block` on a collapsed panel. On paper there is no control to open, so a collapsed section printed as if the guideline never mentioned it - and undetectably, since the reader holding the printout cannot tell. The docstring's claim that `hidden` kept content reachable to Ctrl-F was false and is corrected. - AccessibleTable: keep a clipped dense header's full string as `title`, and build the expand control from the registered `Button`. The hand-rolled recipe had drifted off the system three ways: a ring focus treatment no other control uses, a raw `h-4 w-4` glyph off the icon scale, and its own tap/border/shadow recipe that would not follow a change to `Button`. - Tabs: a `value` matching no enabled tab no longer empties the tab order. Every tab took `tabIndex={-1}`, so Tab skipped the strip and the arrow keys that would have fixed it were unreachable - the state a stale saved filter or a deep link to a removed tab produces. This restores reachability only: no `onChange` is fired to repair caller state, and the panel wiring follows a tab that exists. - Pagination: clamp `page`/`pageCount` (a `page` of 0 emitted `onPageChange(-1)`), wrap the control row so a seven-page window fits 320px, hand focus to the current page when the pressed step button disables itself, and announce "Page N of M" through `LiveAnnouncer` - `aria-current` moved silently. - Links: `download` is now both type-omitted and written after the spread, so a caller cannot get the download glyph and detail on a link that navigates away. `LinkAction`'s hover nudge becomes a composited `translate-x`: `gap` is not in Tailwind's `transition` property list, so the old `hover:gap-2` never eased - it jumped, reflowing the arrow - and its `motion-reduce` guard was reducing a transition that did not exist. - Checkbox/RadioGroup: raw `size-[1.125rem]`/`h-[2px]` onto the spacing scale. Two of that row's three defects were stale when re-measured: ids already sanitise through `optionId`, and the group already carries hint/error. `Button` leaves the reference-only adoption snapshot because `AccessibleTable` genuinely adopts it, with a real production v2 mount; the assertion moves rather than relaxes, which that test documents as the expected shape of an adoption change. Contract ratchets fell and none rose: edge conflicts 28 -> 27, legacy shadow aliases 229 -> 228. The baseline is deliberately not lowered here - that belongs to the A3/A5 ratchet work, and lowering it would need a full debt-baseline regeneration this change has not verified. Verified: npm run check:design-system-contract exit 0 (all three sub-checks); tsc -p tsconfig.typecheck.json --noEmit exit 0; npm run lint exit 0 at --max-warnings 0; prettier --check . clean; 260 tests passed across the eight component/token suites plus the seven further suites touching these components. Co-Authored-By: Claude Opus 5 --- .design-sync/config.json | 4 +- docs/design-system/COMPONENTS.md | 56 ++-- docs/design-system/adoption-manifest.json | 17 +- src/components/AccessibleTable.tsx | 31 +- src/components/ui/button.tsx | 23 +- src/components/ui/choice.tsx | 9 +- src/components/ui/citation.tsx | 17 +- src/components/ui/disclosure.tsx | 23 +- src/components/ui/link.tsx | 26 +- src/components/ui/page-header.tsx | 10 +- src/components/ui/pagination.tsx | 59 +++- src/components/ui/progress.tsx | 138 +++++---- src/components/ui/status-mark.tsx | 21 +- src/components/ui/tabs.tsx | 25 +- tests/accessible-table.dom.test.tsx | 45 +++ tests/design-system-adoption.test.ts | 14 +- tests/ui-v2-components.dom.test.tsx | 361 +++++++++++++++++++++- 17 files changed, 733 insertions(+), 146 deletions(-) diff --git a/.design-sync/config.json b/.design-sync/config.json index d022658458..6491a74f25 100644 --- a/.design-sync/config.json +++ b/.design-sync/config.json @@ -24,7 +24,7 @@ "AnswerFooter": "className?: string; generatedAt?: string | null; publisher?: string | null; reviewDate?: string | null; version?: string | null;", "AsyncButton": "about?: string; accessKey?: string; \"aria-activedescendant\"?: string; \"aria-atomic\"?: (boolean | \"true\" | \"false\"); \"aria-autocomplete\"?: \"none\" | \"list\" | \"inline\" | \"both\"; \"aria-braillelabel\"?: string; \"aria-brailleroledescription\"?: string; \"aria-busy\"?: (boolean | \"true\" | \"false\"); \"aria-checked\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-colcount\"?: number; \"aria-colindex\"?: number; \"aria-colindextext\"?: string; \"aria-colspan\"?: number; \"aria-controls\"?: string; \"aria-current\"?: boolean | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\" | \"time\"; \"aria-describedby\"?: string; \"aria-description\"?: string; \"aria-details\"?: string; \"aria-disabled\"?: (boolean | \"true\" | \"false\"); \"aria-dropeffect\"?: \"none\" | \"link\" | \"copy\" | \"execute\" | \"move\" | \"popup\"; \"aria-errormessage\"?: string; \"aria-expanded\"?: (boolean | \"true\" | \"false\"); \"aria-flowto\"?: string; \"aria-grabbed\"?: (boolean | \"true\" | \"false\"); \"aria-haspopup\"?: boolean | \"true\" | \"false\" | \"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"; \"aria-hidden\"?: (boolean | \"true\" | \"false\"); \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"; \"aria-keyshortcuts\"?: string; \"aria-label\"?: string; \"aria-labelledby\"?: string; \"aria-level\"?: number; \"aria-live\"?: \"off\" | \"assertive\" | \"polite\"; \"aria-modal\"?: (boolean | \"true\" | \"false\"); \"aria-multiline\"?: (boolean | \"true\" | \"false\"); \"aria-multiselectable\"?: (boolean | \"true\" | \"false\"); \"aria-orientation\"?: \"horizontal\" | \"vertical\"; \"aria-owns\"?: string; \"aria-placeholder\"?: string; \"aria-posinset\"?: number; \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-readonly\"?: (boolean | \"true\" | \"false\"); \"aria-relevant\"?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"; \"aria-required\"?: (boolean | \"true\" | \"false\"); \"aria-roledescription\"?: string; \"aria-rowcount\"?: number; \"aria-rowindex\"?: number; \"aria-rowindextext\"?: string; \"aria-rowspan\"?: number; \"aria-selected\"?: (boolean | \"true\" | \"false\"); \"aria-setsize\"?: number; \"aria-sort\"?: \"none\" | \"ascending\" | \"descending\" | \"other\"; \"aria-valuemax\"?: number; \"aria-valuemin\"?: number; \"aria-valuenow\"?: number; \"aria-valuetext\"?: string; autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}); autoCorrect?: string; autoFocus?: boolean; autoSave?: string; busy: boolean; busyLabel: string; children: ReactNode; className?: string; color?: string; content?: string; contentEditable?: (boolean | \"true\" | \"false\") | \"inherit\" | \"plaintext-only\"; contextMenu?: string; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; }; datatype?: string; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; dir?: string; disabled?: boolean; draggable?: (boolean | \"true\" | \"false\"); enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\"; exportparts?: string; form?: string; formAction?: string | ((formData: FormData) => void | Promise); formEncType?: string; formMethod?: string; formNoValidate?: boolean; formTarget?: string; hidden?: boolean; id?: string; idleIcon?: ReactNode; inert?: boolean; inlist?: any; inputMode?: \"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"; is?: string; itemID?: string; itemProp?: string; itemRef?: string; itemScope?: boolean; itemType?: string; lang?: string; name?: string; nonce?: string; onAbort?: import(\"react\").ReactEventHandler; onAbortCapture?: import(\"react\").ReactEventHandler; onAnimationEnd?: import(\"react\").AnimationEventHandler; onAnimationEndCapture?: import(\"react\").AnimationEventHandler; onAnimationIteration?: import(\"react\").AnimationEventHandler; onAnimationIterationCapture?: import(\"react\").AnimationEventHandler; onAnimationStart?: import(\"react\").AnimationEventHandler; onAnimationStartCapture?: import(\"react\").AnimationEventHandler; onAuxClick?: import(\"react\").MouseEventHandler; onAuxClickCapture?: import(\"react\").MouseEventHandler; onBeforeInput?: import(\"react\").InputEventHandler; onBeforeInputCapture?: import(\"react\").InputEventHandler; onBeforeToggle?: import(\"react\").ToggleEventHandler; onBlur?: import(\"react\").FocusEventHandler; onBlurCapture?: import(\"react\").FocusEventHandler; onCanPlay?: import(\"react\").ReactEventHandler; onCanPlayCapture?: import(\"react\").ReactEventHandler; onCanPlayThrough?: import(\"react\").ReactEventHandler; onCanPlayThroughCapture?: import(\"react\").ReactEventHandler; onChange?: import(\"react\").ChangeEventHandler; onChangeCapture?: import(\"react\").ChangeEventHandler; onClick?: import(\"react\").MouseEventHandler; onClickCapture?: import(\"react\").MouseEventHandler; onCompositionEnd?: import(\"react\").CompositionEventHandler; onCompositionEndCapture?: import(\"react\").CompositionEventHandler; onCompositionStart?: import(\"react\").CompositionEventHandler; onCompositionStartCapture?: import(\"react\").CompositionEventHandler; onCompositionUpdate?: import(\"react\").CompositionEventHandler; onCompositionUpdateCapture?: import(\"react\").CompositionEventHandler; onContextMenu?: import(\"react\").MouseEventHandler; onContextMenuCapture?: import(\"react\").MouseEventHandler; onCopy?: import(\"react\").ClipboardEventHandler; onCopyCapture?: import(\"react\").ClipboardEventHandler; onCut?: import(\"react\").ClipboardEventHandler; onCutCapture?: import(\"react\").ClipboardEventHandler; onDoubleClick?: import(\"react\").MouseEventHandler; onDoubleClickCapture?: import(\"react\").MouseEventHandler; onDrag?: import(\"react\").DragEventHandler; onDragCapture?: import(\"react\").DragEventHandler; onDragEnd?: import(\"react\").DragEventHandler; onDragEndCapture?: import(\"react\").DragEventHandler; onDragEnter?: import(\"react\").DragEventHandler; onDragEnterCapture?: import(\"react\").DragEventHandler; onDragExit?: import(\"react\").DragEventHandler; onDragExitCapture?: import(\"react\").DragEventHandler; onDragLeave?: import(\"react\").DragEventHandler; onDragLeaveCapture?: import(\"react\").DragEventHandler; onDragOver?: import(\"react\").DragEventHandler; onDragOverCapture?: import(\"react\").DragEventHandler; onDragStart?: import(\"react\").DragEventHandler; onDragStartCapture?: import(\"react\").DragEventHandler; onDrop?: import(\"react\").DragEventHandler; onDropCapture?: import(\"react\").DragEventHandler; onDurationChange?: import(\"react\").ReactEventHandler; onDurationChangeCapture?: import(\"react\").ReactEventHandler; onEmptied?: import(\"react\").ReactEventHandler; onEmptiedCapture?: import(\"react\").ReactEventHandler; onEncrypted?: import(\"react\").ReactEventHandler; onEncryptedCapture?: import(\"react\").ReactEventHandler; onEnded?: import(\"react\").ReactEventHandler; onEndedCapture?: import(\"react\").ReactEventHandler; onError?: import(\"react\").ReactEventHandler; onErrorCapture?: import(\"react\").ReactEventHandler; onFocus?: import(\"react\").FocusEventHandler; onFocusCapture?: import(\"react\").FocusEventHandler; onGotPointerCapture?: import(\"react\").PointerEventHandler; onGotPointerCaptureCapture?: import(\"react\").PointerEventHandler; onInput?: import(\"react\").InputEventHandler; onInputCapture?: import(\"react\").InputEventHandler; onInvalid?: import(\"react\").ReactEventHandler; onInvalidCapture?: import(\"react\").ReactEventHandler; onKeyDown?: import(\"react\").KeyboardEventHandler; onKeyDownCapture?: import(\"react\").KeyboardEventHandler; onKeyPress?: import(\"react\").KeyboardEventHandler; onKeyPressCapture?: import(\"react\").KeyboardEventHandler; onKeyUp?: import(\"react\").KeyboardEventHandler; onKeyUpCapture?: import(\"react\").KeyboardEventHandler; onLoad?: import(\"react\").ReactEventHandler; onLoadCapture?: import(\"react\").ReactEventHandler; onLoadedData?: import(\"react\").ReactEventHandler; onLoadedDataCapture?: import(\"react\").ReactEventHandler; onLoadedMetadata?: import(\"react\").ReactEventHandler; onLoadedMetadataCapture?: import(\"react\").ReactEventHandler; onLoadStart?: import(\"react\").ReactEventHandler; onLoadStartCapture?: import(\"react\").ReactEventHandler; onLostPointerCapture?: import(\"react\").PointerEventHandler; onLostPointerCaptureCapture?: import(\"react\").PointerEventHandler; onMouseDown?: import(\"react\").MouseEventHandler; onMouseDownCapture?: import(\"react\").MouseEventHandler; onMouseEnter?: import(\"react\").MouseEventHandler; onMouseLeave?: import(\"react\").MouseEventHandler; onMouseMove?: import(\"react\").MouseEventHandler; onMouseMoveCapture?: import(\"react\").MouseEventHandler; onMouseOut?: import(\"react\").MouseEventHandler; onMouseOutCapture?: import(\"react\").MouseEventHandler; onMouseOver?: import(\"react\").MouseEventHandler; onMouseOverCapture?: import(\"react\").MouseEventHandler; onMouseUp?: import(\"react\").MouseEventHandler; onMouseUpCapture?: import(\"react\").MouseEventHandler; onPaste?: import(\"react\").ClipboardEventHandler; onPasteCapture?: import(\"react\").ClipboardEventHandler; onPause?: import(\"react\").ReactEventHandler; onPauseCapture?: import(\"react\").ReactEventHandler; onPlay?: import(\"react\").ReactEventHandler; onPlayCapture?: import(\"react\").ReactEventHandler; onPlaying?: import(\"react\").ReactEventHandler; onPlayingCapture?: import(\"react\").ReactEventHandler; onPointerCancel?: import(\"react\").PointerEventHandler; onPointerCancelCapture?: import(\"react\").PointerEventHandler; onPointerDown?: import(\"react\").PointerEventHandler; onPointerDownCapture?: import(\"react\").PointerEventHandler; onPointerEnter?: import(\"react\").PointerEventHandler; onPointerLeave?: import(\"react\").PointerEventHandler; onPointerMove?: import(\"react\").PointerEventHandler; onPointerMoveCapture?: import(\"react\").PointerEventHandler; onPointerOut?: import(\"react\").PointerEventHandler; onPointerOutCapture?: import(\"react\").PointerEventHandler; onPointerOver?: import(\"react\").PointerEventHandler; onPointerOverCapture?: import(\"react\").PointerEventHandler; onPointerUp?: import(\"react\").PointerEventHandler; onPointerUpCapture?: import(\"react\").PointerEventHandler; onProgress?: import(\"react\").ReactEventHandler; onProgressCapture?: import(\"react\").ReactEventHandler; onRateChange?: import(\"react\").ReactEventHandler; onRateChangeCapture?: import(\"react\").ReactEventHandler; onReset?: import(\"react\").ReactEventHandler; onResetCapture?: import(\"react\").ReactEventHandler; onScroll?: import(\"react\").UIEventHandler; onScrollCapture?: import(\"react\").UIEventHandler; onScrollEnd?: import(\"react\").UIEventHandler; onScrollEndCapture?: import(\"react\").UIEventHandler; onSeeked?: import(\"react\").ReactEventHandler; onSeekedCapture?: import(\"react\").ReactEventHandler; onSeeking?: import(\"react\").ReactEventHandler; onSeekingCapture?: import(\"react\").ReactEventHandler; onSelect?: import(\"react\").ReactEventHandler; onSelectCapture?: import(\"react\").ReactEventHandler; onStalled?: import(\"react\").ReactEventHandler; onStalledCapture?: import(\"react\").ReactEventHandler; onSubmit?: import(\"react\").SubmitEventHandler; onSubmitCapture?: import(\"react\").SubmitEventHandler; onSuspend?: import(\"react\").ReactEventHandler; onSuspendCapture?: import(\"react\").ReactEventHandler; onTimeUpdate?: import(\"react\").ReactEventHandler; onTimeUpdateCapture?: import(\"react\").ReactEventHandler; onToggle?: import(\"react\").ToggleEventHandler; onTouchCancel?: import(\"react\").TouchEventHandler; onTouchCancelCapture?: import(\"react\").TouchEventHandler; onTouchEnd?: import(\"react\").TouchEventHandler; onTouchEndCapture?: import(\"react\").TouchEventHandler; onTouchMove?: import(\"react\").TouchEventHandler; onTouchMoveCapture?: import(\"react\").TouchEventHandler; onTouchStart?: import(\"react\").TouchEventHandler; onTouchStartCapture?: import(\"react\").TouchEventHandler; onTransitionCancel?: import(\"react\").TransitionEventHandler; onTransitionCancelCapture?: import(\"react\").TransitionEventHandler; onTransitionEnd?: import(\"react\").TransitionEventHandler; onTransitionEndCapture?: import(\"react\").TransitionEventHandler; onTransitionRun?: import(\"react\").TransitionEventHandler; onTransitionRunCapture?: import(\"react\").TransitionEventHandler; onTransitionStart?: import(\"react\").TransitionEventHandler; onTransitionStartCapture?: import(\"react\").TransitionEventHandler; onVolumeChange?: import(\"react\").ReactEventHandler; onVolumeChangeCapture?: import(\"react\").ReactEventHandler; onWaiting?: import(\"react\").ReactEventHandler; onWaitingCapture?: import(\"react\").ReactEventHandler; onWheel?: import(\"react\").WheelEventHandler; onWheelCapture?: import(\"react\").WheelEventHandler; part?: string; popover?: \"\" | \"auto\" | \"manual\" | \"hint\"; popoverTarget?: string; popoverTargetAction?: \"toggle\" | \"show\" | \"hide\"; prefix?: string; property?: string; radioGroup?: string; rel?: string; resource?: string; results?: number; rev?: string; role?: import(\"react\").AriaRole; security?: string; slot?: string; spellCheck?: (boolean | \"true\" | \"false\"); style?: import(\"react\").CSSProperties; suppressContentEditableWarning?: boolean; suppressHydrationWarning?: boolean; tabIndex?: number; title?: string; translate?: \"yes\" | \"no\"; type?: \"submit\" | \"reset\" | \"button\"; typeof?: string; unselectable?: \"off\" | \"on\"; value?: string | number | readonly string[]; vocab?: string;", "Breadcrumb": "className?: string; items: Crumb[];", - "Button": "about?: string; accessKey?: string; \"aria-activedescendant\"?: string; \"aria-atomic\"?: (boolean | \"true\" | \"false\"); \"aria-autocomplete\"?: \"none\" | \"list\" | \"inline\" | \"both\"; \"aria-braillelabel\"?: string; \"aria-brailleroledescription\"?: string; \"aria-busy\"?: (boolean | \"true\" | \"false\"); \"aria-checked\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-colcount\"?: number; \"aria-colindex\"?: number; \"aria-colindextext\"?: string; \"aria-colspan\"?: number; \"aria-controls\"?: string; \"aria-current\"?: boolean | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\" | \"time\"; \"aria-describedby\"?: string; \"aria-description\"?: string; \"aria-details\"?: string; \"aria-disabled\"?: (boolean | \"true\" | \"false\"); \"aria-dropeffect\"?: \"none\" | \"link\" | \"copy\" | \"execute\" | \"move\" | \"popup\"; \"aria-errormessage\"?: string; \"aria-expanded\"?: (boolean | \"true\" | \"false\"); \"aria-flowto\"?: string; \"aria-grabbed\"?: (boolean | \"true\" | \"false\"); \"aria-haspopup\"?: boolean | \"true\" | \"false\" | \"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"; \"aria-hidden\"?: (boolean | \"true\" | \"false\"); \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"; \"aria-keyshortcuts\"?: string; \"aria-label\"?: string; \"aria-labelledby\"?: string; \"aria-level\"?: number; \"aria-live\"?: \"off\" | \"assertive\" | \"polite\"; \"aria-modal\"?: (boolean | \"true\" | \"false\"); \"aria-multiline\"?: (boolean | \"true\" | \"false\"); \"aria-multiselectable\"?: (boolean | \"true\" | \"false\"); \"aria-orientation\"?: \"horizontal\" | \"vertical\"; \"aria-owns\"?: string; \"aria-placeholder\"?: string; \"aria-posinset\"?: number; \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-readonly\"?: (boolean | \"true\" | \"false\"); \"aria-relevant\"?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"; \"aria-required\"?: (boolean | \"true\" | \"false\"); \"aria-roledescription\"?: string; \"aria-rowcount\"?: number; \"aria-rowindex\"?: number; \"aria-rowindextext\"?: string; \"aria-rowspan\"?: number; \"aria-selected\"?: (boolean | \"true\" | \"false\"); \"aria-setsize\"?: number; \"aria-sort\"?: \"none\" | \"ascending\" | \"descending\" | \"other\"; \"aria-valuemax\"?: number; \"aria-valuemin\"?: number; \"aria-valuenow\"?: number; \"aria-valuetext\"?: string; autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}); autoCorrect?: string; autoFocus?: boolean; autoSave?: string; block?: boolean; busy?: boolean; busyLabel?: string; children: ReactNode; className?: string; color?: string; content?: string; contentEditable?: (boolean | \"true\" | \"false\") | \"inherit\" | \"plaintext-only\"; contextMenu?: string; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; }; datatype?: string; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; dir?: string; disabled?: boolean; draggable?: (boolean | \"true\" | \"false\"); enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\"; exportparts?: string; form?: string; formAction?: string | ((formData: FormData) => void | Promise); formEncType?: string; formMethod?: string; formNoValidate?: boolean; formTarget?: string; hidden?: boolean; icon?: LucideIcon; id?: string; inert?: boolean; inlist?: any; inputMode?: \"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"; is?: string; itemID?: string; itemProp?: string; itemRef?: string; itemScope?: boolean; itemType?: string; lang?: string; name?: string; nonce?: string; onAbort?: import(\"react\").ReactEventHandler; onAbortCapture?: import(\"react\").ReactEventHandler; onAnimationEnd?: import(\"react\").AnimationEventHandler; onAnimationEndCapture?: import(\"react\").AnimationEventHandler; onAnimationIteration?: import(\"react\").AnimationEventHandler; onAnimationIterationCapture?: import(\"react\").AnimationEventHandler; onAnimationStart?: import(\"react\").AnimationEventHandler; onAnimationStartCapture?: import(\"react\").AnimationEventHandler; onAuxClick?: import(\"react\").MouseEventHandler; onAuxClickCapture?: import(\"react\").MouseEventHandler; onBeforeInput?: import(\"react\").InputEventHandler; onBeforeInputCapture?: import(\"react\").InputEventHandler; onBeforeToggle?: import(\"react\").ToggleEventHandler; onBlur?: import(\"react\").FocusEventHandler; onBlurCapture?: import(\"react\").FocusEventHandler; onCanPlay?: import(\"react\").ReactEventHandler; onCanPlayCapture?: import(\"react\").ReactEventHandler; onCanPlayThrough?: import(\"react\").ReactEventHandler; onCanPlayThroughCapture?: import(\"react\").ReactEventHandler; onChange?: import(\"react\").ChangeEventHandler; onChangeCapture?: import(\"react\").ChangeEventHandler; onClick?: import(\"react\").MouseEventHandler; onClickCapture?: import(\"react\").MouseEventHandler; onCompositionEnd?: import(\"react\").CompositionEventHandler; onCompositionEndCapture?: import(\"react\").CompositionEventHandler; onCompositionStart?: import(\"react\").CompositionEventHandler; onCompositionStartCapture?: import(\"react\").CompositionEventHandler; onCompositionUpdate?: import(\"react\").CompositionEventHandler; onCompositionUpdateCapture?: import(\"react\").CompositionEventHandler; onContextMenu?: import(\"react\").MouseEventHandler; onContextMenuCapture?: import(\"react\").MouseEventHandler; onCopy?: import(\"react\").ClipboardEventHandler; onCopyCapture?: import(\"react\").ClipboardEventHandler; onCut?: import(\"react\").ClipboardEventHandler; onCutCapture?: import(\"react\").ClipboardEventHandler; onDoubleClick?: import(\"react\").MouseEventHandler; onDoubleClickCapture?: import(\"react\").MouseEventHandler; onDrag?: import(\"react\").DragEventHandler; onDragCapture?: import(\"react\").DragEventHandler; onDragEnd?: import(\"react\").DragEventHandler; onDragEndCapture?: import(\"react\").DragEventHandler; onDragEnter?: import(\"react\").DragEventHandler; onDragEnterCapture?: import(\"react\").DragEventHandler; onDragExit?: import(\"react\").DragEventHandler; onDragExitCapture?: import(\"react\").DragEventHandler; onDragLeave?: import(\"react\").DragEventHandler; onDragLeaveCapture?: import(\"react\").DragEventHandler; onDragOver?: import(\"react\").DragEventHandler; onDragOverCapture?: import(\"react\").DragEventHandler; onDragStart?: import(\"react\").DragEventHandler; onDragStartCapture?: import(\"react\").DragEventHandler; onDrop?: import(\"react\").DragEventHandler; onDropCapture?: import(\"react\").DragEventHandler; onDurationChange?: import(\"react\").ReactEventHandler; onDurationChangeCapture?: import(\"react\").ReactEventHandler; onEmptied?: import(\"react\").ReactEventHandler; onEmptiedCapture?: import(\"react\").ReactEventHandler; onEncrypted?: import(\"react\").ReactEventHandler; onEncryptedCapture?: import(\"react\").ReactEventHandler; onEnded?: import(\"react\").ReactEventHandler; onEndedCapture?: import(\"react\").ReactEventHandler; onError?: import(\"react\").ReactEventHandler; onErrorCapture?: import(\"react\").ReactEventHandler; onFocus?: import(\"react\").FocusEventHandler; onFocusCapture?: import(\"react\").FocusEventHandler; onGotPointerCapture?: import(\"react\").PointerEventHandler; onGotPointerCaptureCapture?: import(\"react\").PointerEventHandler; onInput?: import(\"react\").InputEventHandler; onInputCapture?: import(\"react\").InputEventHandler; onInvalid?: import(\"react\").ReactEventHandler; onInvalidCapture?: import(\"react\").ReactEventHandler; onKeyDown?: import(\"react\").KeyboardEventHandler; onKeyDownCapture?: import(\"react\").KeyboardEventHandler; onKeyPress?: import(\"react\").KeyboardEventHandler; onKeyPressCapture?: import(\"react\").KeyboardEventHandler; onKeyUp?: import(\"react\").KeyboardEventHandler; onKeyUpCapture?: import(\"react\").KeyboardEventHandler; onLoad?: import(\"react\").ReactEventHandler; onLoadCapture?: import(\"react\").ReactEventHandler; onLoadedData?: import(\"react\").ReactEventHandler; onLoadedDataCapture?: import(\"react\").ReactEventHandler; onLoadedMetadata?: import(\"react\").ReactEventHandler; onLoadedMetadataCapture?: import(\"react\").ReactEventHandler; onLoadStart?: import(\"react\").ReactEventHandler; onLoadStartCapture?: import(\"react\").ReactEventHandler; onLostPointerCapture?: import(\"react\").PointerEventHandler; onLostPointerCaptureCapture?: import(\"react\").PointerEventHandler; onMouseDown?: import(\"react\").MouseEventHandler; onMouseDownCapture?: import(\"react\").MouseEventHandler; onMouseEnter?: import(\"react\").MouseEventHandler; onMouseLeave?: import(\"react\").MouseEventHandler; onMouseMove?: import(\"react\").MouseEventHandler; onMouseMoveCapture?: import(\"react\").MouseEventHandler; onMouseOut?: import(\"react\").MouseEventHandler; onMouseOutCapture?: import(\"react\").MouseEventHandler; onMouseOver?: import(\"react\").MouseEventHandler; onMouseOverCapture?: import(\"react\").MouseEventHandler; onMouseUp?: import(\"react\").MouseEventHandler; onMouseUpCapture?: import(\"react\").MouseEventHandler; onPaste?: import(\"react\").ClipboardEventHandler; onPasteCapture?: import(\"react\").ClipboardEventHandler; onPause?: import(\"react\").ReactEventHandler; onPauseCapture?: import(\"react\").ReactEventHandler; onPlay?: import(\"react\").ReactEventHandler; onPlayCapture?: import(\"react\").ReactEventHandler; onPlaying?: import(\"react\").ReactEventHandler; onPlayingCapture?: import(\"react\").ReactEventHandler; onPointerCancel?: import(\"react\").PointerEventHandler; onPointerCancelCapture?: import(\"react\").PointerEventHandler; onPointerDown?: import(\"react\").PointerEventHandler; onPointerDownCapture?: import(\"react\").PointerEventHandler; onPointerEnter?: import(\"react\").PointerEventHandler; onPointerLeave?: import(\"react\").PointerEventHandler; onPointerMove?: import(\"react\").PointerEventHandler; onPointerMoveCapture?: import(\"react\").PointerEventHandler; onPointerOut?: import(\"react\").PointerEventHandler; onPointerOutCapture?: import(\"react\").PointerEventHandler; onPointerOver?: import(\"react\").PointerEventHandler; onPointerOverCapture?: import(\"react\").PointerEventHandler; onPointerUp?: import(\"react\").PointerEventHandler; onPointerUpCapture?: import(\"react\").PointerEventHandler; onProgress?: import(\"react\").ReactEventHandler; onProgressCapture?: import(\"react\").ReactEventHandler; onRateChange?: import(\"react\").ReactEventHandler; onRateChangeCapture?: import(\"react\").ReactEventHandler; onReset?: import(\"react\").ReactEventHandler; onResetCapture?: import(\"react\").ReactEventHandler; onScroll?: import(\"react\").UIEventHandler; onScrollCapture?: import(\"react\").UIEventHandler; onScrollEnd?: import(\"react\").UIEventHandler; onScrollEndCapture?: import(\"react\").UIEventHandler; onSeeked?: import(\"react\").ReactEventHandler; onSeekedCapture?: import(\"react\").ReactEventHandler; onSeeking?: import(\"react\").ReactEventHandler; onSeekingCapture?: import(\"react\").ReactEventHandler; onSelect?: import(\"react\").ReactEventHandler; onSelectCapture?: import(\"react\").ReactEventHandler; onStalled?: import(\"react\").ReactEventHandler; onStalledCapture?: import(\"react\").ReactEventHandler; onSubmit?: import(\"react\").SubmitEventHandler; onSubmitCapture?: import(\"react\").SubmitEventHandler; onSuspend?: import(\"react\").ReactEventHandler; onSuspendCapture?: import(\"react\").ReactEventHandler; onTimeUpdate?: import(\"react\").ReactEventHandler; onTimeUpdateCapture?: import(\"react\").ReactEventHandler; onToggle?: import(\"react\").ToggleEventHandler; onTouchCancel?: import(\"react\").TouchEventHandler; onTouchCancelCapture?: import(\"react\").TouchEventHandler; onTouchEnd?: import(\"react\").TouchEventHandler; onTouchEndCapture?: import(\"react\").TouchEventHandler; onTouchMove?: import(\"react\").TouchEventHandler; onTouchMoveCapture?: import(\"react\").TouchEventHandler; onTouchStart?: import(\"react\").TouchEventHandler; onTouchStartCapture?: import(\"react\").TouchEventHandler; onTransitionCancel?: import(\"react\").TransitionEventHandler; onTransitionCancelCapture?: import(\"react\").TransitionEventHandler; onTransitionEnd?: import(\"react\").TransitionEventHandler; onTransitionEndCapture?: import(\"react\").TransitionEventHandler; onTransitionRun?: import(\"react\").TransitionEventHandler; onTransitionRunCapture?: import(\"react\").TransitionEventHandler; onTransitionStart?: import(\"react\").TransitionEventHandler; onTransitionStartCapture?: import(\"react\").TransitionEventHandler; onVolumeChange?: import(\"react\").ReactEventHandler; onVolumeChangeCapture?: import(\"react\").ReactEventHandler; onWaiting?: import(\"react\").ReactEventHandler; onWaitingCapture?: import(\"react\").ReactEventHandler; onWheel?: import(\"react\").WheelEventHandler; onWheelCapture?: import(\"react\").WheelEventHandler; part?: string; popover?: \"\" | \"auto\" | \"manual\" | \"hint\"; popoverTarget?: string; popoverTargetAction?: \"toggle\" | \"show\" | \"hide\"; prefix?: string; property?: string; radioGroup?: string; rel?: string; resource?: string; results?: number; rev?: string; role?: import(\"react\").AriaRole; security?: string; size?: ButtonSize; slot?: string; spellCheck?: (boolean | \"true\" | \"false\"); style?: import(\"react\").CSSProperties; suppressContentEditableWarning?: boolean; suppressHydrationWarning?: boolean; tabIndex?: number; title?: string; trailingIcon?: LucideIcon; translate?: \"yes\" | \"no\"; type?: \"submit\" | \"reset\" | \"button\"; typeof?: string; unselectable?: \"off\" | \"on\"; value?: string | number | readonly string[]; variant?: ButtonVariant; vocab?: string;", + "Button": "about?: string; accessKey?: string; \"aria-activedescendant\"?: string; \"aria-atomic\"?: (boolean | \"true\" | \"false\"); \"aria-autocomplete\"?: \"none\" | \"list\" | \"inline\" | \"both\"; \"aria-braillelabel\"?: string; \"aria-brailleroledescription\"?: string; \"aria-busy\"?: (boolean | \"true\" | \"false\"); \"aria-checked\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-colcount\"?: number; \"aria-colindex\"?: number; \"aria-colindextext\"?: string; \"aria-colspan\"?: number; \"aria-controls\"?: string; \"aria-current\"?: boolean | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\" | \"time\"; \"aria-describedby\"?: string; \"aria-description\"?: string; \"aria-details\"?: string; \"aria-disabled\"?: (boolean | \"true\" | \"false\"); \"aria-dropeffect\"?: \"none\" | \"link\" | \"copy\" | \"execute\" | \"move\" | \"popup\"; \"aria-errormessage\"?: string; \"aria-expanded\"?: (boolean | \"true\" | \"false\"); \"aria-flowto\"?: string; \"aria-grabbed\"?: (boolean | \"true\" | \"false\"); \"aria-haspopup\"?: boolean | \"true\" | \"false\" | \"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"; \"aria-hidden\"?: (boolean | \"true\" | \"false\"); \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"; \"aria-keyshortcuts\"?: string; \"aria-label\"?: string; \"aria-labelledby\"?: string; \"aria-level\"?: number; \"aria-live\"?: \"off\" | \"assertive\" | \"polite\"; \"aria-modal\"?: (boolean | \"true\" | \"false\"); \"aria-multiline\"?: (boolean | \"true\" | \"false\"); \"aria-multiselectable\"?: (boolean | \"true\" | \"false\"); \"aria-orientation\"?: \"horizontal\" | \"vertical\"; \"aria-owns\"?: string; \"aria-placeholder\"?: string; \"aria-posinset\"?: number; \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-readonly\"?: (boolean | \"true\" | \"false\"); \"aria-relevant\"?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"; \"aria-required\"?: (boolean | \"true\" | \"false\"); \"aria-roledescription\"?: string; \"aria-rowcount\"?: number; \"aria-rowindex\"?: number; \"aria-rowindextext\"?: string; \"aria-rowspan\"?: number; \"aria-selected\"?: (boolean | \"true\" | \"false\"); \"aria-setsize\"?: number; \"aria-sort\"?: \"none\" | \"ascending\" | \"descending\" | \"other\"; \"aria-valuemax\"?: number; \"aria-valuemin\"?: number; \"aria-valuenow\"?: number; \"aria-valuetext\"?: string; autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}); autoCorrect?: string; autoFocus?: boolean; autoSave?: string; block?: boolean; busy?: boolean; busyLabel?: string; children: ReactNode; className?: string; color?: string; content?: string; contentEditable?: (boolean | \"true\" | \"false\") | \"inherit\" | \"plaintext-only\"; contextMenu?: string; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; }; datatype?: string; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; dir?: string; disabled?: boolean; draggable?: (boolean | \"true\" | \"false\"); enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\"; exportparts?: string; form?: string; formAction?: string | ((formData: FormData) => void | Promise); formEncType?: string; formMethod?: string; formNoValidate?: boolean; formTarget?: string; hidden?: boolean; icon?: LucideIcon; id?: string; inert?: boolean; inlist?: any; inputMode?: \"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"; is?: string; itemID?: string; itemProp?: string; itemRef?: string; itemScope?: boolean; itemType?: string; lang?: string; name?: string; nonce?: string; onAbort?: import(\"react\").ReactEventHandler; onAbortCapture?: import(\"react\").ReactEventHandler; onAnimationEnd?: import(\"react\").AnimationEventHandler; onAnimationEndCapture?: import(\"react\").AnimationEventHandler; onAnimationIteration?: import(\"react\").AnimationEventHandler; onAnimationIterationCapture?: import(\"react\").AnimationEventHandler; onAnimationStart?: import(\"react\").AnimationEventHandler; onAnimationStartCapture?: import(\"react\").AnimationEventHandler; onAuxClick?: import(\"react\").MouseEventHandler; onAuxClickCapture?: import(\"react\").MouseEventHandler; onBeforeInput?: import(\"react\").InputEventHandler; onBeforeInputCapture?: import(\"react\").InputEventHandler; onBeforeToggle?: import(\"react\").ToggleEventHandler; onBlur?: import(\"react\").FocusEventHandler; onBlurCapture?: import(\"react\").FocusEventHandler; onCanPlay?: import(\"react\").ReactEventHandler; onCanPlayCapture?: import(\"react\").ReactEventHandler; onCanPlayThrough?: import(\"react\").ReactEventHandler; onCanPlayThroughCapture?: import(\"react\").ReactEventHandler; onChange?: import(\"react\").ChangeEventHandler; onChangeCapture?: import(\"react\").ChangeEventHandler; onClick?: import(\"react\").MouseEventHandler; onClickCapture?: import(\"react\").MouseEventHandler; onCompositionEnd?: import(\"react\").CompositionEventHandler; onCompositionEndCapture?: import(\"react\").CompositionEventHandler; onCompositionStart?: import(\"react\").CompositionEventHandler; onCompositionStartCapture?: import(\"react\").CompositionEventHandler; onCompositionUpdate?: import(\"react\").CompositionEventHandler; onCompositionUpdateCapture?: import(\"react\").CompositionEventHandler; onContextMenu?: import(\"react\").MouseEventHandler; onContextMenuCapture?: import(\"react\").MouseEventHandler; onCopy?: import(\"react\").ClipboardEventHandler; onCopyCapture?: import(\"react\").ClipboardEventHandler; onCut?: import(\"react\").ClipboardEventHandler; onCutCapture?: import(\"react\").ClipboardEventHandler; onDoubleClick?: import(\"react\").MouseEventHandler; onDoubleClickCapture?: import(\"react\").MouseEventHandler; onDrag?: import(\"react\").DragEventHandler; onDragCapture?: import(\"react\").DragEventHandler; onDragEnd?: import(\"react\").DragEventHandler; onDragEndCapture?: import(\"react\").DragEventHandler; onDragEnter?: import(\"react\").DragEventHandler; onDragEnterCapture?: import(\"react\").DragEventHandler; onDragExit?: import(\"react\").DragEventHandler; onDragExitCapture?: import(\"react\").DragEventHandler; onDragLeave?: import(\"react\").DragEventHandler; onDragLeaveCapture?: import(\"react\").DragEventHandler; onDragOver?: import(\"react\").DragEventHandler; onDragOverCapture?: import(\"react\").DragEventHandler; onDragStart?: import(\"react\").DragEventHandler; onDragStartCapture?: import(\"react\").DragEventHandler; onDrop?: import(\"react\").DragEventHandler; onDropCapture?: import(\"react\").DragEventHandler; onDurationChange?: import(\"react\").ReactEventHandler; onDurationChangeCapture?: import(\"react\").ReactEventHandler; onEmptied?: import(\"react\").ReactEventHandler; onEmptiedCapture?: import(\"react\").ReactEventHandler; onEncrypted?: import(\"react\").ReactEventHandler; onEncryptedCapture?: import(\"react\").ReactEventHandler; onEnded?: import(\"react\").ReactEventHandler; onEndedCapture?: import(\"react\").ReactEventHandler; onError?: import(\"react\").ReactEventHandler; onErrorCapture?: import(\"react\").ReactEventHandler; onFocus?: import(\"react\").FocusEventHandler; onFocusCapture?: import(\"react\").FocusEventHandler; onGotPointerCapture?: import(\"react\").PointerEventHandler; onGotPointerCaptureCapture?: import(\"react\").PointerEventHandler; onInput?: import(\"react\").InputEventHandler; onInputCapture?: import(\"react\").InputEventHandler; onInvalid?: import(\"react\").ReactEventHandler; onInvalidCapture?: import(\"react\").ReactEventHandler; onKeyDown?: import(\"react\").KeyboardEventHandler; onKeyDownCapture?: import(\"react\").KeyboardEventHandler; onKeyPress?: import(\"react\").KeyboardEventHandler; onKeyPressCapture?: import(\"react\").KeyboardEventHandler; onKeyUp?: import(\"react\").KeyboardEventHandler; onKeyUpCapture?: import(\"react\").KeyboardEventHandler; onLoad?: import(\"react\").ReactEventHandler; onLoadCapture?: import(\"react\").ReactEventHandler; onLoadedData?: import(\"react\").ReactEventHandler; onLoadedDataCapture?: import(\"react\").ReactEventHandler; onLoadedMetadata?: import(\"react\").ReactEventHandler; onLoadedMetadataCapture?: import(\"react\").ReactEventHandler; onLoadStart?: import(\"react\").ReactEventHandler; onLoadStartCapture?: import(\"react\").ReactEventHandler; onLostPointerCapture?: import(\"react\").PointerEventHandler; onLostPointerCaptureCapture?: import(\"react\").PointerEventHandler; onMouseDown?: import(\"react\").MouseEventHandler; onMouseDownCapture?: import(\"react\").MouseEventHandler; onMouseEnter?: import(\"react\").MouseEventHandler; onMouseLeave?: import(\"react\").MouseEventHandler; onMouseMove?: import(\"react\").MouseEventHandler; onMouseMoveCapture?: import(\"react\").MouseEventHandler; onMouseOut?: import(\"react\").MouseEventHandler; onMouseOutCapture?: import(\"react\").MouseEventHandler; onMouseOver?: import(\"react\").MouseEventHandler; onMouseOverCapture?: import(\"react\").MouseEventHandler; onMouseUp?: import(\"react\").MouseEventHandler; onMouseUpCapture?: import(\"react\").MouseEventHandler; onPaste?: import(\"react\").ClipboardEventHandler; onPasteCapture?: import(\"react\").ClipboardEventHandler; onPause?: import(\"react\").ReactEventHandler; onPauseCapture?: import(\"react\").ReactEventHandler; onPlay?: import(\"react\").ReactEventHandler; onPlayCapture?: import(\"react\").ReactEventHandler; onPlaying?: import(\"react\").ReactEventHandler; onPlayingCapture?: import(\"react\").ReactEventHandler; onPointerCancel?: import(\"react\").PointerEventHandler; onPointerCancelCapture?: import(\"react\").PointerEventHandler; onPointerDown?: import(\"react\").PointerEventHandler; onPointerDownCapture?: import(\"react\").PointerEventHandler; onPointerEnter?: import(\"react\").PointerEventHandler; onPointerLeave?: import(\"react\").PointerEventHandler; onPointerMove?: import(\"react\").PointerEventHandler; onPointerMoveCapture?: import(\"react\").PointerEventHandler; onPointerOut?: import(\"react\").PointerEventHandler; onPointerOutCapture?: import(\"react\").PointerEventHandler; onPointerOver?: import(\"react\").PointerEventHandler; onPointerOverCapture?: import(\"react\").PointerEventHandler; onPointerUp?: import(\"react\").PointerEventHandler; onPointerUpCapture?: import(\"react\").PointerEventHandler; onProgress?: import(\"react\").ReactEventHandler; onProgressCapture?: import(\"react\").ReactEventHandler; onRateChange?: import(\"react\").ReactEventHandler; onRateChangeCapture?: import(\"react\").ReactEventHandler; onReset?: import(\"react\").ReactEventHandler; onResetCapture?: import(\"react\").ReactEventHandler; onScroll?: import(\"react\").UIEventHandler; onScrollCapture?: import(\"react\").UIEventHandler; onScrollEnd?: import(\"react\").UIEventHandler; onScrollEndCapture?: import(\"react\").UIEventHandler; onSeeked?: import(\"react\").ReactEventHandler; onSeekedCapture?: import(\"react\").ReactEventHandler; onSeeking?: import(\"react\").ReactEventHandler; onSeekingCapture?: import(\"react\").ReactEventHandler; onSelect?: import(\"react\").ReactEventHandler; onSelectCapture?: import(\"react\").ReactEventHandler; onStalled?: import(\"react\").ReactEventHandler; onStalledCapture?: import(\"react\").ReactEventHandler; onSubmit?: import(\"react\").SubmitEventHandler; onSubmitCapture?: import(\"react\").SubmitEventHandler; onSuspend?: import(\"react\").ReactEventHandler; onSuspendCapture?: import(\"react\").ReactEventHandler; onTimeUpdate?: import(\"react\").ReactEventHandler; onTimeUpdateCapture?: import(\"react\").ReactEventHandler; onToggle?: import(\"react\").ToggleEventHandler; onTouchCancel?: import(\"react\").TouchEventHandler; onTouchCancelCapture?: import(\"react\").TouchEventHandler; onTouchEnd?: import(\"react\").TouchEventHandler; onTouchEndCapture?: import(\"react\").TouchEventHandler; onTouchMove?: import(\"react\").TouchEventHandler; onTouchMoveCapture?: import(\"react\").TouchEventHandler; onTouchStart?: import(\"react\").TouchEventHandler; onTouchStartCapture?: import(\"react\").TouchEventHandler; onTransitionCancel?: import(\"react\").TransitionEventHandler; onTransitionCancelCapture?: import(\"react\").TransitionEventHandler; onTransitionEnd?: import(\"react\").TransitionEventHandler; onTransitionEndCapture?: import(\"react\").TransitionEventHandler; onTransitionRun?: import(\"react\").TransitionEventHandler; onTransitionRunCapture?: import(\"react\").TransitionEventHandler; onTransitionStart?: import(\"react\").TransitionEventHandler; onTransitionStartCapture?: import(\"react\").TransitionEventHandler; onVolumeChange?: import(\"react\").ReactEventHandler; onVolumeChangeCapture?: import(\"react\").ReactEventHandler; onWaiting?: import(\"react\").ReactEventHandler; onWaitingCapture?: import(\"react\").ReactEventHandler; onWheel?: import(\"react\").WheelEventHandler; onWheelCapture?: import(\"react\").WheelEventHandler; part?: string; popover?: \"\" | \"auto\" | \"manual\" | \"hint\"; popoverTarget?: string; popoverTargetAction?: \"toggle\" | \"show\" | \"hide\"; prefix?: string; property?: string; radioGroup?: string; ref?: Ref; rel?: string; resource?: string; results?: number; rev?: string; role?: import(\"react\").AriaRole; security?: string; size?: ButtonSize; slot?: string; spellCheck?: (boolean | \"true\" | \"false\"); style?: import(\"react\").CSSProperties; suppressContentEditableWarning?: boolean; suppressHydrationWarning?: boolean; tabIndex?: number; testId?: string; title?: string; trailingIcon?: LucideIcon; translate?: \"yes\" | \"no\"; type?: \"submit\" | \"reset\" | \"button\"; typeof?: string; unselectable?: \"off\" | \"on\"; value?: string | number | readonly string[]; variant?: ButtonVariant; vocab?: string;", "Checkbox": "about?: string; accept?: string; accessKey?: string; alt?: string; \"aria-activedescendant\"?: string; \"aria-atomic\"?: (boolean | \"true\" | \"false\"); \"aria-autocomplete\"?: \"none\" | \"list\" | \"inline\" | \"both\"; \"aria-braillelabel\"?: string; \"aria-brailleroledescription\"?: string; \"aria-busy\"?: (boolean | \"true\" | \"false\"); \"aria-checked\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-colcount\"?: number; \"aria-colindex\"?: number; \"aria-colindextext\"?: string; \"aria-colspan\"?: number; \"aria-controls\"?: string; \"aria-current\"?: boolean | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\" | \"time\"; \"aria-describedby\"?: string; \"aria-description\"?: string; \"aria-details\"?: string; \"aria-disabled\"?: (boolean | \"true\" | \"false\"); \"aria-dropeffect\"?: \"none\" | \"link\" | \"copy\" | \"execute\" | \"move\" | \"popup\"; \"aria-errormessage\"?: string; \"aria-expanded\"?: (boolean | \"true\" | \"false\"); \"aria-flowto\"?: string; \"aria-grabbed\"?: (boolean | \"true\" | \"false\"); \"aria-haspopup\"?: boolean | \"true\" | \"false\" | \"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"; \"aria-hidden\"?: (boolean | \"true\" | \"false\"); \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"; \"aria-keyshortcuts\"?: string; \"aria-label\"?: string; \"aria-labelledby\"?: string; \"aria-level\"?: number; \"aria-live\"?: \"off\" | \"assertive\" | \"polite\"; \"aria-modal\"?: (boolean | \"true\" | \"false\"); \"aria-multiline\"?: (boolean | \"true\" | \"false\"); \"aria-multiselectable\"?: (boolean | \"true\" | \"false\"); \"aria-orientation\"?: \"horizontal\" | \"vertical\"; \"aria-owns\"?: string; \"aria-placeholder\"?: string; \"aria-posinset\"?: number; \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-readonly\"?: (boolean | \"true\" | \"false\"); \"aria-relevant\"?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"; \"aria-required\"?: (boolean | \"true\" | \"false\"); \"aria-roledescription\"?: string; \"aria-rowcount\"?: number; \"aria-rowindex\"?: number; \"aria-rowindextext\"?: string; \"aria-rowspan\"?: number; \"aria-selected\"?: (boolean | \"true\" | \"false\"); \"aria-setsize\"?: number; \"aria-sort\"?: \"none\" | \"ascending\" | \"descending\" | \"other\"; \"aria-valuemax\"?: number; \"aria-valuemin\"?: number; \"aria-valuenow\"?: number; \"aria-valuetext\"?: string; autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}); autoComplete?: import(\"react\").HTMLInputAutoCompleteAttribute; autoCorrect?: string; autoFocus?: boolean; autoSave?: string; capture?: boolean | \"user\" | \"environment\"; checked?: boolean; children?: ReactNode; className?: string; color?: string; content?: string; contentEditable?: (boolean | \"true\" | \"false\") | \"inherit\" | \"plaintext-only\"; contextMenu?: string; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; }; datatype?: string; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; description?: ReactNode; dir?: string; disabled?: boolean; draggable?: (boolean | \"true\" | \"false\"); enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\"; exportparts?: string; form?: string; formAction?: string | ((formData: FormData) => void | Promise); formEncType?: string; formMethod?: string; formNoValidate?: boolean; formTarget?: string; height?: string | number; hidden?: boolean; id?: string; indeterminate?: boolean; inert?: boolean; inlist?: any; inputMode?: \"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"; is?: string; itemID?: string; itemProp?: string; itemRef?: string; itemScope?: boolean; itemType?: string; label: ReactNode; lang?: string; list?: string; max?: string | number; maxLength?: number; min?: string | number; minLength?: number; multiple?: boolean; name?: string; nonce?: string; onAbort?: import(\"react\").ReactEventHandler; onAbortCapture?: import(\"react\").ReactEventHandler; onAnimationEnd?: import(\"react\").AnimationEventHandler; onAnimationEndCapture?: import(\"react\").AnimationEventHandler; onAnimationIteration?: import(\"react\").AnimationEventHandler; onAnimationIterationCapture?: import(\"react\").AnimationEventHandler; onAnimationStart?: import(\"react\").AnimationEventHandler; onAnimationStartCapture?: import(\"react\").AnimationEventHandler; onAuxClick?: import(\"react\").MouseEventHandler; onAuxClickCapture?: import(\"react\").MouseEventHandler; onBeforeInput?: import(\"react\").InputEventHandler; onBeforeInputCapture?: import(\"react\").InputEventHandler; onBeforeToggle?: import(\"react\").ToggleEventHandler; onBlur?: import(\"react\").FocusEventHandler; onBlurCapture?: import(\"react\").FocusEventHandler; onCanPlay?: import(\"react\").ReactEventHandler; onCanPlayCapture?: import(\"react\").ReactEventHandler; onCanPlayThrough?: import(\"react\").ReactEventHandler; onCanPlayThroughCapture?: import(\"react\").ReactEventHandler; onChange?: import(\"react\").ChangeEventHandler; onChangeCapture?: import(\"react\").ChangeEventHandler; onClick?: import(\"react\").MouseEventHandler; onClickCapture?: import(\"react\").MouseEventHandler; onCompositionEnd?: import(\"react\").CompositionEventHandler; onCompositionEndCapture?: import(\"react\").CompositionEventHandler; onCompositionStart?: import(\"react\").CompositionEventHandler; onCompositionStartCapture?: import(\"react\").CompositionEventHandler; onCompositionUpdate?: import(\"react\").CompositionEventHandler; onCompositionUpdateCapture?: import(\"react\").CompositionEventHandler; onContextMenu?: import(\"react\").MouseEventHandler; onContextMenuCapture?: import(\"react\").MouseEventHandler; onCopy?: import(\"react\").ClipboardEventHandler; onCopyCapture?: import(\"react\").ClipboardEventHandler; onCut?: import(\"react\").ClipboardEventHandler; onCutCapture?: import(\"react\").ClipboardEventHandler; onDoubleClick?: import(\"react\").MouseEventHandler; onDoubleClickCapture?: import(\"react\").MouseEventHandler; onDrag?: import(\"react\").DragEventHandler; onDragCapture?: import(\"react\").DragEventHandler; onDragEnd?: import(\"react\").DragEventHandler; onDragEndCapture?: import(\"react\").DragEventHandler; onDragEnter?: import(\"react\").DragEventHandler; onDragEnterCapture?: import(\"react\").DragEventHandler; onDragExit?: import(\"react\").DragEventHandler; onDragExitCapture?: import(\"react\").DragEventHandler; onDragLeave?: import(\"react\").DragEventHandler; onDragLeaveCapture?: import(\"react\").DragEventHandler; onDragOver?: import(\"react\").DragEventHandler; onDragOverCapture?: import(\"react\").DragEventHandler; onDragStart?: import(\"react\").DragEventHandler; onDragStartCapture?: import(\"react\").DragEventHandler; onDrop?: import(\"react\").DragEventHandler; onDropCapture?: import(\"react\").DragEventHandler; onDurationChange?: import(\"react\").ReactEventHandler; onDurationChangeCapture?: import(\"react\").ReactEventHandler; onEmptied?: import(\"react\").ReactEventHandler; onEmptiedCapture?: import(\"react\").ReactEventHandler; onEncrypted?: import(\"react\").ReactEventHandler; onEncryptedCapture?: import(\"react\").ReactEventHandler; onEnded?: import(\"react\").ReactEventHandler; onEndedCapture?: import(\"react\").ReactEventHandler; onError?: import(\"react\").ReactEventHandler; onErrorCapture?: import(\"react\").ReactEventHandler; onFocus?: import(\"react\").FocusEventHandler; onFocusCapture?: import(\"react\").FocusEventHandler; onGotPointerCapture?: import(\"react\").PointerEventHandler; onGotPointerCaptureCapture?: import(\"react\").PointerEventHandler; onInput?: import(\"react\").InputEventHandler; onInputCapture?: import(\"react\").InputEventHandler; onInvalid?: import(\"react\").ReactEventHandler; onInvalidCapture?: import(\"react\").ReactEventHandler; onKeyDown?: import(\"react\").KeyboardEventHandler; onKeyDownCapture?: import(\"react\").KeyboardEventHandler; onKeyPress?: import(\"react\").KeyboardEventHandler; onKeyPressCapture?: import(\"react\").KeyboardEventHandler; onKeyUp?: import(\"react\").KeyboardEventHandler; onKeyUpCapture?: import(\"react\").KeyboardEventHandler; onLoad?: import(\"react\").ReactEventHandler; onLoadCapture?: import(\"react\").ReactEventHandler; onLoadedData?: import(\"react\").ReactEventHandler; onLoadedDataCapture?: import(\"react\").ReactEventHandler; onLoadedMetadata?: import(\"react\").ReactEventHandler; onLoadedMetadataCapture?: import(\"react\").ReactEventHandler; onLoadStart?: import(\"react\").ReactEventHandler; onLoadStartCapture?: import(\"react\").ReactEventHandler; onLostPointerCapture?: import(\"react\").PointerEventHandler; onLostPointerCaptureCapture?: import(\"react\").PointerEventHandler; onMouseDown?: import(\"react\").MouseEventHandler; onMouseDownCapture?: import(\"react\").MouseEventHandler; onMouseEnter?: import(\"react\").MouseEventHandler; onMouseLeave?: import(\"react\").MouseEventHandler; onMouseMove?: import(\"react\").MouseEventHandler; onMouseMoveCapture?: import(\"react\").MouseEventHandler; onMouseOut?: import(\"react\").MouseEventHandler; onMouseOutCapture?: import(\"react\").MouseEventHandler; onMouseOver?: import(\"react\").MouseEventHandler; onMouseOverCapture?: import(\"react\").MouseEventHandler; onMouseUp?: import(\"react\").MouseEventHandler; onMouseUpCapture?: import(\"react\").MouseEventHandler; onPaste?: import(\"react\").ClipboardEventHandler; onPasteCapture?: import(\"react\").ClipboardEventHandler; onPause?: import(\"react\").ReactEventHandler; onPauseCapture?: import(\"react\").ReactEventHandler; onPlay?: import(\"react\").ReactEventHandler; onPlayCapture?: import(\"react\").ReactEventHandler; onPlaying?: import(\"react\").ReactEventHandler; onPlayingCapture?: import(\"react\").ReactEventHandler; onPointerCancel?: import(\"react\").PointerEventHandler; onPointerCancelCapture?: import(\"react\").PointerEventHandler; onPointerDown?: import(\"react\").PointerEventHandler; onPointerDownCapture?: import(\"react\").PointerEventHandler; onPointerEnter?: import(\"react\").PointerEventHandler; onPointerLeave?: import(\"react\").PointerEventHandler; onPointerMove?: import(\"react\").PointerEventHandler; onPointerMoveCapture?: import(\"react\").PointerEventHandler; onPointerOut?: import(\"react\").PointerEventHandler; onPointerOutCapture?: import(\"react\").PointerEventHandler; onPointerOver?: import(\"react\").PointerEventHandler; onPointerOverCapture?: import(\"react\").PointerEventHandler; onPointerUp?: import(\"react\").PointerEventHandler; onPointerUpCapture?: import(\"react\").PointerEventHandler; onProgress?: import(\"react\").ReactEventHandler; onProgressCapture?: import(\"react\").ReactEventHandler; onRateChange?: import(\"react\").ReactEventHandler; onRateChangeCapture?: import(\"react\").ReactEventHandler; onReset?: import(\"react\").ReactEventHandler; onResetCapture?: import(\"react\").ReactEventHandler; onScroll?: import(\"react\").UIEventHandler; onScrollCapture?: import(\"react\").UIEventHandler; onScrollEnd?: import(\"react\").UIEventHandler; onScrollEndCapture?: import(\"react\").UIEventHandler; onSeeked?: import(\"react\").ReactEventHandler; onSeekedCapture?: import(\"react\").ReactEventHandler; onSeeking?: import(\"react\").ReactEventHandler; onSeekingCapture?: import(\"react\").ReactEventHandler; onSelect?: import(\"react\").ReactEventHandler; onSelectCapture?: import(\"react\").ReactEventHandler; onStalled?: import(\"react\").ReactEventHandler; onStalledCapture?: import(\"react\").ReactEventHandler; onSubmit?: import(\"react\").SubmitEventHandler; onSubmitCapture?: import(\"react\").SubmitEventHandler; onSuspend?: import(\"react\").ReactEventHandler; onSuspendCapture?: import(\"react\").ReactEventHandler; onTimeUpdate?: import(\"react\").ReactEventHandler; onTimeUpdateCapture?: import(\"react\").ReactEventHandler; onToggle?: import(\"react\").ToggleEventHandler; onTouchCancel?: import(\"react\").TouchEventHandler; onTouchCancelCapture?: import(\"react\").TouchEventHandler; onTouchEnd?: import(\"react\").TouchEventHandler; onTouchEndCapture?: import(\"react\").TouchEventHandler; onTouchMove?: import(\"react\").TouchEventHandler; onTouchMoveCapture?: import(\"react\").TouchEventHandler; onTouchStart?: import(\"react\").TouchEventHandler; onTouchStartCapture?: import(\"react\").TouchEventHandler; onTransitionCancel?: import(\"react\").TransitionEventHandler; onTransitionCancelCapture?: import(\"react\").TransitionEventHandler; onTransitionEnd?: import(\"react\").TransitionEventHandler; onTransitionEndCapture?: import(\"react\").TransitionEventHandler; onTransitionRun?: import(\"react\").TransitionEventHandler; onTransitionRunCapture?: import(\"react\").TransitionEventHandler; onTransitionStart?: import(\"react\").TransitionEventHandler; onTransitionStartCapture?: import(\"react\").TransitionEventHandler; onVolumeChange?: import(\"react\").ReactEventHandler; onVolumeChangeCapture?: import(\"react\").ReactEventHandler; onWaiting?: import(\"react\").ReactEventHandler; onWaitingCapture?: import(\"react\").ReactEventHandler; onWheel?: import(\"react\").WheelEventHandler; onWheelCapture?: import(\"react\").WheelEventHandler; part?: string; pattern?: string; placeholder?: string; popover?: \"\" | \"auto\" | \"manual\" | \"hint\"; popoverTarget?: string; popoverTargetAction?: \"toggle\" | \"show\" | \"hide\"; prefix?: string; property?: string; radioGroup?: string; readOnly?: boolean; ref?: Ref; rel?: string; required?: boolean; resource?: string; results?: number; rev?: string; role?: import(\"react\").AriaRole; security?: string; size?: number; slot?: string; spellCheck?: (boolean | \"true\" | \"false\"); src?: string; step?: string | number; style?: import(\"react\").CSSProperties; suppressContentEditableWarning?: boolean; suppressHydrationWarning?: boolean; tabIndex?: number; title?: string; translate?: \"yes\" | \"no\"; typeof?: string; unselectable?: \"off\" | \"on\"; value?: string | number | readonly string[]; vocab?: string; width?: string | number;", "Chip": "appearance?: ChipAppearance; children: ReactNode; className?: string; dot?: boolean; icon?: LucideIcon; onRemove?: (() => void); removeLabel?: string; size?: ChipSize; title?: string; wrap?: boolean;", "Citation": "className?: string; index: number; interactive?: boolean; label: string; locator?: string; onActivate?: (() => void); status?: DocumentStatus;", @@ -34,7 +34,7 @@ "Disclosure": "children: ReactNode; className?: string; defaultOpen?: boolean; description?: ReactNode; headingLevel?: 2 | 3 | 4 | 5 | 6; meta?: ReactNode; onOpenChange?: ((open: boolean) => void); open?: boolean; title: ReactNode;", "DisclosureGroup": "className?: string; exclusive?: boolean; headingLevel?: 2 | 3 | 4 | 5 | 6; items: { id: string; title: ReactNode; description?: ReactNode; meta?: ReactNode; content: ReactNode; }[];", "DoseLine": "caption?: string; className?: string; onOpenSource: (sourceId: string, locator?: string) => void; rows: readonly DoseRow[];", - "DownloadLink": "about?: string; accessKey?: string; \"aria-activedescendant\"?: string; \"aria-atomic\"?: (boolean | \"true\" | \"false\"); \"aria-autocomplete\"?: \"none\" | \"list\" | \"inline\" | \"both\"; \"aria-braillelabel\"?: string; \"aria-brailleroledescription\"?: string; \"aria-busy\"?: (boolean | \"true\" | \"false\"); \"aria-checked\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-colcount\"?: number; \"aria-colindex\"?: number; \"aria-colindextext\"?: string; \"aria-colspan\"?: number; \"aria-controls\"?: string; \"aria-current\"?: boolean | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\" | \"time\"; \"aria-describedby\"?: string; \"aria-description\"?: string; \"aria-details\"?: string; \"aria-disabled\"?: (boolean | \"true\" | \"false\"); \"aria-dropeffect\"?: \"none\" | \"link\" | \"copy\" | \"execute\" | \"move\" | \"popup\"; \"aria-errormessage\"?: string; \"aria-expanded\"?: (boolean | \"true\" | \"false\"); \"aria-flowto\"?: string; \"aria-grabbed\"?: (boolean | \"true\" | \"false\"); \"aria-haspopup\"?: boolean | \"true\" | \"false\" | \"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"; \"aria-hidden\"?: (boolean | \"true\" | \"false\"); \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"; \"aria-keyshortcuts\"?: string; \"aria-label\"?: string; \"aria-labelledby\"?: string; \"aria-level\"?: number; \"aria-live\"?: \"off\" | \"assertive\" | \"polite\"; \"aria-modal\"?: (boolean | \"true\" | \"false\"); \"aria-multiline\"?: (boolean | \"true\" | \"false\"); \"aria-multiselectable\"?: (boolean | \"true\" | \"false\"); \"aria-orientation\"?: \"horizontal\" | \"vertical\"; \"aria-owns\"?: string; \"aria-placeholder\"?: string; \"aria-posinset\"?: number; \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-readonly\"?: (boolean | \"true\" | \"false\"); \"aria-relevant\"?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"; \"aria-required\"?: (boolean | \"true\" | \"false\"); \"aria-roledescription\"?: string; \"aria-rowcount\"?: number; \"aria-rowindex\"?: number; \"aria-rowindextext\"?: string; \"aria-rowspan\"?: number; \"aria-selected\"?: (boolean | \"true\" | \"false\"); \"aria-setsize\"?: number; \"aria-sort\"?: \"none\" | \"ascending\" | \"descending\" | \"other\"; \"aria-valuemax\"?: number; \"aria-valuemin\"?: number; \"aria-valuenow\"?: number; \"aria-valuetext\"?: string; autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}); autoCorrect?: string; autoFocus?: boolean; autoSave?: string; children: ReactNode; className?: string; color?: string; content?: string; contentEditable?: (boolean | \"true\" | \"false\") | \"inherit\" | \"plaintext-only\"; contextMenu?: string; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; }; datatype?: string; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; dir?: string; download?: any; draggable?: (boolean | \"true\" | \"false\"); enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\"; exportparts?: string; format?: string; hidden?: boolean; href: string; hrefLang?: string; id?: string; inert?: boolean; inlist?: any; inputMode?: \"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"; is?: string; itemID?: string; itemProp?: string; itemRef?: string; itemScope?: boolean; itemType?: string; lang?: string; media?: string; nonce?: string; onAbort?: import(\"react\").ReactEventHandler; onAbortCapture?: import(\"react\").ReactEventHandler; onAnimationEnd?: import(\"react\").AnimationEventHandler; onAnimationEndCapture?: import(\"react\").AnimationEventHandler; onAnimationIteration?: import(\"react\").AnimationEventHandler; onAnimationIterationCapture?: import(\"react\").AnimationEventHandler; onAnimationStart?: import(\"react\").AnimationEventHandler; onAnimationStartCapture?: import(\"react\").AnimationEventHandler; onAuxClick?: import(\"react\").MouseEventHandler; onAuxClickCapture?: import(\"react\").MouseEventHandler; onBeforeInput?: import(\"react\").InputEventHandler; onBeforeInputCapture?: import(\"react\").InputEventHandler; onBeforeToggle?: import(\"react\").ToggleEventHandler; onBlur?: import(\"react\").FocusEventHandler; onBlurCapture?: import(\"react\").FocusEventHandler; onCanPlay?: import(\"react\").ReactEventHandler; onCanPlayCapture?: import(\"react\").ReactEventHandler; onCanPlayThrough?: import(\"react\").ReactEventHandler; onCanPlayThroughCapture?: import(\"react\").ReactEventHandler; onChange?: import(\"react\").ChangeEventHandler; onChangeCapture?: import(\"react\").ChangeEventHandler; onClick?: import(\"react\").MouseEventHandler; onClickCapture?: import(\"react\").MouseEventHandler; onCompositionEnd?: import(\"react\").CompositionEventHandler; onCompositionEndCapture?: import(\"react\").CompositionEventHandler; onCompositionStart?: import(\"react\").CompositionEventHandler; onCompositionStartCapture?: import(\"react\").CompositionEventHandler; onCompositionUpdate?: import(\"react\").CompositionEventHandler; onCompositionUpdateCapture?: import(\"react\").CompositionEventHandler; onContextMenu?: import(\"react\").MouseEventHandler; onContextMenuCapture?: import(\"react\").MouseEventHandler; onCopy?: import(\"react\").ClipboardEventHandler; onCopyCapture?: import(\"react\").ClipboardEventHandler; onCut?: import(\"react\").ClipboardEventHandler; onCutCapture?: import(\"react\").ClipboardEventHandler; onDoubleClick?: import(\"react\").MouseEventHandler; onDoubleClickCapture?: import(\"react\").MouseEventHandler; onDrag?: import(\"react\").DragEventHandler; onDragCapture?: import(\"react\").DragEventHandler; onDragEnd?: import(\"react\").DragEventHandler; onDragEndCapture?: import(\"react\").DragEventHandler; onDragEnter?: import(\"react\").DragEventHandler; onDragEnterCapture?: import(\"react\").DragEventHandler; onDragExit?: import(\"react\").DragEventHandler; onDragExitCapture?: import(\"react\").DragEventHandler; onDragLeave?: import(\"react\").DragEventHandler; onDragLeaveCapture?: import(\"react\").DragEventHandler; onDragOver?: import(\"react\").DragEventHandler; onDragOverCapture?: import(\"react\").DragEventHandler; onDragStart?: import(\"react\").DragEventHandler; onDragStartCapture?: import(\"react\").DragEventHandler; onDrop?: import(\"react\").DragEventHandler; onDropCapture?: import(\"react\").DragEventHandler; onDurationChange?: import(\"react\").ReactEventHandler; onDurationChangeCapture?: import(\"react\").ReactEventHandler; onEmptied?: import(\"react\").ReactEventHandler; onEmptiedCapture?: import(\"react\").ReactEventHandler; onEncrypted?: import(\"react\").ReactEventHandler; onEncryptedCapture?: import(\"react\").ReactEventHandler; onEnded?: import(\"react\").ReactEventHandler; onEndedCapture?: import(\"react\").ReactEventHandler; onError?: import(\"react\").ReactEventHandler; onErrorCapture?: import(\"react\").ReactEventHandler; onFocus?: import(\"react\").FocusEventHandler; onFocusCapture?: import(\"react\").FocusEventHandler; onGotPointerCapture?: import(\"react\").PointerEventHandler; onGotPointerCaptureCapture?: import(\"react\").PointerEventHandler; onInput?: import(\"react\").InputEventHandler; onInputCapture?: import(\"react\").InputEventHandler; onInvalid?: import(\"react\").ReactEventHandler; onInvalidCapture?: import(\"react\").ReactEventHandler; onKeyDown?: import(\"react\").KeyboardEventHandler; onKeyDownCapture?: import(\"react\").KeyboardEventHandler; onKeyPress?: import(\"react\").KeyboardEventHandler; onKeyPressCapture?: import(\"react\").KeyboardEventHandler; onKeyUp?: import(\"react\").KeyboardEventHandler; onKeyUpCapture?: import(\"react\").KeyboardEventHandler; onLoad?: import(\"react\").ReactEventHandler; onLoadCapture?: import(\"react\").ReactEventHandler; onLoadedData?: import(\"react\").ReactEventHandler; onLoadedDataCapture?: import(\"react\").ReactEventHandler; onLoadedMetadata?: import(\"react\").ReactEventHandler; onLoadedMetadataCapture?: import(\"react\").ReactEventHandler; onLoadStart?: import(\"react\").ReactEventHandler; onLoadStartCapture?: import(\"react\").ReactEventHandler; onLostPointerCapture?: import(\"react\").PointerEventHandler; onLostPointerCaptureCapture?: import(\"react\").PointerEventHandler; onMouseDown?: import(\"react\").MouseEventHandler; onMouseDownCapture?: import(\"react\").MouseEventHandler; onMouseEnter?: import(\"react\").MouseEventHandler; onMouseLeave?: import(\"react\").MouseEventHandler; onMouseMove?: import(\"react\").MouseEventHandler; onMouseMoveCapture?: import(\"react\").MouseEventHandler; onMouseOut?: import(\"react\").MouseEventHandler; onMouseOutCapture?: import(\"react\").MouseEventHandler; onMouseOver?: import(\"react\").MouseEventHandler; onMouseOverCapture?: import(\"react\").MouseEventHandler; onMouseUp?: import(\"react\").MouseEventHandler; onMouseUpCapture?: import(\"react\").MouseEventHandler; onPaste?: import(\"react\").ClipboardEventHandler; onPasteCapture?: import(\"react\").ClipboardEventHandler; onPause?: import(\"react\").ReactEventHandler; onPauseCapture?: import(\"react\").ReactEventHandler; onPlay?: import(\"react\").ReactEventHandler; onPlayCapture?: import(\"react\").ReactEventHandler; onPlaying?: import(\"react\").ReactEventHandler; onPlayingCapture?: import(\"react\").ReactEventHandler; onPointerCancel?: import(\"react\").PointerEventHandler; onPointerCancelCapture?: import(\"react\").PointerEventHandler; onPointerDown?: import(\"react\").PointerEventHandler; onPointerDownCapture?: import(\"react\").PointerEventHandler; onPointerEnter?: import(\"react\").PointerEventHandler; onPointerLeave?: import(\"react\").PointerEventHandler; onPointerMove?: import(\"react\").PointerEventHandler; onPointerMoveCapture?: import(\"react\").PointerEventHandler; onPointerOut?: import(\"react\").PointerEventHandler; onPointerOutCapture?: import(\"react\").PointerEventHandler; onPointerOver?: import(\"react\").PointerEventHandler; onPointerOverCapture?: import(\"react\").PointerEventHandler; onPointerUp?: import(\"react\").PointerEventHandler; onPointerUpCapture?: import(\"react\").PointerEventHandler; onProgress?: import(\"react\").ReactEventHandler; onProgressCapture?: import(\"react\").ReactEventHandler; onRateChange?: import(\"react\").ReactEventHandler; onRateChangeCapture?: import(\"react\").ReactEventHandler; onReset?: import(\"react\").ReactEventHandler; onResetCapture?: import(\"react\").ReactEventHandler; onScroll?: import(\"react\").UIEventHandler; onScrollCapture?: import(\"react\").UIEventHandler; onScrollEnd?: import(\"react\").UIEventHandler; onScrollEndCapture?: import(\"react\").UIEventHandler; onSeeked?: import(\"react\").ReactEventHandler; onSeekedCapture?: import(\"react\").ReactEventHandler; onSeeking?: import(\"react\").ReactEventHandler; onSeekingCapture?: import(\"react\").ReactEventHandler; onSelect?: import(\"react\").ReactEventHandler; onSelectCapture?: import(\"react\").ReactEventHandler; onStalled?: import(\"react\").ReactEventHandler; onStalledCapture?: import(\"react\").ReactEventHandler; onSubmit?: import(\"react\").SubmitEventHandler; onSubmitCapture?: import(\"react\").SubmitEventHandler; onSuspend?: import(\"react\").ReactEventHandler; onSuspendCapture?: import(\"react\").ReactEventHandler; onTimeUpdate?: import(\"react\").ReactEventHandler; onTimeUpdateCapture?: import(\"react\").ReactEventHandler; onToggle?: import(\"react\").ToggleEventHandler; onTouchCancel?: import(\"react\").TouchEventHandler; onTouchCancelCapture?: import(\"react\").TouchEventHandler; onTouchEnd?: import(\"react\").TouchEventHandler; onTouchEndCapture?: import(\"react\").TouchEventHandler; onTouchMove?: import(\"react\").TouchEventHandler; onTouchMoveCapture?: import(\"react\").TouchEventHandler; onTouchStart?: import(\"react\").TouchEventHandler; onTouchStartCapture?: import(\"react\").TouchEventHandler; onTransitionCancel?: import(\"react\").TransitionEventHandler; onTransitionCancelCapture?: import(\"react\").TransitionEventHandler; onTransitionEnd?: import(\"react\").TransitionEventHandler; onTransitionEndCapture?: import(\"react\").TransitionEventHandler; onTransitionRun?: import(\"react\").TransitionEventHandler; onTransitionRunCapture?: import(\"react\").TransitionEventHandler; onTransitionStart?: import(\"react\").TransitionEventHandler; onTransitionStartCapture?: import(\"react\").TransitionEventHandler; onVolumeChange?: import(\"react\").ReactEventHandler; onVolumeChangeCapture?: import(\"react\").ReactEventHandler; onWaiting?: import(\"react\").ReactEventHandler; onWaitingCapture?: import(\"react\").ReactEventHandler; onWheel?: import(\"react\").WheelEventHandler; onWheelCapture?: import(\"react\").WheelEventHandler; part?: string; ping?: string; popover?: \"\" | \"auto\" | \"manual\" | \"hint\"; popoverTarget?: string; popoverTargetAction?: \"toggle\" | \"show\" | \"hide\"; prefix?: string; property?: string; radioGroup?: string; referrerPolicy?: import(\"react\").HTMLAttributeReferrerPolicy; rel?: string; resource?: string; results?: number; rev?: string; role?: import(\"react\").AriaRole; security?: string; size?: string; slot?: string; spellCheck?: (boolean | \"true\" | \"false\"); style?: import(\"react\").CSSProperties; suppressContentEditableWarning?: boolean; suppressHydrationWarning?: boolean; tabIndex?: number; target?: import(\"react\").HTMLAttributeAnchorTarget; title?: string; tone?: \"inherit\" | \"accent\"; translate?: \"yes\" | \"no\"; type?: string; typeof?: string; unselectable?: \"off\" | \"on\"; vocab?: string;", + "DownloadLink": "about?: string; accessKey?: string; \"aria-activedescendant\"?: string; \"aria-atomic\"?: (boolean | \"true\" | \"false\"); \"aria-autocomplete\"?: \"none\" | \"list\" | \"inline\" | \"both\"; \"aria-braillelabel\"?: string; \"aria-brailleroledescription\"?: string; \"aria-busy\"?: (boolean | \"true\" | \"false\"); \"aria-checked\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-colcount\"?: number; \"aria-colindex\"?: number; \"aria-colindextext\"?: string; \"aria-colspan\"?: number; \"aria-controls\"?: string; \"aria-current\"?: boolean | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\" | \"time\"; \"aria-describedby\"?: string; \"aria-description\"?: string; \"aria-details\"?: string; \"aria-disabled\"?: (boolean | \"true\" | \"false\"); \"aria-dropeffect\"?: \"none\" | \"link\" | \"copy\" | \"execute\" | \"move\" | \"popup\"; \"aria-errormessage\"?: string; \"aria-expanded\"?: (boolean | \"true\" | \"false\"); \"aria-flowto\"?: string; \"aria-grabbed\"?: (boolean | \"true\" | \"false\"); \"aria-haspopup\"?: boolean | \"true\" | \"false\" | \"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"; \"aria-hidden\"?: (boolean | \"true\" | \"false\"); \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"; \"aria-keyshortcuts\"?: string; \"aria-label\"?: string; \"aria-labelledby\"?: string; \"aria-level\"?: number; \"aria-live\"?: \"off\" | \"assertive\" | \"polite\"; \"aria-modal\"?: (boolean | \"true\" | \"false\"); \"aria-multiline\"?: (boolean | \"true\" | \"false\"); \"aria-multiselectable\"?: (boolean | \"true\" | \"false\"); \"aria-orientation\"?: \"horizontal\" | \"vertical\"; \"aria-owns\"?: string; \"aria-placeholder\"?: string; \"aria-posinset\"?: number; \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-readonly\"?: (boolean | \"true\" | \"false\"); \"aria-relevant\"?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"; \"aria-required\"?: (boolean | \"true\" | \"false\"); \"aria-roledescription\"?: string; \"aria-rowcount\"?: number; \"aria-rowindex\"?: number; \"aria-rowindextext\"?: string; \"aria-rowspan\"?: number; \"aria-selected\"?: (boolean | \"true\" | \"false\"); \"aria-setsize\"?: number; \"aria-sort\"?: \"none\" | \"ascending\" | \"descending\" | \"other\"; \"aria-valuemax\"?: number; \"aria-valuemin\"?: number; \"aria-valuenow\"?: number; \"aria-valuetext\"?: string; autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}); autoCorrect?: string; autoFocus?: boolean; autoSave?: string; children: ReactNode; className?: string; color?: string; content?: string; contentEditable?: (boolean | \"true\" | \"false\") | \"inherit\" | \"plaintext-only\"; contextMenu?: string; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; }; datatype?: string; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; dir?: string; draggable?: (boolean | \"true\" | \"false\"); enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\"; exportparts?: string; format?: string; hidden?: boolean; href: string; hrefLang?: string; id?: string; inert?: boolean; inlist?: any; inputMode?: \"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"; is?: string; itemID?: string; itemProp?: string; itemRef?: string; itemScope?: boolean; itemType?: string; lang?: string; media?: string; nonce?: string; onAbort?: import(\"react\").ReactEventHandler; onAbortCapture?: import(\"react\").ReactEventHandler; onAnimationEnd?: import(\"react\").AnimationEventHandler; onAnimationEndCapture?: import(\"react\").AnimationEventHandler; onAnimationIteration?: import(\"react\").AnimationEventHandler; onAnimationIterationCapture?: import(\"react\").AnimationEventHandler; onAnimationStart?: import(\"react\").AnimationEventHandler; onAnimationStartCapture?: import(\"react\").AnimationEventHandler; onAuxClick?: import(\"react\").MouseEventHandler; onAuxClickCapture?: import(\"react\").MouseEventHandler; onBeforeInput?: import(\"react\").InputEventHandler; onBeforeInputCapture?: import(\"react\").InputEventHandler; onBeforeToggle?: import(\"react\").ToggleEventHandler; onBlur?: import(\"react\").FocusEventHandler; onBlurCapture?: import(\"react\").FocusEventHandler; onCanPlay?: import(\"react\").ReactEventHandler; onCanPlayCapture?: import(\"react\").ReactEventHandler; onCanPlayThrough?: import(\"react\").ReactEventHandler; onCanPlayThroughCapture?: import(\"react\").ReactEventHandler; onChange?: import(\"react\").ChangeEventHandler; onChangeCapture?: import(\"react\").ChangeEventHandler; onClick?: import(\"react\").MouseEventHandler; onClickCapture?: import(\"react\").MouseEventHandler; onCompositionEnd?: import(\"react\").CompositionEventHandler; onCompositionEndCapture?: import(\"react\").CompositionEventHandler; onCompositionStart?: import(\"react\").CompositionEventHandler; onCompositionStartCapture?: import(\"react\").CompositionEventHandler; onCompositionUpdate?: import(\"react\").CompositionEventHandler; onCompositionUpdateCapture?: import(\"react\").CompositionEventHandler; onContextMenu?: import(\"react\").MouseEventHandler; onContextMenuCapture?: import(\"react\").MouseEventHandler; onCopy?: import(\"react\").ClipboardEventHandler; onCopyCapture?: import(\"react\").ClipboardEventHandler; onCut?: import(\"react\").ClipboardEventHandler; onCutCapture?: import(\"react\").ClipboardEventHandler; onDoubleClick?: import(\"react\").MouseEventHandler; onDoubleClickCapture?: import(\"react\").MouseEventHandler; onDrag?: import(\"react\").DragEventHandler; onDragCapture?: import(\"react\").DragEventHandler; onDragEnd?: import(\"react\").DragEventHandler; onDragEndCapture?: import(\"react\").DragEventHandler; onDragEnter?: import(\"react\").DragEventHandler; onDragEnterCapture?: import(\"react\").DragEventHandler; onDragExit?: import(\"react\").DragEventHandler; onDragExitCapture?: import(\"react\").DragEventHandler; onDragLeave?: import(\"react\").DragEventHandler; onDragLeaveCapture?: import(\"react\").DragEventHandler; onDragOver?: import(\"react\").DragEventHandler; onDragOverCapture?: import(\"react\").DragEventHandler; onDragStart?: import(\"react\").DragEventHandler; onDragStartCapture?: import(\"react\").DragEventHandler; onDrop?: import(\"react\").DragEventHandler; onDropCapture?: import(\"react\").DragEventHandler; onDurationChange?: import(\"react\").ReactEventHandler; onDurationChangeCapture?: import(\"react\").ReactEventHandler; onEmptied?: import(\"react\").ReactEventHandler; onEmptiedCapture?: import(\"react\").ReactEventHandler; onEncrypted?: import(\"react\").ReactEventHandler; onEncryptedCapture?: import(\"react\").ReactEventHandler; onEnded?: import(\"react\").ReactEventHandler; onEndedCapture?: import(\"react\").ReactEventHandler; onError?: import(\"react\").ReactEventHandler; onErrorCapture?: import(\"react\").ReactEventHandler; onFocus?: import(\"react\").FocusEventHandler; onFocusCapture?: import(\"react\").FocusEventHandler; onGotPointerCapture?: import(\"react\").PointerEventHandler; onGotPointerCaptureCapture?: import(\"react\").PointerEventHandler; onInput?: import(\"react\").InputEventHandler; onInputCapture?: import(\"react\").InputEventHandler; onInvalid?: import(\"react\").ReactEventHandler; onInvalidCapture?: import(\"react\").ReactEventHandler; onKeyDown?: import(\"react\").KeyboardEventHandler; onKeyDownCapture?: import(\"react\").KeyboardEventHandler; onKeyPress?: import(\"react\").KeyboardEventHandler; onKeyPressCapture?: import(\"react\").KeyboardEventHandler; onKeyUp?: import(\"react\").KeyboardEventHandler; onKeyUpCapture?: import(\"react\").KeyboardEventHandler; onLoad?: import(\"react\").ReactEventHandler; onLoadCapture?: import(\"react\").ReactEventHandler; onLoadedData?: import(\"react\").ReactEventHandler; onLoadedDataCapture?: import(\"react\").ReactEventHandler; onLoadedMetadata?: import(\"react\").ReactEventHandler; onLoadedMetadataCapture?: import(\"react\").ReactEventHandler; onLoadStart?: import(\"react\").ReactEventHandler; onLoadStartCapture?: import(\"react\").ReactEventHandler; onLostPointerCapture?: import(\"react\").PointerEventHandler; onLostPointerCaptureCapture?: import(\"react\").PointerEventHandler; onMouseDown?: import(\"react\").MouseEventHandler; onMouseDownCapture?: import(\"react\").MouseEventHandler; onMouseEnter?: import(\"react\").MouseEventHandler; onMouseLeave?: import(\"react\").MouseEventHandler; onMouseMove?: import(\"react\").MouseEventHandler; onMouseMoveCapture?: import(\"react\").MouseEventHandler; onMouseOut?: import(\"react\").MouseEventHandler; onMouseOutCapture?: import(\"react\").MouseEventHandler; onMouseOver?: import(\"react\").MouseEventHandler; onMouseOverCapture?: import(\"react\").MouseEventHandler; onMouseUp?: import(\"react\").MouseEventHandler; onMouseUpCapture?: import(\"react\").MouseEventHandler; onPaste?: import(\"react\").ClipboardEventHandler; onPasteCapture?: import(\"react\").ClipboardEventHandler; onPause?: import(\"react\").ReactEventHandler; onPauseCapture?: import(\"react\").ReactEventHandler; onPlay?: import(\"react\").ReactEventHandler; onPlayCapture?: import(\"react\").ReactEventHandler; onPlaying?: import(\"react\").ReactEventHandler; onPlayingCapture?: import(\"react\").ReactEventHandler; onPointerCancel?: import(\"react\").PointerEventHandler; onPointerCancelCapture?: import(\"react\").PointerEventHandler; onPointerDown?: import(\"react\").PointerEventHandler; onPointerDownCapture?: import(\"react\").PointerEventHandler; onPointerEnter?: import(\"react\").PointerEventHandler; onPointerLeave?: import(\"react\").PointerEventHandler; onPointerMove?: import(\"react\").PointerEventHandler; onPointerMoveCapture?: import(\"react\").PointerEventHandler; onPointerOut?: import(\"react\").PointerEventHandler; onPointerOutCapture?: import(\"react\").PointerEventHandler; onPointerOver?: import(\"react\").PointerEventHandler; onPointerOverCapture?: import(\"react\").PointerEventHandler; onPointerUp?: import(\"react\").PointerEventHandler; onPointerUpCapture?: import(\"react\").PointerEventHandler; onProgress?: import(\"react\").ReactEventHandler; onProgressCapture?: import(\"react\").ReactEventHandler; onRateChange?: import(\"react\").ReactEventHandler; onRateChangeCapture?: import(\"react\").ReactEventHandler; onReset?: import(\"react\").ReactEventHandler; onResetCapture?: import(\"react\").ReactEventHandler; onScroll?: import(\"react\").UIEventHandler; onScrollCapture?: import(\"react\").UIEventHandler; onScrollEnd?: import(\"react\").UIEventHandler; onScrollEndCapture?: import(\"react\").UIEventHandler; onSeeked?: import(\"react\").ReactEventHandler; onSeekedCapture?: import(\"react\").ReactEventHandler; onSeeking?: import(\"react\").ReactEventHandler; onSeekingCapture?: import(\"react\").ReactEventHandler; onSelect?: import(\"react\").ReactEventHandler; onSelectCapture?: import(\"react\").ReactEventHandler; onStalled?: import(\"react\").ReactEventHandler; onStalledCapture?: import(\"react\").ReactEventHandler; onSubmit?: import(\"react\").SubmitEventHandler; onSubmitCapture?: import(\"react\").SubmitEventHandler; onSuspend?: import(\"react\").ReactEventHandler; onSuspendCapture?: import(\"react\").ReactEventHandler; onTimeUpdate?: import(\"react\").ReactEventHandler; onTimeUpdateCapture?: import(\"react\").ReactEventHandler; onToggle?: import(\"react\").ToggleEventHandler; onTouchCancel?: import(\"react\").TouchEventHandler; onTouchCancelCapture?: import(\"react\").TouchEventHandler; onTouchEnd?: import(\"react\").TouchEventHandler; onTouchEndCapture?: import(\"react\").TouchEventHandler; onTouchMove?: import(\"react\").TouchEventHandler; onTouchMoveCapture?: import(\"react\").TouchEventHandler; onTouchStart?: import(\"react\").TouchEventHandler; onTouchStartCapture?: import(\"react\").TouchEventHandler; onTransitionCancel?: import(\"react\").TransitionEventHandler; onTransitionCancelCapture?: import(\"react\").TransitionEventHandler; onTransitionEnd?: import(\"react\").TransitionEventHandler; onTransitionEndCapture?: import(\"react\").TransitionEventHandler; onTransitionRun?: import(\"react\").TransitionEventHandler; onTransitionRunCapture?: import(\"react\").TransitionEventHandler; onTransitionStart?: import(\"react\").TransitionEventHandler; onTransitionStartCapture?: import(\"react\").TransitionEventHandler; onVolumeChange?: import(\"react\").ReactEventHandler; onVolumeChangeCapture?: import(\"react\").ReactEventHandler; onWaiting?: import(\"react\").ReactEventHandler; onWaitingCapture?: import(\"react\").ReactEventHandler; onWheel?: import(\"react\").WheelEventHandler; onWheelCapture?: import(\"react\").WheelEventHandler; part?: string; ping?: string; popover?: \"\" | \"auto\" | \"manual\" | \"hint\"; popoverTarget?: string; popoverTargetAction?: \"toggle\" | \"show\" | \"hide\"; prefix?: string; property?: string; radioGroup?: string; referrerPolicy?: import(\"react\").HTMLAttributeReferrerPolicy; rel?: string; resource?: string; results?: number; rev?: string; role?: import(\"react\").AriaRole; security?: string; size?: string; slot?: string; spellCheck?: (boolean | \"true\" | \"false\"); style?: import(\"react\").CSSProperties; suppressContentEditableWarning?: boolean; suppressHydrationWarning?: boolean; tabIndex?: number; target?: import(\"react\").HTMLAttributeAnchorTarget; title?: string; tone?: \"inherit\" | \"accent\"; translate?: \"yes\" | \"no\"; type?: string; typeof?: string; unselectable?: \"off\" | \"on\"; vocab?: string;", "EmptyState": "actions?: ReactNode; align?: \"start\" | \"center\"; body?: string; centeredTreatment?: \"neutral\" | \"clinical\"; description?: string; headingLevel?: 2 | 3 | 4 | 5 | 6; icon?: LucideIcon; iconNode?: ReactNode; live?: \"off\" | \"assertive\" | \"polite\"; testId?: string; title: string; tone?: \"danger\" | \"neutral\" | \"info\";", "ErrorSummary": "attempt?: number; className?: string; errors: { fieldId: string; label: string; message: string; }[]; heading?: string;", "ExternalTextLink": "about?: string; accessKey?: string; \"aria-activedescendant\"?: string; \"aria-atomic\"?: (boolean | \"true\" | \"false\"); \"aria-autocomplete\"?: \"none\" | \"list\" | \"inline\" | \"both\"; \"aria-braillelabel\"?: string; \"aria-brailleroledescription\"?: string; \"aria-busy\"?: (boolean | \"true\" | \"false\"); \"aria-checked\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-colcount\"?: number; \"aria-colindex\"?: number; \"aria-colindextext\"?: string; \"aria-colspan\"?: number; \"aria-controls\"?: string; \"aria-current\"?: boolean | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\" | \"time\"; \"aria-describedby\"?: string; \"aria-description\"?: string; \"aria-details\"?: string; \"aria-disabled\"?: (boolean | \"true\" | \"false\"); \"aria-dropeffect\"?: \"none\" | \"link\" | \"copy\" | \"execute\" | \"move\" | \"popup\"; \"aria-errormessage\"?: string; \"aria-expanded\"?: (boolean | \"true\" | \"false\"); \"aria-flowto\"?: string; \"aria-grabbed\"?: (boolean | \"true\" | \"false\"); \"aria-haspopup\"?: boolean | \"true\" | \"false\" | \"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"; \"aria-hidden\"?: (boolean | \"true\" | \"false\"); \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"; \"aria-keyshortcuts\"?: string; \"aria-label\"?: string; \"aria-labelledby\"?: string; \"aria-level\"?: number; \"aria-live\"?: \"off\" | \"assertive\" | \"polite\"; \"aria-modal\"?: (boolean | \"true\" | \"false\"); \"aria-multiline\"?: (boolean | \"true\" | \"false\"); \"aria-multiselectable\"?: (boolean | \"true\" | \"false\"); \"aria-orientation\"?: \"horizontal\" | \"vertical\"; \"aria-owns\"?: string; \"aria-placeholder\"?: string; \"aria-posinset\"?: number; \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-readonly\"?: (boolean | \"true\" | \"false\"); \"aria-relevant\"?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"; \"aria-required\"?: (boolean | \"true\" | \"false\"); \"aria-roledescription\"?: string; \"aria-rowcount\"?: number; \"aria-rowindex\"?: number; \"aria-rowindextext\"?: string; \"aria-rowspan\"?: number; \"aria-selected\"?: (boolean | \"true\" | \"false\"); \"aria-setsize\"?: number; \"aria-sort\"?: \"none\" | \"ascending\" | \"descending\" | \"other\"; \"aria-valuemax\"?: number; \"aria-valuemin\"?: number; \"aria-valuenow\"?: number; \"aria-valuetext\"?: string; autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}); autoCorrect?: string; autoFocus?: boolean; autoSave?: string; children: ReactNode; className?: string; color?: string; content?: string; contentEditable?: (boolean | \"true\" | \"false\") | \"inherit\" | \"plaintext-only\"; contextMenu?: string; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; }; datatype?: string; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; dir?: string; download?: any; draggable?: (boolean | \"true\" | \"false\"); enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\"; exportparts?: string; hidden?: boolean; href: string; hrefLang?: string; id?: string; inert?: boolean; inlist?: any; inputMode?: \"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"; is?: string; itemID?: string; itemProp?: string; itemRef?: string; itemScope?: boolean; itemType?: string; lang?: string; media?: string; nonce?: string; onAbort?: import(\"react\").ReactEventHandler; onAbortCapture?: import(\"react\").ReactEventHandler; onAnimationEnd?: import(\"react\").AnimationEventHandler; onAnimationEndCapture?: import(\"react\").AnimationEventHandler; onAnimationIteration?: import(\"react\").AnimationEventHandler; onAnimationIterationCapture?: import(\"react\").AnimationEventHandler; onAnimationStart?: import(\"react\").AnimationEventHandler; onAnimationStartCapture?: import(\"react\").AnimationEventHandler; onAuxClick?: import(\"react\").MouseEventHandler; onAuxClickCapture?: import(\"react\").MouseEventHandler; onBeforeInput?: import(\"react\").InputEventHandler; onBeforeInputCapture?: import(\"react\").InputEventHandler; onBeforeToggle?: import(\"react\").ToggleEventHandler; onBlur?: import(\"react\").FocusEventHandler; onBlurCapture?: import(\"react\").FocusEventHandler; onCanPlay?: import(\"react\").ReactEventHandler; onCanPlayCapture?: import(\"react\").ReactEventHandler; onCanPlayThrough?: import(\"react\").ReactEventHandler; onCanPlayThroughCapture?: import(\"react\").ReactEventHandler; onChange?: import(\"react\").ChangeEventHandler; onChangeCapture?: import(\"react\").ChangeEventHandler; onClick?: import(\"react\").MouseEventHandler; onClickCapture?: import(\"react\").MouseEventHandler; onCompositionEnd?: import(\"react\").CompositionEventHandler; onCompositionEndCapture?: import(\"react\").CompositionEventHandler; onCompositionStart?: import(\"react\").CompositionEventHandler; onCompositionStartCapture?: import(\"react\").CompositionEventHandler; onCompositionUpdate?: import(\"react\").CompositionEventHandler; onCompositionUpdateCapture?: import(\"react\").CompositionEventHandler; onContextMenu?: import(\"react\").MouseEventHandler; onContextMenuCapture?: import(\"react\").MouseEventHandler; onCopy?: import(\"react\").ClipboardEventHandler; onCopyCapture?: import(\"react\").ClipboardEventHandler; onCut?: import(\"react\").ClipboardEventHandler; onCutCapture?: import(\"react\").ClipboardEventHandler; onDoubleClick?: import(\"react\").MouseEventHandler; onDoubleClickCapture?: import(\"react\").MouseEventHandler; onDrag?: import(\"react\").DragEventHandler; onDragCapture?: import(\"react\").DragEventHandler; onDragEnd?: import(\"react\").DragEventHandler; onDragEndCapture?: import(\"react\").DragEventHandler; onDragEnter?: import(\"react\").DragEventHandler; onDragEnterCapture?: import(\"react\").DragEventHandler; onDragExit?: import(\"react\").DragEventHandler; onDragExitCapture?: import(\"react\").DragEventHandler; onDragLeave?: import(\"react\").DragEventHandler; onDragLeaveCapture?: import(\"react\").DragEventHandler; onDragOver?: import(\"react\").DragEventHandler; onDragOverCapture?: import(\"react\").DragEventHandler; onDragStart?: import(\"react\").DragEventHandler; onDragStartCapture?: import(\"react\").DragEventHandler; onDrop?: import(\"react\").DragEventHandler; onDropCapture?: import(\"react\").DragEventHandler; onDurationChange?: import(\"react\").ReactEventHandler; onDurationChangeCapture?: import(\"react\").ReactEventHandler; onEmptied?: import(\"react\").ReactEventHandler; onEmptiedCapture?: import(\"react\").ReactEventHandler; onEncrypted?: import(\"react\").ReactEventHandler; onEncryptedCapture?: import(\"react\").ReactEventHandler; onEnded?: import(\"react\").ReactEventHandler; onEndedCapture?: import(\"react\").ReactEventHandler; onError?: import(\"react\").ReactEventHandler; onErrorCapture?: import(\"react\").ReactEventHandler; onFocus?: import(\"react\").FocusEventHandler; onFocusCapture?: import(\"react\").FocusEventHandler; onGotPointerCapture?: import(\"react\").PointerEventHandler; onGotPointerCaptureCapture?: import(\"react\").PointerEventHandler; onInput?: import(\"react\").InputEventHandler; onInputCapture?: import(\"react\").InputEventHandler; onInvalid?: import(\"react\").ReactEventHandler; onInvalidCapture?: import(\"react\").ReactEventHandler; onKeyDown?: import(\"react\").KeyboardEventHandler; onKeyDownCapture?: import(\"react\").KeyboardEventHandler; onKeyPress?: import(\"react\").KeyboardEventHandler; onKeyPressCapture?: import(\"react\").KeyboardEventHandler; onKeyUp?: import(\"react\").KeyboardEventHandler; onKeyUpCapture?: import(\"react\").KeyboardEventHandler; onLoad?: import(\"react\").ReactEventHandler; onLoadCapture?: import(\"react\").ReactEventHandler; onLoadedData?: import(\"react\").ReactEventHandler; onLoadedDataCapture?: import(\"react\").ReactEventHandler; onLoadedMetadata?: import(\"react\").ReactEventHandler; onLoadedMetadataCapture?: import(\"react\").ReactEventHandler; onLoadStart?: import(\"react\").ReactEventHandler; onLoadStartCapture?: import(\"react\").ReactEventHandler; onLostPointerCapture?: import(\"react\").PointerEventHandler; onLostPointerCaptureCapture?: import(\"react\").PointerEventHandler; onMouseDown?: import(\"react\").MouseEventHandler; onMouseDownCapture?: import(\"react\").MouseEventHandler; onMouseEnter?: import(\"react\").MouseEventHandler; onMouseLeave?: import(\"react\").MouseEventHandler; onMouseMove?: import(\"react\").MouseEventHandler; onMouseMoveCapture?: import(\"react\").MouseEventHandler; onMouseOut?: import(\"react\").MouseEventHandler; onMouseOutCapture?: import(\"react\").MouseEventHandler; onMouseOver?: import(\"react\").MouseEventHandler; onMouseOverCapture?: import(\"react\").MouseEventHandler; onMouseUp?: import(\"react\").MouseEventHandler; onMouseUpCapture?: import(\"react\").MouseEventHandler; onPaste?: import(\"react\").ClipboardEventHandler; onPasteCapture?: import(\"react\").ClipboardEventHandler; onPause?: import(\"react\").ReactEventHandler; onPauseCapture?: import(\"react\").ReactEventHandler; onPlay?: import(\"react\").ReactEventHandler; onPlayCapture?: import(\"react\").ReactEventHandler; onPlaying?: import(\"react\").ReactEventHandler; onPlayingCapture?: import(\"react\").ReactEventHandler; onPointerCancel?: import(\"react\").PointerEventHandler; onPointerCancelCapture?: import(\"react\").PointerEventHandler; onPointerDown?: import(\"react\").PointerEventHandler; onPointerDownCapture?: import(\"react\").PointerEventHandler; onPointerEnter?: import(\"react\").PointerEventHandler; onPointerLeave?: import(\"react\").PointerEventHandler; onPointerMove?: import(\"react\").PointerEventHandler; onPointerMoveCapture?: import(\"react\").PointerEventHandler; onPointerOut?: import(\"react\").PointerEventHandler; onPointerOutCapture?: import(\"react\").PointerEventHandler; onPointerOver?: import(\"react\").PointerEventHandler; onPointerOverCapture?: import(\"react\").PointerEventHandler; onPointerUp?: import(\"react\").PointerEventHandler; onPointerUpCapture?: import(\"react\").PointerEventHandler; onProgress?: import(\"react\").ReactEventHandler; onProgressCapture?: import(\"react\").ReactEventHandler; onRateChange?: import(\"react\").ReactEventHandler; onRateChangeCapture?: import(\"react\").ReactEventHandler; onReset?: import(\"react\").ReactEventHandler; onResetCapture?: import(\"react\").ReactEventHandler; onScroll?: import(\"react\").UIEventHandler; onScrollCapture?: import(\"react\").UIEventHandler; onScrollEnd?: import(\"react\").UIEventHandler; onScrollEndCapture?: import(\"react\").UIEventHandler; onSeeked?: import(\"react\").ReactEventHandler; onSeekedCapture?: import(\"react\").ReactEventHandler; onSeeking?: import(\"react\").ReactEventHandler; onSeekingCapture?: import(\"react\").ReactEventHandler; onSelect?: import(\"react\").ReactEventHandler; onSelectCapture?: import(\"react\").ReactEventHandler; onStalled?: import(\"react\").ReactEventHandler; onStalledCapture?: import(\"react\").ReactEventHandler; onSubmit?: import(\"react\").SubmitEventHandler; onSubmitCapture?: import(\"react\").SubmitEventHandler; onSuspend?: import(\"react\").ReactEventHandler; onSuspendCapture?: import(\"react\").ReactEventHandler; onTimeUpdate?: import(\"react\").ReactEventHandler; onTimeUpdateCapture?: import(\"react\").ReactEventHandler; onToggle?: import(\"react\").ToggleEventHandler; onTouchCancel?: import(\"react\").TouchEventHandler; onTouchCancelCapture?: import(\"react\").TouchEventHandler; onTouchEnd?: import(\"react\").TouchEventHandler; onTouchEndCapture?: import(\"react\").TouchEventHandler; onTouchMove?: import(\"react\").TouchEventHandler; onTouchMoveCapture?: import(\"react\").TouchEventHandler; onTouchStart?: import(\"react\").TouchEventHandler; onTouchStartCapture?: import(\"react\").TouchEventHandler; onTransitionCancel?: import(\"react\").TransitionEventHandler; onTransitionCancelCapture?: import(\"react\").TransitionEventHandler; onTransitionEnd?: import(\"react\").TransitionEventHandler; onTransitionEndCapture?: import(\"react\").TransitionEventHandler; onTransitionRun?: import(\"react\").TransitionEventHandler; onTransitionRunCapture?: import(\"react\").TransitionEventHandler; onTransitionStart?: import(\"react\").TransitionEventHandler; onTransitionStartCapture?: import(\"react\").TransitionEventHandler; onVolumeChange?: import(\"react\").ReactEventHandler; onVolumeChangeCapture?: import(\"react\").ReactEventHandler; onWaiting?: import(\"react\").ReactEventHandler; onWaitingCapture?: import(\"react\").ReactEventHandler; onWheel?: import(\"react\").WheelEventHandler; onWheelCapture?: import(\"react\").WheelEventHandler; part?: string; ping?: string; popover?: \"\" | \"auto\" | \"manual\" | \"hint\"; popoverTarget?: string; popoverTargetAction?: \"toggle\" | \"show\" | \"hide\"; prefix?: string; property?: string; radioGroup?: string; referrerPolicy?: import(\"react\").HTMLAttributeReferrerPolicy; resource?: string; results?: number; rev?: string; role?: import(\"react\").AriaRole; security?: string; slot?: string; spellCheck?: (boolean | \"true\" | \"false\"); style?: import(\"react\").CSSProperties; suppressContentEditableWarning?: boolean; suppressHydrationWarning?: boolean; tabIndex?: number; title?: string; tone?: \"inherit\" | \"accent\"; translate?: \"yes\" | \"no\"; type?: string; typeof?: string; unselectable?: \"off\" | \"on\"; vocab?: string;", diff --git a/docs/design-system/COMPONENTS.md b/docs/design-system/COMPONENTS.md index f4e7b92860..1d42901ecc 100644 --- a/docs/design-system/COMPONENTS.md +++ b/docs/design-system/COMPONENTS.md @@ -60,32 +60,32 @@ print primitives (`PrintHeader`, `PrintFooter`, `CitationFootnote`, `PrintOnly`, ### 0.4 Open-defect ledger (existing components → closing PR) -| Component | Open defects (compressed) | Closes in | -| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | -| Button | danger hover/active tokens and the 48px comment are resolved; ref forwarding and the client boundary remain | follow-on | -| AsyncButton | `type` applied after spread (default `button`; explicit `submit` preserved). Prefer `Button` busy API for new sites. | **done** (PR-A) | -| IconButton | disabled encoding uses `controlDisabled` (opacity retired in PR-A) | **done** (PR-A) | -| ToggleSwitch | operable branch requires `aria-label`; opacity disabled retired; knob now travels on `transform` (`translate-x-4`), tokenised duration, reduced-motion opt-out | **done** (motion) | -| Chip | final `appearance`/size API, removable label contract, tap target and full-value title | **done** | -| TextField/SearchField/Select | hint dropped on error (comment promises otherwise) · describedBy overwritten · no external id/refs (placeholder off decoration tier in PR-A) | PR 7 | -| Checkbox/RadioGroup | RadioGroup controlled/uncontrolled union done; raw dimensions · unsanitised ids · no group hint/error | PR 7 | -| Citation/CitationList | required interactive handler and stable-id list keys are resolved; static span labelling and route/source modes remain | follow-on | -| DoseLine | must compose `Quantity` · structured dose model · overdue text + non-colour mark + open action | **done** (PR 6) | -| StatusMark | app-type coupling · inline styles/raw geometry · HCM token remaps proven (computed suite); mark shape still visual | PR 12 | -| AnswerCard | required verification/state, structured actions and five-state vocabulary landed | **done** (PR 6) | -| AnswerFooter | machine ISO values composed through `DateDisplay` + `MissingValue` landed | **done** (PR 6) | -| PageHeader/Breadcrumb | `

    ` truncates · actions starve title · eyebrow ink moved off decoration (PR 3); layout starve remains | PR 7-adjacent layout fix | -| Tabs | `aria-controls` to unrendered panels is resolved (emitted only for the selected tab when it owns a panel); invalid selected value can still empty the tab order | PR 4-adjacent | -| Pagination | unclamped props · 320px overflow · opacity disabled retired · no focus/announce policy | PR 8 | -| Links | `tone` leak is resolved (destructured before the spread in every link); `download` still overridable by spread · `gap` animation · new-tab policy implicit | PR 9 | -| Tooltip | composed handlers/description, string content, OverlayRoot portal, collision and delay contract | **done** (PR 10) | -| Toast | independent tone/priority/persistence, OverlayRoot portal, pause, dedupe and queue contract | **done** (PR 10) | -| Sheet/ConfirmDialog | required names/action labels, portal default, tokened layers/duration and wrapping titles | **done** (PR 10) | -| Disclosure | configurable heading level is resolved; print behaviour and truncation remain | PR 11 | -| Progress/StageList | determinate width animation is resolved via `scaleX`; hardcoded indeterminate timing, "step 0 of N" and whole-list live region remain | PR 8, PR 9 | -| EmptyState | static live-off default with explicit polite/assertive opt-in | **done** (PR 8) | -| AccessibleTable | required semantic caption and `MissingValue` cells landed; dense headers, content-role widths and Button-based expander remain | PR 6/PR 12 remainder | -| ui-primitives.tsx | 686-line module mixing recipes/actions/feedback/clinical — split. 200 product files import it, against the 53 registered components' 27 adopted; this module, not the registry, is what the product actually runs on | PR 12 | +| Component | Open defects (compressed) | Closes in | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | +| Button | danger hover/active tokens, the 48px comment and `ref` forwarding (plus a `testId` passthrough — React 19 types give components no `data-*` index signature) are resolved; the client boundary remains | follow-on | +| AsyncButton | `type` applied after spread (default `button`; explicit `submit` preserved). Prefer `Button` busy API for new sites. | **done** (PR-A) | +| IconButton | disabled encoding uses `controlDisabled` (opacity retired in PR-A) | **done** (PR-A) | +| ToggleSwitch | operable branch requires `aria-label`; opacity disabled retired; knob now travels on `transform` (`translate-x-4`), tokenised duration, reduced-motion opt-out | **done** (motion) | +| Chip | final `appearance`/size API, removable label contract, tap target and full-value title | **done** | +| TextField/SearchField/Select | hint dropped on error (comment promises otherwise) · describedBy overwritten · no external id/refs (placeholder off decoration tier in PR-A) | PR 7 | +| Checkbox/RadioGroup | all four closed. Two rows were stale when re-measured on 2026-08-08: ids already sanitise through `optionId`, and the group already carries `hint`/`error`/`describedBy`. Raw dimensions moved to `size-4.5`/`h-0.5` | **done** | +| Citation/CitationList | required interactive handler, stable-id list keys and static-span labelling (`aria-label` on a role-less span was dropped, losing the currency phrase on print) are resolved; route/source modes remain | follow-on | +| DoseLine | must compose `Quantity` · structured dose model · overdue text + non-colour mark + open action | **done** (PR 6) | +| StatusMark | app-type coupling resolved — `DocumentStatus` is declared here and `@/lib/types` conforms to it (asserted at compile time in the DOM suite), not the reverse; inline styles/raw geometry remain | PR 12 | +| AnswerCard | required verification/state, structured actions and five-state vocabulary landed | **done** (PR 6) | +| AnswerFooter | machine ISO values composed through `DateDisplay` + `MissingValue` landed | **done** (PR 6) | +| PageHeader/Breadcrumb | all resolved. The wrap alone was not enough — the actions track still took full max-content first, so the title column now has a `minmax(20ch,1fr)` floor and the actions track is the one that gives way | **done** | +| Tabs | both resolved. An invalid `value` no longer empties the tab order: the first enabled tab becomes reachable and owns the panel wiring. Reachability only — no `onChange` is fired to repair caller state | **done** | +| Pagination | all resolved. Props clamp (`page=0` no longer emits `onPageChange(-1)`), the control row wraps at 320px, and reaching a boundary hands focus to the current page and announces "Page N of M" through `LiveAnnouncer` | **done** | +| Links | `tone` leak, spread-overridable `download` (now type-omitted AND written after the spread) and the `gap` "animation" (`gap` is not in Tailwind's `transition` list, so it jumped; now a composited `translate-x`) are resolved; new-tab policy still implicit, and `LinkAction` silently ignores `tone` | PR 9 | +| Tooltip | composed handlers/description, string content, OverlayRoot portal, collision and delay contract | **done** (PR 10) | +| Toast | independent tone/priority/persistence, OverlayRoot portal, pause, dedupe and queue contract | **done** (PR 10) | +| Sheet/ConfirmDialog | required names/action labels, portal default, tokened layers/duration and wrapping titles | **done** (PR 10) | +| Disclosure | heading level and print resolved — a collapsed panel is `print:block`, so a section no longer prints as if the guideline never mentioned it; truncation remains. The old docstring's Ctrl-F/print claim was false | PR 11 | +| Progress/StageList | all four resolved: `scaleX`, the theme `animate-shimmer` in place of a hardcoded `1.4s`, a step index clamped to ≥1, and an sr-only `role="status"` sibling in place of `aria-live` on the whole `
      ` | **done** | +| EmptyState | static live-off default with explicit polite/assertive opt-in | **done** (PR 8) | +| AccessibleTable | semantic caption, `MissingValue` cells, dense headers (clipped header keeps its full string as `title`) and the expander (now the registered `Button`, off the local ring-focus recipe) landed; content-role widths remain | PR 6/PR 12 remainder | +| ui-primitives.tsx | 686-line module mixing recipes/actions/feedback/clinical — split. 200 product files import it, against the 53 registered components' 27 adopted; this module, not the registry, is what the product actually runs on | PR 12 | --- @@ -935,7 +935,7 @@ tests to run. Registered public components: 53 Components with a valid design-sync preview: 53 -Components with product imports: 29 +Components with product imports: 30 This generated snapshot is a local source-derived inventory. It does not assert remote design-project publication. @@ -946,7 +946,7 @@ This generated snapshot is a local source-derived inventory. It does not assert | `AnswerFooter` | answer | yes | yes | no | yes | no | 0 | | `AsyncButton` | controls | yes | yes | inherited-global-root | yes | no | 4 | | `Breadcrumb` | layout | yes | yes | inherited-global-root | yes | no | 1 | -| `Button` | controls | yes | yes | no | yes | no | 0 | +| `Button` | controls | yes | yes | inherited-global-root | yes | no | 1 | | `Checkbox` | controls | yes | yes | no | yes | no | 0 | | `Chip` | controls | yes | yes | inherited-global-root | yes | no | 3 | | `Citation` | source | yes | yes | no | yes | no | 0 | diff --git a/docs/design-system/adoption-manifest.json b/docs/design-system/adoption-manifest.json index a133bf4e86..07140b3712 100644 --- a/docs/design-system/adoption-manifest.json +++ b/docs/design-system/adoption-manifest.json @@ -267,8 +267,8 @@ "source": "src/components/ui/button.tsx", "sourceExported": true, "entryExported": true, - "directImportFiles": ["src/components/ui/confirm-dialog.tsx"], - "productImportFiles": [], + "directImportFiles": ["src/components/AccessibleTable.tsx", "src/components/ui/confirm-dialog.tsx"], + "productImportFiles": ["src/components/AccessibleTable.tsx"], "designSync": { "listedInSourceMap": true, "listedInDtsProps": true, @@ -276,6 +276,7 @@ "previewValid": true }, "testFiles": [ + "tests/accessible-table.dom.test.tsx", "tests/design-sync-visual-exports.test.ts", "tests/design-system-adoption.test.ts", "tests/ui-v2-components.dom.test.tsx" @@ -288,8 +289,8 @@ }, "built": true, "locallyRegistered": true, - "v2ShellMounted": false, - "v2MountMode": "none", + "v2ShellMounted": true, + "v2MountMode": "inherited-global-root", "proofDeclared": true, "baselineCommitted": false }, @@ -1056,7 +1057,7 @@ "preview": ".design-sync/previews/PageHeader.tsx", "previewValid": true }, - "testFiles": ["tests/design-sync-visual-exports.test.ts"], + "testFiles": ["tests/design-sync-visual-exports.test.ts", "tests/ui-v2-components.dom.test.tsx"], "baseline": { "targetLayer": "v2", "liveLayer": "v2", @@ -1620,7 +1621,7 @@ "preview": ".design-sync/previews/StageList.tsx", "previewValid": true }, - "testFiles": ["tests/design-sync-visual-exports.test.ts"], + "testFiles": ["tests/design-sync-visual-exports.test.ts", "tests/ui-v2-components.dom.test.tsx"], "baseline": { "targetLayer": "v2", "liveLayer": "v2", @@ -1652,7 +1653,7 @@ "preview": ".design-sync/previews/StatusMark.tsx", "previewValid": true }, - "testFiles": ["tests/design-sync-visual-exports.test.ts"], + "testFiles": ["tests/design-sync-visual-exports.test.ts", "tests/ui-v2-components.dom.test.tsx"], "baseline": { "targetLayer": "v2", "liveLayer": "v2", @@ -3241,7 +3242,7 @@ "summary": { "registeredComponentCount": 53, "previewCount": 53, - "productImportedComponentCount": 29, + "productImportedComponentCount": 30, "rootCount": 55, "productionRouteCount": 47, "nextUiEntryCount": 99 diff --git a/src/components/AccessibleTable.tsx b/src/components/AccessibleTable.tsx index f0af910df5..aea33441b3 100644 --- a/src/components/AccessibleTable.tsx +++ b/src/components/AccessibleTable.tsx @@ -3,6 +3,7 @@ import { Maximize2, TriangleAlert } from "lucide-react"; import { type ReactNode, useCallback, useId, useMemo, useRef, useState, useSyncExternalStore } from "react"; import { cn, textMuted } from "@/components/ui-primitives"; +import { Button } from "@/components/ui/button"; import { Sheet } from "@/components/ui/sheet"; import { MissingValue } from "@/components/ui/missing-value"; import { normalizeAccessibleTable, type NormalizedAccessibleTable } from "@/lib/accessible-table-normalization"; @@ -220,6 +221,13 @@ function AccessibleTableMarkup({ {canExpand ? ( - + Expand table + ) : null} , "childre */ busy?: boolean; busyLabel?: string; + /** + * Forwarded to the underlying `); + + expect(ref.current).toBeInstanceOf(HTMLButtonElement); + expect(ref.current).toBe(screen.getByRole("button", { name: "Reindex" })); + }); + + it("passes testId through as data-testid", () => { + render(); + expect(screen.getByTestId("reindex-button")).toHaveAccessibleName("Reindex"); + }); +}); + +describe("Progress / StageList", () => { + it("drives the indeterminate sweep from the theme animation, not a hardcoded duration", () => { + render(); + const fill = screen.getByTestId("progress-fill"); + + expect(fill.className).toContain("animate-shimmer"); + // The literal it replaced pinned both the timing and a second easing here. + expect(fill.className).not.toMatch(/animate-\[/); + }); + + it("never announces step 0 — an unstarted job is at its first step", () => { + render( + , + ); + + expect(screen.getByTestId("stage-list")).toHaveAttribute("aria-label", "Ingestion: step 1 of 3"); + }); + + it("counts the active stage, and clamps at the last one when every stage is done", () => { + const { rerender } = render( + , + ); + expect(screen.getByTestId("stage-list")).toHaveAttribute("aria-label", "Ingestion: step 2 of 3"); + + rerender( + , + ); + expect(screen.getByTestId("stage-list")).toHaveAttribute("aria-label", "Ingestion: step 3 of 3"); + }); + + it("announces through a dedicated status node rather than making the whole list live", () => { + render( + , + ); + + const list = screen.getByTestId("stage-list"); + // aria-live on the
        re-read every stage label and detail on each + // transition; five stages meant five sentences to learn that step 3 started. + expect(list).not.toHaveAttribute("aria-live"); + // And the status node is a sibling, not a child — an sr-only
      1. would have + // made a three-stage job announce as "list, 4 items". + expect(list.querySelector('[data-testid="stage-list-status"]')).toBeNull(); + + const status = screen.getByTestId("stage-list-status"); + expect(status).toHaveAttribute("role", "status"); + expect(status).toHaveTextContent("Ingestion: step 2 of 3, Parse"); + }); +}); + +describe("StatusMark — design-system vocabulary", () => { + it("draws a distinct shape for every state it declares", () => { + const states: DocumentStatus[] = ["current", "review_due", "outdated", "unknown"]; + for (const status of states) { + const { unmount } = render(); + expect(screen.getByTestId("status-mark")).toHaveAttribute("data-status", status); + unmount(); + } + }); + + it("keeps the application row type conforming to what the mark can draw", () => { + // Compile-time half of the decoupling: `StatusMark` no longer aliases its + // API off `ClinicalSourceMetadata["document_status"]`, so this assignment is + // what fails `typecheck` if the row type grows a fifth value the mark has no + // shape for. Previously the alias let it through and it fell silently + // through to the `unknown` styling. + const appStatus: NonNullable = "review_due"; + const drawable: DocumentStatus = appStatus; + expect(drawable).toBe("review_due"); + }); +}); + +describe("PageHeader — actions must not starve the title", () => { + it("floors the title column and lets the actions column give way", () => { + const { container } = render( + + + + + + } + />, + ); + + const grid = container.querySelector("header > div"); + // `[minmax(0,1fr)_auto]` sized the actions track to its full max-content + // first and let the title collapse toward zero. The floor is the fix; the + // existing flex-wrap only decided where the actions sat, not how wide. + expect(grid?.className).toContain("sm:grid-cols-[minmax(20ch,1fr)_minmax(0,auto)]"); + expect(grid?.className).not.toContain("sm:grid-cols-[minmax(0,1fr)_auto]"); + }); +}); + +describe("Disclosure — print", () => { + it("expands a collapsed panel for print instead of omitting the section", () => { + render( + +

        Review ECG and electrolytes.

        +
        , + ); + + const panel = screen.getByTestId("disclosure").querySelector('[role="region"]'); + expect(panel).toHaveAttribute("hidden"); + expect(panel).toHaveAttribute("data-open", "false"); + // On paper there is no control to open, so a collapsed section would print + // as though the guideline never mentioned it — undetectably. + expect(panel?.className).toContain("print:block"); + }); + + it("keeps an open panel visible and still print-expanded", () => { + render( + +

        Review ECG and electrolytes.

        +
        , + ); + + const panel = screen.getByRole("region"); + expect(panel).not.toHaveAttribute("hidden"); + expect(panel).toHaveAttribute("data-open", "true"); + }); +}); + +describe("Tabs — invalid selected value", () => { + const items = [ + { id: "answer", label: "Answer" }, + { id: "sources", label: "Sources" }, + ]; + + it("keeps the strip reachable when value matches no enabled tab", () => { + // A stale saved filter or a deep link to a removed tab produced this. Every + // tab took tabIndex -1, so Tab skipped the whole strip and the arrow keys + // that would have fixed it were unreachable. + render( + {}}> +

        panel

        +
        , + ); + + const tabs = screen.getAllByRole("tab"); + expect(tabs.filter((tab) => tab.getAttribute("tabindex") === "0")).toHaveLength(1); + expect(screen.getByRole("tab", { name: "Answer" })).toHaveAttribute("tabindex", "0"); + // Reachability only: nothing is claimed as selected, and onChange is not + // fired behind the caller's back. + expect(tabs.every((tab) => tab.getAttribute("aria-selected") === "false")).toBe(true); + }); + + it("does not fire onChange to repair the caller's state", () => { + const onChange = vi.fn(); + render(); + expect(onChange).not.toHaveBeenCalled(); + }); + + it("skips a disabled tab when picking the reachable fallback", () => { + render( + {}} + />, + ); + expect(screen.getByRole("tab", { name: "Sources" })).toHaveAttribute("tabindex", "0"); + }); + + it("keeps the panel wired to a tab that exists", () => { + render( + {}}> +

        panel

        +
        , + ); + + const panel = screen.getByRole("tabpanel"); + const owner = screen.getByRole("tab", { name: "Answer" }); + // Keyed off `value`, both of these dangled at ids no element carried. + expect(panel).toHaveAttribute("aria-labelledby", owner.id); + expect(owner).toHaveAttribute("aria-controls", panel.id); + }); +}); + +describe("Pagination — clamping, focus and announcement", () => { + it("clamps an out-of-range page rather than emitting page 0 or -1", async () => { + const onPageChange = vi.fn(); + render(); + + // page=0 arrives from a URL query or a stale saved filter; it is an ordinary + // state, not a caller bug. + expect(screen.getByRole("button", { name: "Page 1" })).toHaveAttribute("aria-current", "page"); + expect(screen.getByRole("button", { name: "Previous page" })).toBeDisabled(); + + await userEvent.click(screen.getByRole("button", { name: "Next page" })); + expect(onPageChange).toHaveBeenCalledWith(2); + }); + + it("clamps a page past the end onto the last page", async () => { + const onPageChange = vi.fn(); + render(); + + expect(screen.getByRole("button", { name: "Page 5" })).toHaveAttribute("aria-current", "page"); + expect(screen.getByRole("button", { name: "Next page" })).toBeDisabled(); + + await userEvent.click(screen.getByRole("button", { name: "Previous page" })); + expect(onPageChange).toHaveBeenCalledWith(4); + }); + + it("hands focus to the current page when the pressed step button disables itself", async () => { + function Harness() { + const [page, setPage] = useState(2); + return ; + } + render(); + + const previous = screen.getByRole("button", { name: "Previous page" }); + previous.focus(); + await userEvent.click(previous); + + // Reaching page 1 disables Previous, which used to drop focus to and + // lose the reader's place in the list. + await waitFor(() => expect(previous).toBeDisabled()); + expect(document.activeElement).toBe(screen.getByRole("button", { name: "Page 1" })); + }); + + it("wraps the control row so a seven-page window fits a 320px viewport", () => { + render( {}} />); + const row = screen.getByRole("button", { name: "Previous page" }).parentElement; + expect(row?.className).toContain("flex-wrap"); + }); +}); + +describe("Links — invariants a spread cannot override", () => { + it("keeps download set even when a caller spreads over it", () => { + // The type omits `download` and the attribute is written after the spread — + // two independent guards, because a JS caller can defeat the first. + render( + + Guideline + , + ); + + expect(screen.getByRole("link", { name: /Guideline/ })).toHaveAttribute("download"); + }); + + it("keeps the external link's target and rel ahead of a spread", () => { + render( + + External + , + ); + + const link = screen.getByRole("link", { name: /External/ }); + expect(link).toHaveAttribute("target", "_blank"); + expect(link).toHaveAttribute("rel", "noopener noreferrer"); + }); +}); + +describe("Checkbox / RadioGroup — dimensions on the spacing scale", () => { + it("sizes the checkbox box and its mixed-state dash without arbitrary literals", () => { + const { container } = render(); + + const box = container.querySelector(".grid.size-4\\.5"); + expect(box).not.toBeNull(); + expect(container.innerHTML).not.toContain("size-[1.125rem]"); + expect(container.innerHTML).not.toContain("h-[2px]"); + }); + + it("keeps the radio control on the same box recipe", () => { + const { container } = render( + , + ); + + expect(container.querySelector(".size-4\\.5")).not.toBeNull(); + expect(container.innerHTML).not.toContain("size-[1.125rem]"); + }); +}); From fcd4388803fadde155cd30684faa8d3488c09c01 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:45:23 +0800 Subject: [PATCH 2/8] docs(issues): close #263 (Track A4 component defects) and capture the LinkAction tone gap Records that three of the listed sub-defects were stale when re-measured, what was deliberately left out of scope, and the two ratchets that fell. Adds #273 for LinkAction accepting a `tone` prop it never reads. Co-Authored-By: Claude Opus 5 --- docs/outstanding-issues.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index d809e66e17..e2de35963d 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -166,7 +166,7 @@ removed after current-main verification; it is not missing recommended work. | 113 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | - + ## Open items > **Merged-main canary update (2026-07-23, run `30018289898`):** the new structured report correctly recorded evaluated tree `c24f2e8f2d30d0c59fc1eba025d3dcd63478137e`, run/attempt identity and `cross-region-runner` latency context. Golden retrieval remained 36/36 with document/content recall 1.0 and no failed cases. The 44-case answer gate had grounded-supported and unsupported-correct rates of 1.0, but failed because `neuroleptic-side-effect-escalation` again returned one citation where two are required (citation-failure rate 0.0227). `admission-discharge-comparison` again omitted the specific AKG admission document after `comparison_source_extractive_fallback`; `admission-discharge-coverage-paraphrase` was advisory-only at 24,870 ms. Answer cost was reported as `$0.234736`. Do not retry immediately: retain this as the first structured datapoint, compare it with the scheduled 2026-07-26 report, and keep retrieval/ranking unchanged. @@ -303,7 +303,6 @@ removed after current-main verification; it is not missing recommended work. | #260 | P2 | task | Two unpushed Sentry commits are stranded on a Windows-only branch and will be lost with that machine | **Outcome:** the Sentry setup/logging-hardening work is either shipped or consciously discarded, not left sitting in one machine's reflog. **Detail:** `claude/cloud-pr-loop-prevention-bc052b` carries two commits — `c3c9d6a31` and `abbcdc8e9`, ~389 lines across `src/sentry.*.config.ts`, `src/lib/env.ts`, `src/lib/supabase/client.tsx`, `src/components/ui-primitives.tsx` — that were never pushed and are not the authoring session's own work. The branch does not exist on the remote, so the commits are unreachable from any cloud or remote container; a 2026-08-07 remote session could not inspect, verify, or ship them and could only record their existence. The same worktree (`.claude/worktrees/pensive-borg-6be2f0`) still holds the same four files uncommitted. Two Sentry branches DO exist on origin — `claude/sentry-nextjs-sdk-setup-2v24q5` and `cursor/sentry-nextjs-sdk-7cee` — but whether either already carries this change is unconfirmed: a three-dot diff against `origin/main` from the remote container returned empty for both, which is not trustworthy as proof either way and was not pursued further. Note this touches `src/lib/env.ts` and `src/lib/supabase/client.tsx`, so it is not a docs-class change and needs a real gate whenever it does ship. **Next:** from the Windows machine, diff those two commits against the two remote Sentry branches to decide whether the work is already represented. If it is, delete the branch; if it is not, push it and open a PR rather than leaving it local. **Stop:** do not discard the commits blind, and do not assume the remote Sentry branches supersede them without a content diff — nothing has yet compared them. | session 2026-08-07 remote container; handoff notes from the PR #1649 session; origin branches claude/sentry-nextjs-sdk-setup-2v24q5 and cursor/sentry-nextjs-sdk-7cee | 2026-08-07 | | #261 | P2 | task | DS Track A2: retire --shadow-focus from the search composer | Replace the composer's companion focus ring with the sanctioned outline / --focus treatment used everywhere else, then delete the token (both theme declarations). Live consumer is .chat-composer-shell-delta:focus-within in globals.css — a --include=*.tsx grep reports zero consumers and is wrong. This is a visible focus-state change on the search composer: read docs/search-chrome-behaviour.md first and get a Chromium look. Gate: npm run check:design-system-contract + npm run verify:phone-chrome. | session 2026-08-07 — design-system HANDOVER-2026-08-07 Track A1 handoff (PR #1678) | 2026-08-07 | | #262 | P2 | task | DS Track A3: finish the design-token debt | Three parts. (1) Move --shadow-tight's 155 consumers onto the --eN elevation ladder. (2) Add a step-SELECTION lint for the eight non-standard type steps (1 318 sites) — check:type-scale already blocks arbitrary text-[12px], so do NOT write a lint duplicating the half that ships. (3) Extend the contract ratchet to raw padding / radius / line-height literals; it covers colour, shadow, tap and tracking today. Gate: npm run check:design-system-contract. | session 2026-08-07 — design-system HANDOVER-2026-08-07 Track A1 handoff (PR #1678) | 2026-08-07 | -| #263 | P2 | task | DS Track A4: close the 13 open COMPONENTS section 0.4 defect rows | Button forwardRef; Progress/StageList (hardcoded 1.4s shimmer, 'step 0 of N' reachable, whole-list live region); StatusMark app-type coupling; PageHeader title starve; Disclosure print; AccessibleTable dense headers and Button-based expander; Tabs invalid-value tab order; Pagination focus/announce; Links download overridable by spread and gap animation; Citation static-span labelling; Checkbox/RadioGroup raw dimensions and group hint/error. Roughly five of the original fourteen rows were already stale and are excluded. Gate: the three component DOM suites plus check:design-system-contract. | session 2026-08-07 — design-system HANDOVER-2026-08-07 Track A1 handoff (PR #1678) | 2026-08-07 | | #264 | P2 | task | DS Track A5: gate the six ungated design-system prohibitions | Highest-value item in Track A — a prohibition with no gate row is a suggestion, which is the failure mode GATES.md exists to prevent. Colour-boundary lint (no numeral painted with a status colour); PDF/diagram invert refusal; layout-property animation lint; border+ring co-occurrence; --shadow-tight alias lint; dark: override lint (3 live instances). Gate: npm run check:design-system-contract + npm run lint. | session 2026-08-07 — design-system HANDOVER-2026-08-07 Track A1 handoff (PR #1678) | 2026-08-07 | | #265 | P2 | task | DS Track A6: move design-system gates 2, 4, 7 and 8 from partial to blocking | Gate 4 needs a repo-wide status-indicator enumeration — today a new colour-only indicator slips through. Gate 2 needs the fixed-height h-10 case and test:e2e:style-contract wired into verify:cheap. Gates 7 and 8 likewise. Gate: npm run verify:cheap once wired. | session 2026-08-07 — design-system HANDOVER-2026-08-07 Track A1 handoff (PR #1678) | 2026-08-07 | | #266 | P2 | task | DS Track B1: adopt the 24 unadopted components demand-driven, never as a race to 53/53 | Pick a surface and let it pull, the way PR #1658 did for AnswerCard. Forms are the largest single tranche: FieldError, FieldHint, ErrorSummary, SearchField, Checkbox and RadioGroup all land together on one form conversion. Do not stub a component to move the adoption count. Regenerate with npm run design-system:adoption:update after any import change; the manifest is generated, never hand-edited. | session 2026-08-07 — design-system HANDOVER-2026-08-07 Track A1 handoff (PR #1678) | 2026-08-07 | @@ -313,6 +312,7 @@ removed after current-main verification; it is not missing recommended work. | #270 | P2 | task | 22 call sites pair min-h-tap/h-tap with a dead numeric height, blocking tap from the tailwind-merge config | Follow-up from #218 / PR #1678. Tailwind emits .min-h-tap after every numeric .min-h-* and .h-tap after .h-4 / .h-10.5, so the 48px tap token already wins at these sites and the numeric class is dead: document-admin (3), DocumentManagerPanel (7), favourites-hub (3), settings-dialog (2), service-detail-page (4, all min-h-12 so genuinely no-ops), form-detail-page (1), clinical-output-helpers (1), account-setup-dialog (1). Because of them, --spacing-tap is deliberately NOT declared in src/lib/tailwind-merge.ts — declaring it would flip the win to the later class and drop 18 production targets from 48px to 32/36/40/42px, which AGENTS.md forbids. Next action: delete the dead numeric class at each site (no visual change — it does not apply today), then add 'tap' to CLINICAL_TWMERGE_THEME.spacing and delete the pinning test in tests/tailwind-merge-config.test.ts. Do NOT instead lower any target to min-h-11. Gate: npm run check:design-system-contract, npm run test, and a Chromium look. | session 2026-08-07 — design-system HANDOVER-2026-08-07 Track A1 handoff (PR #1678) | 2026-08-07 | | #271 | P3 | task | Decide whether to delete the now-consumer-less action kind in SecondaryNavigation | PR #1679 removed the last live consumer of SecondaryNavigationActionItem: the seven single-surface modes registered one action entry each and those were deleted. therapy-compass still declares action entries but PageSecondaryNavigation early-returns on /therapy-compass* before reading them, so nothing builds an action item any more. Kept deliberately rather than deleted in the same PR: the kind carries the tablist roving-focus behaviour and is covered directly by tests/secondary-navigation.dom.test.tsx, so it is component API with tests rather than orphaned code, and check:knip runs without --include exports so the dead-code gate will never flag it either way. Next: decide keep-or-delete as its own change. If delete, also remove the tablist-mode tests in tests/secondary-navigation.dom.test.tsx and the therapy-compass action entries in the registry. Stop: do not do half of each — a deleted branch with its tests left behind, or vice versa, is worse than either. Renumbered from this PR's original #261 → #271 because main claimed #261–#270 via PR #1678 design-system tracks. | session 2026-08-07; PR #1679 | 2026-08-07 | | #272 | P3 | rec | Header addon-slot single-owner rule is enforced by two lists agreeing by coincidence, not a guard | The universal header's addon slot must hold exactly ONE page-owned header. Nothing in PageSecondaryNavigation states that rule. What actually enforces it is that every route claiming the slot (DocumentViewer, differentials/differential-detail-page) also happens to be hasLocalInformationPageNavigation, which returns null before the mode branch is reached — two independently maintained lists agreeing by accident. tests/mode-nav-addon-slot.dom.test.tsx asserts the agreement route-for-route and is what will go red when a future claimant falls outside that cover. The original incidental protection (a claimant mode had fewer than MODE_NAV_MIN_ITEMS destinations so ModeNav rendered nothing) has already expired twice: for differentials in PR #1647 and for factsheets in PR #1674. Next: no action required while the lists agree; if documents or another slot claimant ever gains a second routed destination, add an explicit isHeaderAddonSlotOwnedRoute guard at the mode branch in PageSecondaryNavigation rather than widening either list. Stop: do not delete isHeaderAddonSlotOwnedRoute as unused — no production code calls it, but it is the named claimant list the test asserts against. Renumbered from this PR's original #262 → #272 because main claimed #261–#270 via PR #1678 design-system tracks. | session 2026-08-07; PRs #1647, #1674, #1679 | 2026-08-07 | +| #273 | P3 | issue | LinkAction accepts a tone prop it never reads | src/components/ui/link.tsx: LinkActionProps = BaseProps & { href: string }, and BaseProps declares tone?: 'accent' \| 'inherit'. LinkAction destructures only { href, children, className }, so a call site can pass tone='inherit' and get the accent styling anyway, with no type error and no DOM leak to notice it by. The three sibling links (TextLink, ExternalTextLink, DownloadLink) all honour tone, so this is an inconsistency inside one module rather than a deliberate omission. Found while closing #263 (Track A4) and left alone because it is a behaviour change to a link's colour on adopted surfaces, not a defect on the #263 list. Next: decide whether LinkAction should honour tone (apply toneClass[tone] like its siblings) or refuse it (drop BaseProps' tone from LinkActionProps so the compiler rejects the call). Refusing is probably right — a forward-action link is always the accent by design — and is the smaller diff. Gate: npm run check:design-system-contract plus the Links block in tests/ui-v2-components.dom.test.tsx. | session 2026-08-08 closing #263 / COMPONENTS section 0.4; commit e674f6e20 | 2026-08-07 | ## Resolved / archive @@ -468,5 +468,6 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th | #225 | issue | Favourites renders up to three EmptyState live regions at once | Resolved 2026-08-06: FavouritesEmptyMatches already rendered once outside breakpoint containers on main. | 2026-08-06 | | #246 | issue | `refetching` claims a CSS dimming that globals.css does not define | Resolved 2026-08-06: false CSS dimming claim already removed from search-results-header-band; pulsing dot is the intentional refetch signal. | 2026-08-06 | | #259 | task | Ledger row for claude/pr-handoff-loop-prevention-54y5zr (PR #1670) is still owed | Resolved 2026-08-07, and not by this session: a Cursor review-and-fix sweep on PR #1670 appended the row for claude/pr-handoff-loop-prevention-54y5zr at dfe2946110e0ff93bd4acc571ae79c26b79a7a85 (scope 'PR #1670 heavy review-and-fix'), and refined the #1649 row's checks cell to name the exact gates after a CodeRabbit finding. The structural observation this row was opened for still holds and is worth keeping: a handoff whose deliverable IS a ledger append cannot append its own row without a second commit to that head cancelling its in-flight CI. What resolved it here was a DIFFERENT session picking the row up in a sweep that was already touching the branch — which is the practical answer to the pattern, and cheaper than either paying for a CI restart or writing the row post-merge against the squash commit. | 2026-08-07 | +| #263 | task | DS Track A4: close the 13 open COMPONENTS section 0.4 defect rows | Closed on branch claude/ds-a4-component-defects (commit e674f6e20), one commit, all gates green. Eleven defects fixed across ten registered components: Button ref forwarding (+ a testId prop, because @types/react@19 gives components no data-${string} index signature); Progress indeterminate sweep onto the animate-shimmer theme token; StageList step index clamped to >=1 and announcement moved off the
          onto an sr-only role=status SIBLING (a child
        1. would make a five-stage job announce as 'list, 6 items'); StatusMark DocumentStatus declared in the component with the app row type asserted to conform, not the reverse; PageHeader title column floored at minmax(20ch,1fr) — the pre-existing wrap decided WHERE actions sat, not how wide, so the title still starved; Disclosure collapsed panel print:block (and the docstring's Ctrl-F claim was false); AccessibleTable dense header keeps its full string as title and the expander is now the registered Button; Tabs invalid value no longer empties the tab order (reachability only, no onChange fired to repair caller state); Pagination props clamped, row wraps at 320px, boundary focus handed to the current page, page announced via LiveAnnouncer; Links download type-omitted AND written after the spread, and LinkAction's hover nudge is a composited translate-x because gap is not in Tailwind's transition list so hover:gap-2 never eased; Checkbox/RadioGroup raw size-[1.125rem]/h-[2px] onto the spacing scale. THREE ROWS RE-MEASURED AS STALE and were not work: Checkbox/RadioGroup unsanitised ids (optionId already sanitises) and no group hint/error (fieldset already carries hint/error/describedBy). Button is now genuinely product-adopted by AccessibleTable with a real v2 mount, so it left the reference-only snapshot in tests/design-system-adoption.test.ts. Ratchets fell and none rose: edge conflicts 28->27, legacy shadow aliases 229->228; baseline deliberately NOT lowered — that is A3/A5 work and needs a full debt-baseline regeneration. Evidence: check:design-system-contract exit 0 (all three sub-checks); tsc --noEmit exit 0; lint exit 0 at --max-warnings 0; prettier --check . clean; 260 tests passed. Deliberately OUT of scope and still open: TextField/SearchField/Select (PR 7) and the ui-primitives.tsx module split (PR 12) were never in this task; Disclosure title truncation, StatusMark inline styles/raw geometry, Citation route/source modes, Links implicit new-tab policy, AccessibleTable content-role widths and the Button client boundary remain on their own rows. New finding worth a row: LinkAction accepts tone via BaseProps but never destructures it, so tone is silently ignored. | 2026-08-07 | From e09c2d1b986f751025229f412883a25c2647602b Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sat, 8 Aug 2026 02:38:45 +0800 Subject: [PATCH 3/8] docs(ledger): record the Track A4 component-defect handoff (PR #1712) Co-Authored-By: Claude Opus 5 --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index a44da0ff37..d2c29f1092 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -712,3 +712,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-07 | claude/handover-review-nlhuln | de8b74e2fb94d1ec9b1982c15a2dea43421c3eee | outstanding-issues ledger capture after the mode-nav rollout (PR #1685) | Confirmed #256's two remaining suspected section sets are dead (/forms/ and /differentials/presentations/ draw no section nav; form-decision-context-mobile is a testId not an id; ruled out sectionId indirection in both files). Added #261 (delete-or-keep the consumer-less action kind) and #262 (addon-slot single-owner rule held by two lists agreeing by coincidence). #207/#226/#231 reviewed and deliberately left untouched as existing P1 rows. | check:outstanding-issues passed (260 rows, 119 open, unique ids, no ids deleted from base 1ff9ed206456); prettier --check clean on the changed file; rows written via scripts/outstanding-issues.mjs, never hand-edited; no code gates run - docs-only diff | | 2026-08-07 | claude/handover-review-nlhuln | de8b74e2fb94d1ec9b1982c15a2dea43421c3eee | outstanding-issues ledger capture after the mode-nav rollout (PR #1685) (supersedes 2026-08-07) | Confirmed #256's two remaining suspected section sets are dead (/forms/ and /differentials/presentations/ draw no section nav; form-decision-context-mobile is a testId not an id; ruled out sectionId indirection in both files). Added #271 (delete-or-keep the consumer-less action kind) and #272 (addon-slot single-owner rule held by two lists agreeing by coincidence) — renumbered from this PR's original #261/#262 because main claimed #261-#270 via PR #1678 design-system tracks in the interim. #207/#226/#231 reviewed and deliberately left untouched as existing P1 rows. | check:outstanding-issues passed (270 rows, 129 open, 141 archived, unique ids, next-id=273 above the highest, no ids deleted from base d32dd549a3dd); prettier --check clean on the changed file; rows written via scripts/outstanding-issues.mjs, never hand-edited; no code gates run - docs-only diff | | 2026-08-07 | claude/search-bar-mobile-layout-buu0io | 9d64388c0ce530d0c20bb7efe8ffb32cd928319c | phone results-filter idiom: 7 modes off MobileResultFilterControl onto ResultFilterTrigger + ResultFilterSheet; band, docs, tests | changes-shipped | typecheck; lint; test 5538 passed (1 pre-existing pr-handoff-stop failure, baselined on unmodified tree); build; check:rag:fixtures; check:bundle-budget +6.3% within tolerance; targeted Playwright: ui-accessibility 16, ui-specifiers+ui-formulation 12, ui-tools 5, ui-smoke 2, ui-stress 3 | +| 2026-08-07 | claude/ds-a4-component-defects (PR #1712) | c6b2fbfd5ac45fe7f13fd786c40afa200a702051 | design-system Track A4 - COMPONENTS section 0.4 open-defect rows (#263) | Authored and self-verified, not an independent review. Eleven defects closed across ten registered components (Button ref+testId, Progress shimmer token, StageList step clamp and scoped live region, StatusMark app-type decoupling, PageHeader title floor, Disclosure print, AccessibleTable dense-header title and Button expander, Tabs invalid-value tab order, Pagination clamp/wrap/focus/announce, Links download and gap, Checkbox/RadioGroup raw dimensions). Three listed sub-defects re-measured as already fixed and were not work. Button left the reference-only adoption snapshot because AccessibleTable genuinely adopts it. Ratchets fell and none rose: edge conflicts 28 to 27, legacy shadow aliases 229 to 228; debt baseline deliberately not lowered. Merged origin/main (10 commits) and hand-resolved the docs/outstanding-issues.md conflict: id #273 had been taken on main, so the LinkAction finding was renumbered to #276; proved no id lost from either side and no duplicates. Auto-merge deliberately NOT armed - four surfaces change visibly and issue #118 means zero visual baselines, so CI compares no pixels; owner visual review requested before merge. | check:design-system-contract exit 0 (all three sub-checks); tsc --noEmit exit 0; lint exit 0 at --max-warnings 0; prettier --check . clean; verify:ui 407 passed (14.1m) exit 0 zero not-ok; 260 unit tests across 8 component/token suites plus 191 across 7 further suites; all re-run green after the main merge (135 passed). verify:pr-local aggregate NOT run - vitest fork workers would not start under box load; components run individually. | From ac748b0c528a20a7ea26d42056434c1c43b20480 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 8 Aug 2026 05:11:26 +0000 Subject: [PATCH 4/8] docs(ledger): record PR #1712 unblock sync from main Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 0f3358baf1..b192b26d04 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -727,3 +727,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-07 | cursor/fix-lighthouse-chrome-pin (PR #1703) | 621180854248fcc10982f3fd58762229fee999d0 | Run PR sweep: CI fix + threads + drift | GitHub reported dirty/conflicting mergeable_state but git merge-tree and a real test merge in a worktree were clean (stale mergeability). Merged origin/main directly and pushed. No unresolved review threads. | git merge-tree (clean), real worktree merge (clean, no conflicts) | | 2026-08-07 | cursor/document-citation-landing-7bc3 (PR #1705) | 8e62183dea5e07ac5ee4671d8d358937263c5de4 | Run PR sweep: CI fix + threads + drift | Merged origin/main (clean, just behind). Fixed the real CI failure (ui-smoke 'document viewer content disclosures...'): jumpToSection set inspectRevealKey when navigating to source-text but never cleared it navigating away, so IndexedTextPanel's React-controlled open prop stayed true and, sharing the native exclusive accordion group, silently closed whatever section was just navigated to. Same root cause independently flagged by Sentry and CodeRabbit review threads on this PR -- fixed once, replied to both, resolved both plus a 3rd (already-fixed) copilot thread. Declined to fix a 4th P3 CodeRabbit nitpick (edit an existing ledger row) since the ledger is append-only; replied with reasoning and resolved. | eslint on DocumentViewer.tsx (clean); local Playwright build blocked by environment-wide missing tailwind-merge dependency (Node 24.13.0 vs jsdom's required >=24.15.0, npm ci blocked by engine-strict) -- relying on CI | | 2026-08-07 | cursor/viewer-phase2c-rail-filmstrip-1db8 (PR #1707) | af52b592bd22794f5cf96cbca0fb27f2d6bbb3e3 | Run PR sweep: CI fix + threads + drift | Not behind main, no merge needed (mergeable_state 'blocked' was just the failing required check). Fixed the real Unit coverage CI failure: tests/document-image-filmstrip.dom.test.tsx still asserted the stale aria-current='true' after the component was already fixed to emit 'page' for an earlier a11y review finding -- exactly matched an unresolved CodeRabbit finding, applied its suggested fix. Also fixed an unresolved LOW-severity Sentry finding (image metadata line could start with a leading ' · ' separator when image_type is falsy) by collecting parts into an array and filter+join instead of individually prefixing. All 4 review threads now resolved (2 new fixes + 2 already-fixed-on-branch copilot threads). | eslint on both fixed files (clean); local vitest blocked by environment-wide missing tailwind-merge dependency -- relying on CI | +| 2026-08-08 | claude/ds-a4-component-defects | a029a543f744eb80e608ec482aacdbdc5f5599c2 | unblock PR #1712 | Merged origin/main (ef28960e) to clear dirty mergeable_state: real conflict in docs/branch-review-ledger.md auto-merged via merge=ledger driver. Prior tip 9ba483d3 was 1 behind main. Static PR and PR required failures were dirty-state blockers (GitHub could not build refs/pull/1712/merge). Proved post-merge: merge-tree clean, check:branch-review-ledger, check:design-system-contract. | merge-tree clean; ledger:dedupe; check:branch-review-ledger; check:design-system-contract | From 3b15fc1f9fcb913c1681480f6edc3bcbfc1978ec Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 8 Aug 2026 05:27:06 +0000 Subject: [PATCH 5/8] fix(ui): use size-5 for checkbox/radio boxes to pass icon-scale guard The A4 defect fix moved checkbox/radio boxes from size-[1.125rem] to size-4.5, but check:icon-scale --strict flags the retired 4.5 half-step (18px). Use size-5 (20px integer spacing step) per the guard's documented snap for non-icon boxes. Co-authored-by: BigSimmo --- docs/design-system/COMPONENTS.md | 2 +- src/components/ui/choice.tsx | 10 +++++----- tests/ui-v2-components.dom.test.tsx | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/design-system/COMPONENTS.md b/docs/design-system/COMPONENTS.md index ae44d078a3..bdb5f23c9b 100644 --- a/docs/design-system/COMPONENTS.md +++ b/docs/design-system/COMPONENTS.md @@ -68,7 +68,7 @@ print primitives (`PrintHeader`, `PrintFooter`, `CitationFootnote`, `PrintOnly`, | ToggleSwitch | operable branch requires `aria-label`; opacity disabled retired; knob now travels on `transform` (`translate-x-4`), tokenised duration, reduced-motion opt-out | **done** (motion) | | Chip | final `appearance`/size API, removable label contract, tap target and full-value title | **done** | | TextField/SearchField/Select | hint dropped on error (comment promises otherwise) · describedBy overwritten · no external id/refs (placeholder off decoration tier in PR-A) | PR 7 | -| Checkbox/RadioGroup | all four closed. Two rows were stale when re-measured on 2026-08-08: ids already sanitise through `optionId`, and the group already carries `hint`/`error`/`describedBy`. Raw dimensions moved to `size-4.5`/`h-0.5` | **done** | +| Checkbox/RadioGroup | all four closed. Two rows were stale when re-measured on 2026-08-08: ids already sanitise through `optionId`, and the group already carries `hint`/`error`/`describedBy`. Raw dimensions moved to `size-5`/`h-0.5` (`size-4.5` retired by `check:icon-scale`) | **done** | | Citation/CitationList | required interactive handler, stable-id list keys and static-span labelling (`aria-label` on a role-less span was dropped, losing the currency phrase on print) are resolved; route/source modes remain | follow-on | | DoseLine | must compose `Quantity` · structured dose model · overdue text + non-colour mark + open action | **done** (PR 6) | | StatusMark | app-type coupling resolved — `DocumentStatus` is declared here and `@/lib/types` conforms to it (asserted at compile time in the DOM suite), not the reverse; inline styles/raw geometry remain | PR 12 | diff --git a/src/components/ui/choice.tsx b/src/components/ui/choice.tsx index e9fffe64c1..beee0c8f9e 100644 --- a/src/components/ui/choice.tsx +++ b/src/components/ui/choice.tsx @@ -22,11 +22,11 @@ import { cn, textMuted } from "@/components/ui-primitives"; * form participation, `:indeterminate`, and correct screen-reader roles for free. */ -// `size-4.5` / `h-0.5` below, not `size-[1.125rem]` / `h-[2px]`: same rendered -// 18px box and 2px mixed-state dash, but expressed on the spacing scale instead -// of as arbitrary literals, so they move with `--spacing` rather than pinning -// two magic numbers no other control shares. -const boxBase = "grid size-4.5 shrink-0 place-items-center rounded-xs border transition motion-reduce:transition-none"; +// `size-5` / `h-0.5` below, not `size-[1.125rem]` / `h-[2px]`: the retired +// `4.5` half-step (18px) is off the icon/spacing guard (`check:icon-scale`); +// `size-5` (20px) is the documented snap for non-icon boxes. Mixed-state dash +// stays `h-0.5` on the spacing scale instead of `h-[2px]`. +const boxBase = "grid size-5 shrink-0 place-items-center rounded-xs border transition motion-reduce:transition-none"; /** * Option ids are derived from a sanitised key, never the raw value (COMPONENTS diff --git a/tests/ui-v2-components.dom.test.tsx b/tests/ui-v2-components.dom.test.tsx index 8e47d7f258..5b43933f22 100644 --- a/tests/ui-v2-components.dom.test.tsx +++ b/tests/ui-v2-components.dom.test.tsx @@ -1223,10 +1223,11 @@ describe("Checkbox / RadioGroup — dimensions on the spacing scale", () => { it("sizes the checkbox box and its mixed-state dash without arbitrary literals", () => { const { container } = render(); - const box = container.querySelector(".grid.size-4\\.5"); + const box = container.querySelector(".grid.size-5"); expect(box).not.toBeNull(); expect(container.innerHTML).not.toContain("size-[1.125rem]"); expect(container.innerHTML).not.toContain("h-[2px]"); + expect(container.innerHTML).not.toContain("size-4.5"); }); it("keeps the radio control on the same box recipe", () => { @@ -1239,7 +1240,7 @@ describe("Checkbox / RadioGroup — dimensions on the spacing scale", () => { />, ); - expect(container.querySelector(".size-4\\.5")).not.toBeNull(); + expect(container.querySelector(".size-5")).not.toBeNull(); expect(container.innerHTML).not.toContain("size-[1.125rem]"); }); }); From 41d1bad89f3dbd8a5284f67918aded28b80cb230 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 8 Aug 2026 05:33:14 +0000 Subject: [PATCH 6/8] fix(ui): address CodeRabbit review on PR #1712 components - Pagination: announce and restore focus only after current commits to pending target; regression when onPageChange rejects the page - StageList: treat failed stage as current when nothing is active - Tabs: derive aria-selected from selectedId so disabled value tabs stay unselected - StatusMark/Tabs/StageList/Pagination regression tests per review - outstanding-issues: add #276 to recommended queue, fix #263 archive overstated verification, align #276 session date Co-authored-by: BigSimmo --- docs/outstanding-issues.md | 5 +-- src/components/ui/pagination.tsx | 29 ++++++++++++---- src/components/ui/progress.tsx | 17 ++++++---- src/components/ui/tabs.tsx | 2 +- tests/ui-v2-components.dom.test.tsx | 51 ++++++++++++++++++++++++++--- 5 files changed, 83 insertions(+), 21 deletions(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 4f054d4288..060cb337ad 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -163,6 +163,7 @@ removed after current-main verification; it is not missing recommended work. | 110 | `#255` | A2 | High — Cloud/browser gates | Next environment image update | 2–4 hours | Align Cloud Playwright browser builds with lockfile pin; document CI delegation until then. **Stop:** do not force mismatched Chromium revisions. | | 111 | `#256` | A2 | High — mode section nav | Next information-page / mode-nav pass | 2–4 hours | Declared information-page section sets whose target ids nothing renders — verify each set against the rendered DOM per route; render anchors or delete the set. **Stop:** do not audit by grepping for `id=` alone (sectionId props exist). | | 112 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | +| 113 | `#276` | Optional | High — design-system links | Next LinkAction tone follow-up | 30–60 min | LinkAction accepts `tone` via `BaseProps` but never reads it, so `tone="inherit"` is silently ignored while sibling links honour tone. Decide honour vs refuse on `LinkActionProps`; refusing is probably right for a forward-action link. **Gate:** `npm run check:design-system-contract` plus the Links block in `tests/ui-v2-components.dom.test.tsx`. **Stop:** do not bundle with unrelated link policy work (#263 follow-on rows). | @@ -313,7 +314,7 @@ removed after current-main verification; it is not missing recommended work. | #273 | P2 | task | The results band's max-[413px] wrap threshold is stale now that Sort and the phone selects are both gone | **Outcome:** the one-line phone results bar extends down to 320px instead of stopping at 414px, so the Filter trigger stops taking an otherwise-empty second row on the most common phone widths. **Detail:** `search-results-header-band.tsx` wraps the band's first line with `max-[413px]:flex-wrap max-[413px]:py-2`, justified in its own comment as "below 414px one line provably cannot hold count + query + sort + filter even with the query fully truncated". Both halves of that premise have since been removed: Sort became `sm`-and-up (PR #1689), and every mode's phone control became a compact trigger rather than a `w-full` select (#247). Measured in a real browser on the differentials band with the wrap class stripped at runtime, `scrollWidth - clientWidth` is **0 at 320, 360, 375, 390 and 402px** — the line fits at every width with room to spare. Left unchanged deliberately: the fix also changes documents, which is the reference layout the user approved, and it is a shared-geometry change with a 320-540px clip sweep asserting against it in `ui-smoke`. **Next:** delete the two `max-[413px]:` utilities, re-run the `ui-smoke` clip sweep and `ui-tools` phone rail assertions, and re-measure band height at 320/360/390 (expect 60px everywhere, versus 89px today below 414). **Stop:** do not raise the threshold instead of deleting it — it is not a narrower window now, it is an empty one. Do not re-measure with Sort mentally re-added; that control is gone below `sm` and is not coming back. | browser measurement 2026-08-07 on claude/search-bar-mobile-layout-buu0io; band comment; #247 | 2026-08-07 | | #274 | P2 | task | Services' Clear quick filters leaves the q param in the URL, so a reload restores the filter | **Outcome:** clearing a services quick filter survives a reload. **Detail:** `services-navigator-page.tsx`'s Clear runs `setLocalQuery({ urlQuery, value: "" })`, which clears local state only; the route still carries `?q=crisis`, so a remount or reload re-derives `activeQuickFilter` and the filter comes back. Raised by CodeRabbit against the phone sheet's `onClearAll` in PR #1706, but the phone path was deliberately wired to the *same* handler the desktop Clear button already used — the behaviour is pre-existing on desktop and was mirrored, not introduced. Fixing it in that PR would have changed a shipped desktop control's semantics under a phone-scoped change, so it was deferred rather than bundled. **Next:** add one clear path that both resets local state and pushes a route without `q` (or with the mode's default query), and point both the desktop Clear and the sheet's `onClearAll` at it; assert the URL after Clear in `ui-tools`. **Stop:** do not fix only the phone path — two Clear buttons that disagree about whether the URL changes is worse than one that is consistently wrong. | CodeRabbit review on PR #1706; services-navigator-page.tsx Clear handler | 2026-08-07 | | #275 | P2 | task | The shared filter trigger carries arbitrary spacing values inherited from DocumentFilterTrigger | **Outcome:** the phone filter trigger expresses its measurements as named tokens rather than bracketed values. **Detail:** `result-filter-control.tsx`'s `ResultFilterTrigger` uses `pr-[0.6875rem]`, `h-[1.0625rem]`/`min-w-[1.0625rem]` for the badge, and the raw breakpoint window `min-[414px]:max-[429px]` for the label. CodeRabbit flagged these against the design-token rule in PR #1706. Every one of them is copied *verbatim* from `DocumentFilterTrigger`, which shipped on main earlier and is the component this one was deliberately lifted from so the two cannot drift — so the finding is real but its scope is both call sites, not the new one. Changing only the copy would reintroduce exactly the drift the extraction removed, and each value carries a measured justification in its own comment (the asymmetric padding answers a stroked glyph against a filled pill; the breakpoint window is the one band that is single-line and short of width). **Next:** tokenise in `@theme` once, then update the trigger — there is now only one implementation, so it is a single edit. Confirm the badge and padding render identically at 393/402/414/430px before and after. **Stop:** do not tokenise the trigger without also retiring the values from the documents original, and do not treat this as licence to change the measurements themselves. | CodeRabbit review on PR #1706; DocumentFilterTrigger on main | 2026-08-07 | -| #276 | P3 | issue | LinkAction accepts a tone prop it never reads | src/components/ui/link.tsx: LinkActionProps = BaseProps & { href: string }, and BaseProps declares tone?: 'accent' \| 'inherit'. LinkAction destructures only { href, children, className }, so a call site can pass tone='inherit' and get the accent styling anyway, with no type error and no DOM leak to notice it by. The three sibling links (TextLink, ExternalTextLink, DownloadLink) all honour tone, so this is an inconsistency inside one module rather than a deliberate omission. Found while closing #263 (Track A4) and left alone because it is a behaviour change to a link's colour on adopted surfaces, not a defect on the #263 list. Next: decide whether LinkAction should honour tone (apply toneClass[tone] like its siblings) or refuse it (drop BaseProps' tone from LinkActionProps so the compiler rejects the call). Refusing is probably right — a forward-action link is always the accent by design — and is the smaller diff. Gate: npm run check:design-system-contract plus the Links block in tests/ui-v2-components.dom.test.tsx. | session 2026-08-08 closing #263 / COMPONENTS section 0.4; commit e674f6e20 | 2026-08-07 | +| #276 | P3 | issue | LinkAction accepts a tone prop it never reads | src/components/ui/link.tsx: LinkActionProps = BaseProps & { href: string }, and BaseProps declares tone?: 'accent' \| 'inherit'. LinkAction destructures only { href, children, className }, so a call site can pass tone='inherit' and get the accent styling anyway, with no type error and no DOM leak to notice it by. The three sibling links (TextLink, ExternalTextLink, DownloadLink) all honour tone, so this is an inconsistency inside one module rather than a deliberate omission. Found while closing #263 (Track A4) and left alone because it is a behaviour change to a link's colour on adopted surfaces, not a defect on the #263 list. Next: decide whether LinkAction should honour tone (apply toneClass[tone] like its siblings) or refuse it (drop BaseProps' tone from LinkActionProps so the compiler rejects the call). Refusing is probably right — a forward-action link is always the accent by design — and is the smaller diff. Gate: npm run check:design-system-contract plus the Links block in tests/ui-v2-components.dom.test.tsx. | session 2026-08-07 closing #263 / COMPONENTS section 0.4; commit e674f6e20 | 2026-08-07 | ## Resolved / archive @@ -470,6 +471,6 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th | #246 | issue | `refetching` claims a CSS dimming that globals.css does not define | Resolved 2026-08-06: false CSS dimming claim already removed from search-results-header-band; pulsing dot is the intentional refetch signal. | 2026-08-06 | | #259 | task | Ledger row for claude/pr-handoff-loop-prevention-54y5zr (PR #1670) is still owed | Resolved 2026-08-07, and not by this session: a Cursor review-and-fix sweep on PR #1670 appended the row for claude/pr-handoff-loop-prevention-54y5zr at dfe2946110e0ff93bd4acc571ae79c26b79a7a85 (scope 'PR #1670 heavy review-and-fix'), and refined the #1649 row's checks cell to name the exact gates after a CodeRabbit finding. The structural observation this row was opened for still holds and is worth keeping: a handoff whose deliverable IS a ledger append cannot append its own row without a second commit to that head cancelling its in-flight CI. What resolved it here was a DIFFERENT session picking the row up in a sweep that was already touching the branch — which is the practical answer to the pattern, and cheaper than either paying for a CI restart or writing the row post-merge against the squash commit. | 2026-08-07 | | #247 | task | Widen the one-line results bar to the six modes that pass a full-width phone select | RESOLVED 2026-08-07. All seven surfaces that shipped a full-width phone select now pass one compact badged trigger and opt into `mobileControlsPlacement="inline"`, so the one-line results bar is universal rather than a documents/therapy-compass exception. Converted: differentials, services, factsheets, prescribing, and the tools launcher (one select each) plus formulation and specifiers (two selects each, in the two-column grid this row named as the hard case). `MobileResultFilterControl` is deleted — no caller remains. The shared idiom lives in `src/components/clinical-dashboard/result-filter-control.tsx`: `ResultFilterTrigger` (lifted verbatim from `DocumentFilterTrigger`, so the control is the same component everywhere) and `ResultFilterSheet`, which renders one `role="radiogroup"` per dimension because these are genuinely one-of-N. Documents keeps its own panel — multi-select facet groups with counts, a find-a-filter field and collapse-by-default are not radios. Desktop is untouched: the ribbon renders `filterControls` from `sm` up and `mobileControls` below it, never both, so every mode keeps its chip row or tab strip on a wide screen. Verified in a real browser at 390px: the differentials band is geometrically identical to the documents band (89px at 390, 60px at 414 and 430). The "Stop" in this row is honoured — `mobileControlsPlacement` still defaults to `row`; nothing relies on that fallback now, and it stays so a mode that forgets the prop degrades to a second row rather than to an unreadable line. Follow-up captured separately: the `max-[413px]:flex-wrap` threshold was measured when Sort still occupied the phone line and is now stale — one line fits with zero overflow at 320/360/375/390/402px. | 2026-08-07 | -| #263 | task | DS Track A4: close the 13 open COMPONENTS section 0.4 defect rows | Closed on branch claude/ds-a4-component-defects (commit e674f6e20), one commit, all gates green. Eleven defects fixed across ten registered components: Button ref forwarding (+ a testId prop, because @types/react@19 gives components no data-${string} index signature); Progress indeterminate sweep onto the animate-shimmer theme token; StageList step index clamped to >=1 and announcement moved off the
            onto an sr-only role=status SIBLING (a child
          1. would make a five-stage job announce as 'list, 6 items'); StatusMark DocumentStatus declared in the component with the app row type asserted to conform, not the reverse; PageHeader title column floored at minmax(20ch,1fr) — the pre-existing wrap decided WHERE actions sat, not how wide, so the title still starved; Disclosure collapsed panel print:block (and the docstring's Ctrl-F claim was false); AccessibleTable dense header keeps its full string as title and the expander is now the registered Button; Tabs invalid value no longer empties the tab order (reachability only, no onChange fired to repair caller state); Pagination props clamped, row wraps at 320px, boundary focus handed to the current page, page announced via LiveAnnouncer; Links download type-omitted AND written after the spread, and LinkAction's hover nudge is a composited translate-x because gap is not in Tailwind's transition list so hover:gap-2 never eased; Checkbox/RadioGroup raw size-[1.125rem]/h-[2px] onto the spacing scale. THREE ROWS RE-MEASURED AS STALE and were not work: Checkbox/RadioGroup unsanitised ids (optionId already sanitises) and no group hint/error (fieldset already carries hint/error/describedBy). Button is now genuinely product-adopted by AccessibleTable with a real v2 mount, so it left the reference-only snapshot in tests/design-system-adoption.test.ts. Ratchets fell and none rose: edge conflicts 28->27, legacy shadow aliases 229->228; baseline deliberately NOT lowered — that is A3/A5 work and needs a full debt-baseline regeneration. Evidence: check:design-system-contract exit 0 (all three sub-checks); tsc --noEmit exit 0; lint exit 0 at --max-warnings 0; prettier --check . clean; 260 tests passed. Deliberately OUT of scope and still open: TextField/SearchField/Select (PR 7) and the ui-primitives.tsx module split (PR 12) were never in this task; Disclosure title truncation, StatusMark inline styles/raw geometry, Citation route/source modes, Links implicit new-tab policy, AccessibleTable content-role widths and the Button client boundary remain on their own rows. New finding worth a row: LinkAction accepts tone via BaseProps but never destructures it, so tone is silently ignored. | 2026-08-07 | +| #263 | task | DS Track A4: close the 13 open COMPONENTS section 0.4 defect rows | Closed on branch claude/ds-a4-component-defects (commit e674f6e20). Eleven defects fixed across ten registered components: Button ref forwarding (+ a testId prop, because @types/react@19 gives components no data-${string} index signature); Progress indeterminate sweep onto the animate-shimmer theme token; StageList step index clamped to >=1 and announcement moved off the
              onto an sr-only role=status SIBLING (a child
            1. would make a five-stage job announce as 'list, 6 items'); StatusMark DocumentStatus declared in the component with the app row type asserted to conform, not the reverse; PageHeader title column floored at minmax(20ch,1fr) — the pre-existing wrap decided WHERE actions sat, not how wide, so the title still starved; Disclosure collapsed panel print:block (and the docstring's Ctrl-F claim was false); AccessibleTable dense header keeps its full string as title and the expander is now the registered Button; Tabs invalid value no longer empties the tab order (reachability only, no onChange fired to repair caller state); Pagination props clamped, row wraps at 320px, boundary focus handed to the current page, page announced via LiveAnnouncer; Links download type-omitted AND written after the spread, and LinkAction's hover nudge is a composited translate-x because gap is not in Tailwind's transition list so hover:gap-2 never eased; Checkbox/RadioGroup raw size-[1.125rem]/h-[2px] onto size-5/h-0.5 (size-4.5 retired by check:icon-scale). THREE ROWS RE-MEASURED AS STALE and were not work: Checkbox/RadioGroup unsanitised ids (optionId already sanitises) and no group hint/error (fieldset already carries hint/error/describedBy). Button is now genuinely product-adopted by AccessibleTable with a real v2 mount, so it left the reference-only snapshot in tests/design-system-adoption.test.ts. Ratchets fell and none rose: edge conflicts 28->27, legacy shadow aliases 229->228; baseline deliberately NOT lowered — that is A3/A5 work and needs a full debt-baseline regeneration. Evidence executed: check:design-system-contract exit 0 (all three sub-checks); tsc --noEmit exit 0; lint exit 0 at --max-warnings 0; prettier --check . clean; verify:ui 407 passed (14.1m) exit 0; 260+191 unit tests across component suites. npm run verify:pr-local aggregate NOT run — Vitest fork workers unreliable under box load; components run individually. Deliberately OUT of scope and still open: TextField/SearchField/Select (PR 7) and the ui-primitives.tsx module split (PR 12) were never in this task; Disclosure title truncation, StatusMark inline styles/raw geometry, Citation route/source modes, Links implicit new-tab policy, AccessibleTable content-role widths and the Button client boundary remain on their own rows. New finding worth a row: LinkAction accepts tone via BaseProps but never destructures it, so tone is silently ignored (#276). | 2026-08-07 | diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx index 66f848c72b..da20633475 100644 --- a/src/components/ui/pagination.tsx +++ b/src/components/ui/pagination.tsx @@ -48,14 +48,22 @@ export function Pagination({ const current = Number.isFinite(page) ? Math.min(Math.max(1, Math.floor(page)), Math.max(total, 1)) : 1; const currentRef = useRef(null); + const currentValueRef = useRef(current); + currentValueRef.current = current; // Set when the control the user just activated is about to disable itself. const restoreFocus = useRef(false); + const pendingTarget = useRef(null); useEffect(() => { - if (!restoreFocus.current) return; - restoreFocus.current = false; - currentRef.current?.focus(); - }, [current]); + const target = pendingTarget.current; + if (target === null || current !== target) return; + if (restoreFocus.current) { + restoreFocus.current = false; + currentRef.current?.focus(); + } + announce(`Page ${current} of ${total}`, { eventId: `pagination:${label}` }); + pendingTarget.current = null; + }, [current, total, label]); if (total <= 1) return null; const items = pageWindow(current, total); @@ -70,10 +78,17 @@ export function Pagination({ // current-page button, which is always rendered (1 and `total` are both // permanent members of the window). if (target <= 1 || target >= total) restoreFocus.current = true; + pendingTarget.current = target; onPageChange(target); - // The page number is the whole state change and nothing visible announces - // it: `aria-current` moves silently, and the summary line is not live. - announce(`Page ${target} of ${total}`, { eventId: `pagination:${label}` }); + // `onPageChange` does not guarantee the parent accepts `target`. Announce and + // restore focus only after `current` commits to the pending page; if the + // parent rejects the change, clear the pending state without announcing. + queueMicrotask(() => { + if (pendingTarget.current === target && currentValueRef.current !== target) { + restoreFocus.current = false; + pendingTarget.current = null; + } + }); } const step = diff --git a/src/components/ui/progress.tsx b/src/components/ui/progress.tsx index bb573d3240..a323085c9b 100644 --- a/src/components/ui/progress.tsx +++ b/src/components/ui/progress.tsx @@ -92,12 +92,15 @@ export type StageListProps = { */ export function StageList({ stages, label = "Progress", className }: StageListProps) { const activeIndex = stages.findIndex((stage) => stage.state === "active"); + const failedIndex = stages.findIndex((stage) => stage.state === "failed"); + const currentIndex = activeIndex >= 0 ? activeIndex : failedIndex; const doneCount = stages.filter((stage) => stage.state === "done").length; - // Clamp to 1: with nothing started `activeIndex` is -1 and `doneCount` is 0, - // which announced "step 0 of 5". There is no step zero — a job that has not - // begun is at its first step, not before it. - const step = stages.length ? Math.min(Math.max(activeIndex + 1, doneCount, 1), stages.length) : 0; - const currentStage = activeIndex >= 0 ? stages[activeIndex] : undefined; + const step = stages.length + ? currentIndex >= 0 + ? currentIndex + 1 + : Math.min(Math.max(doneCount, 1), stages.length) + : 0; + const currentStage = currentIndex >= 0 ? stages[currentIndex] : undefined; return ( <> @@ -117,7 +120,7 @@ export function StageList({ stages, label = "Progress", className }: StageListPr
            2. = 0 ? "step" : undefined} className="relative flex items-start gap-3 pb-3 last:pb-0" > {/* Connector owned by the gutter column, stopping at the dot centres. */} @@ -125,7 +128,7 @@ export function StageList({ stages, label = "Progress", className }: StageListPr diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index d2c3ddcc2e..4969da0666 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -86,7 +86,7 @@ export function Tabs({ items, value, onChange, label, className, children }: Tab className="flex min-w-0 items-center gap-1 overflow-x-auto border-b border-[color:var(--border)]" > {items.map((item) => { - const selected = item.id === value; + const selected = item.id === selectedId; const Icon = item.icon; return (