Skip to content

Primer API Review #8384

Description

@github-actions

Summary

Review date: 2026-09-10. This run is a partial audit: 32 of 80 components (78 inventoried directories plus the publicly-exported SideNav/VisuallyHidden, which are missing from the inventory file) are now fully reviewed against all 21 style-guide principles — 20 carried forward unchanged from the prior run, plus 12 newly reviewed this run (Details, Dialog, FeatureFlags, Flash, FormControl via component-api-auditor; Header, Heading, Hidden, InlineMessage, KeybindingHint via a second component-api-auditor batch; SideNav and VisuallyHidden via direct inspection).

Proposed API changes

  • Dialog.onClose uses a bare positional string instead of an extensible object (finding)
  • Dialog has no rest-parameter pass-through to its root element (finding)
  • Dialog's inline style merge places consumer style before the component-owned --dialog-width CSS variable, inverting the documented merge order (finding)
  • Dialog's footer-button auto-focus runs in an effect/state-ratchet instead of an event handler (finding)
  • useDetails returns a ref for the caller to apply instead of only accepting one (finding)
  • useDetails's onClickOutside callback sits directly in an effect dependency array instead of a stabilized ref (finding)
  • FormControl has no rest-parameter pass-through to its root element (finding)
  • FormControlLabel.visuallyHidden is a part-visibility toggle that doesn't follow hide/show naming (finding)
  • Header/Header.Item/Header.Link spread rest props before the hard-coded data-component, letting a consumer-supplied data-component silently win (finding)
  • Heading.variant communicates typographic scale (appearance), not semantic purpose (finding)
  • Hidden has no rest-parameter pass-through to its root element (finding)
  • InlineMessage spreads rest props before its own data-size/data-variant/data-component, letting a consumer override them silently (finding)
  • KeybindingHint has no rest-parameter pass-through to its root element (finding)
  • KeybindingHint.size uses 'normal' instead of the guide's standard 'medium' scale name (finding)
  • SideNav.variant (lightweight/normal) communicates padding/border density (appearance), not purpose (finding)
  • Breadcrumbs root has no rest/passthrough props (finding)
  • Breadcrumbs.Item selected uses an inert bare string class instead of data-selected (finding)
  • Breadcrumbs variant="spacious" communicates padding/density, not purpose (finding)
  • Button/ButtonBase variant mixes appearance names (invisible, link) with semantic ones (primary, danger) (finding)
  • ButtonBase drops the consumer's onClick entirely while loading is true, instead of merging (finding)
  • ButtonGroup silently discards a consumer-forwarded data-component via rest-spread ordering (finding)
  • Card computes aria-labelledby via direct mutation of rest instead of intentional merge precedence (finding)
  • Checkbox forwards checked/defaultChecked to the native <input> without useControllableState (finding)
  • Checkbox.onChange still fires when a group-level onChange calls preventDefault() (finding)
  • CheckboxGroup.onChange uses two positional args instead of one extensible object (finding)
  • CheckboxGroup skips its internal selection-state update entirely when no onChange is provided (finding)
  • CircleBadge.variant duplicates size's scale/appearance role (finding)
  • CircleBadge silently overwrites consumer-supplied style instead of merging it (finding)
  • ConfirmationDialog accepts no rest params to forward to its underlying Dialog root (finding)
  • ConfirmationDialog.onClose uses a bare positional string instead of an extensible object (finding)
  • CounterLabel's deprecated scheme prop still duplicates variant as two mode props for one concept (finding)
  • DataTable.onToggleSort uses two positional args instead of one extensible object (finding)
  • DataTable.Title/DataTable.Subtitle type as accepting full HTMLAttributes but silently drop unrecognized attributes at runtime (finding)
  • Pagination.onPageChange uses bare positional args instead of an extensible object (finding)
  • PageLayout DragHandle.onDrag uses bare positional args instead of an extensible object (finding)
  • ActionBar root has no rest/passthrough props (finding)
  • AvatarStack root has no rest/passthrough props (finding)
  • Blankslate applies rest params to the wrong element (finding)
  • ActionMenu.onOpenChange uses a bare positional boolean (finding)
  • AnchoredOverlay onOpen/onClose use multiple/bare positional arguments (finding)
  • ActionMenu open/onOpenChange use useProvidedStateOrCreate instead of useControllableState (finding)
  • Autocomplete syncs controlled value via an effect instead of useControllableState (finding)
  • ActionList List.variant communicates layout/appearance, not purpose (finding)
  • ActionList.Item size scale omits small (finding)
  • AvatarStack.variant (cascade/stack) communicates appearance, not purpose (finding)
  • ActionList.disableFocusZone is a negated, verb-prefixed boolean (finding)
  • AnchoredOverlay.displayCloseButton should follow durable-default/hide-show naming (finding)
  • AvatarStack.disableExpand mirrors the guide's explicit anti-pattern (finding)
  • Autocomplete restores selection range in an effect rather than the triggering handler (finding)
Run details and coverage

Component counts for this run's newly-reviewed set (12 directories; all 21 principles inspected):

Component pass finding not-applicable
Details (+useDetails) 15 2 4
Dialog 13 4 4
FeatureFlags 4 0 17
Flash 8 0 13
FormControl 13 2 6
Header 15 1 5
Heading 13 2 6
Hidden 12 1 8
InlineMessage 15 1 5
KeybindingHint 14 2 5
SideNav (direct inspection; publicly exported, missing from inventory file) 15 2 4
VisuallyHidden (direct inspection; publicly exported, missing from inventory file) 21 0 0
Breadcrumbs, Button (+ButtonBase), ButtonGroup, Card, Checkbox, CheckboxGroup, CircleBadge, ConfirmationDialog, CounterLabel, DataTable, ActionBar, ActionList, ActionMenu, AnchoredOverlay, Autocomplete, Avatar, AvatarStack, Banner, Blankslate, BranchName carried forward unchanged from prior run (full 21-principle coverage, not rechecked against current source this run)

Evidence sources this run: (1) component-api-auditor sub-agent, batch 1 (Details, Dialog, FeatureFlags, Flash, FormControl) — returned a full 21-principle matrix per component with file:line evidence; spot-checked directly against Dialog.tsx, useDetails.tsx, and FormControlLabel.tsx source and confirmed; (2) component-api-auditor sub-agent, batch 2 (Header, Heading, Hidden, InlineMessage, KeybindingHint) — returned a full matrix per component; spot-checked directly against Header.tsx, Heading.tsx, and InlineMessage.tsx source and confirmed; (3) direct inspection for SideNav and VisuallyHidden, publicly exported from packages/react/src/index.ts but absent from /tmp/gh-aw/data/components.json.

Workflow run: §34501498984

Remaining coverage

Prioritized next bounded batches (5 components each), carried forward from the prior run's list (adjusted to remove now-reviewed components):

  1. Label, LabelGroup, Link, NavList, Octicon
  2. Overlay, PageHeader, PageLayout, Pagehead, Pagination
  3. Popover, Portal, ProgressBar, Radio, RadioGroup
  4. RelativeTime, SegmentedControl, Select, SelectPanel, Skeleton
  5. SkeletonAvatar, SkeletonText, Spinner, SplitPageLayout, Stack
  6. StateLabel, SubNav, TabNav, Text, TextInput
  7. TextInputWithTokens, Textarea, Timeline, ToggleSwitch, Token
  8. Tooltip, TooltipV2, TopicTag, TreeView, Truncate
  9. UnderlineNav, deprecated/DialogV1, deprecated/FilteredSearch, deprecated/UnderlineNav, experimental/IssueLabel
  10. experimental/SelectPanel2, experimental/UnderlinePanels, live-region

Note: PageLayout's DragHandle finding is retained from the prior run; batch 2 above should re-verify the rest of PageLayout, Pagehead, and Pagination (currently only Pagination.onPageChange and the DragHandle finding have been checked) against the full 21-principle checklist.

Past findings not rechecked

The following components were carried forward unchanged from the prior run without re-inspection this run: Breadcrumbs, Button (+ButtonBase), ButtonGroup, Card, Checkbox, CheckboxGroup, CircleBadge, ConfirmationDialog, CounterLabel, DataTable, ActionBar, ActionList, ActionMenu, AnchoredOverlay, Autocomplete, Avatar, AvatarStack, Banner, Blankslate, BranchName. Their findings remain listed in "Proposed API changes" above and their evidence is preserved in each component's managed comment.

No new deviations beyond those listed above were found in the 12 components newly reviewed this run. The remaining 48 directories have not been reviewed against the full checklist this run and should not be assumed to pass.


Summary

Review date: 2026-09-14. This run completes the audit: all 83 publicly-exported component surfaces are now reviewed against the full 21-principle style guide checklist (78 inventoried directories, plus SideNav/VisuallyHidden/FilteredActionList/ScrollableRegion/experimental/Tabs, which are publicly exported but missing from the inventory file). This run directly reviewed the 51 components/directories not yet covered (Label, LabelGroup, Link, NavList, Octicon, Overlay, PageHeader, PageLayout, Pagehead, Pagination, Popover, Portal, ProgressBar, Radio, RadioGroup, RelativeTime, SegmentedControl, Select, SelectPanel, Skeleton, SkeletonAvatar, SkeletonText, Spinner, SplitPageLayout, Stack, StateLabel, SubNav, TabNav, Text, TextInput, TextInputWithTokens, Textarea, Timeline, ToggleSwitch, Token, TopicTag, TreeView, Truncate, UnderlineNav, deprecated/DialogV1, deprecated/FilteredSearch, deprecated/UnderlineNav, experimental/IssueLabel, experimental/SelectPanel2, experimental/UnderlinePanels, live-region, FilteredActionList, ScrollableRegion, experimental/Tabs); the remaining 32 were carried forward from the prior run (20 unchanged since the original review, 12 reviewed last run with findings preserved).

Proposed API changes

Merge shared props intentionally (contributor-docs/style.md)

  • NavList.Heading, NavList.Item, and NavList.GroupHeading spread {...props}/{...rest} after their own literal data-component, letting a consumer-supplied data-component silently override it (packages/react/src/NavList/NavList.tsx:110-117 (Heading), :186-191 (Item), :541-547 (GroupHeading))
  • PageHeader.ParentLink spreads {...rest} after its literal data-component="PageHeader.ParentLink" (packages/react/src/PageHeader/PageHeader.tsx:222-226)
  • Popover.Content (the inner div rendered by Popover) spreads {...props} after data-component="Popover.Content" (packages/react/src/Popover/Popover.tsx:106-113)
  • ProgressBar.Item and ProgressBar (root) both spread {...rest} after their own literal data-component (packages/react/src/ProgressBar/ProgressBar.tsx:56-59 (Item), :104-109 (root))
  • SkeletonText's multi-line render path spreads {...rest} after its literal data-component="SkeletonText" (packages/react/src/SkeletonText/SkeletonText.tsx:47-52)
  • SplitPageLayout.Root, .Pane, and .Sidebar all spread {...props} after their own literal data-component (packages/react/src/SplitPageLayout/SplitPageLayout.tsx:19-27 (Root), :80-87 (Pane), :103-110 (Sidebar))
  • Text spreads {...rest} after its literal data-component="Text" (packages/react/src/Text/Text.tsx:22-29)
  • TextInputWithTokens's inner <input> spreads {...inputPropsRest} after its literal data-component="TextInputWithTokens.Input" (packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx:327-328)

Prefer authoring callback prop types with arguments that can be extended (contributor-docs/style.md)

  • RadioGroup.onChange takes two bare positional arguments (selected, e) instead of one extensible object (packages/react/src/RadioGroup/RadioGroup.tsx:15)

Prefer the useControllableState hook when authoring components that can be controlled or uncontrolled (contributor-docs/style.md)

  • ToggleSwitch uses the legacy useProvidedStateOrCreate hook instead of useControllableState to manage its checked/defaultChecked/onChange controlled-or-uncontrolled state (packages/react/src/ToggleSwitch/ToggleSwitch.tsx:5,96)

Carried forward from prior runs (still unresolved)

  • Dialog.onClose uses a bare positional string instead of an extensible object (finding)
  • Dialog has no rest-parameter pass-through to its root element (finding)
  • Dialog's inline style merge places consumer style before the component-owned --dialog-width CSS variable, inverting the documented merge order (finding)
  • Dialog's footer-button auto-focus runs in an effect/state-ratchet instead of an event handler (finding)
  • useDetails returns a ref for the caller to apply instead of only accepting one (finding)
  • useDetails's onClickOutside callback sits directly in an effect dependency array instead of a stabilized ref (finding)
  • FormControl has no rest-parameter pass-through to its root element (finding)
  • FormControlLabel.visuallyHidden is a part-visibility toggle that doesn't follow hide/show naming (finding)
  • Header/Header.Item/Header.Link spread rest props before the hard-coded data-component, letting a consumer-supplied data-component silently win (finding)
  • Heading.variant communicates typographic scale (appearance), not semantic purpose (finding)
  • Hidden has no rest-parameter pass-through to its root element (finding)
  • InlineMessage spreads rest props before its own data-size/data-variant/data-component, letting a consumer override them silently (finding)
  • KeybindingHint has no rest-parameter pass-through to its root element (finding)
  • KeybindingHint.size uses 'normal' instead of the guide's standard 'medium' scale name (finding)
  • SideNav.variant (lightweight/normal) communicates padding/border density (appearance), not purpose (finding)
  • SideNav has no rest-parameter pass-through to its root element (finding)
  • Breadcrumbs root has no rest/passthrough props (finding)
  • Breadcrumbs.Item selected uses an inert bare string class instead of data-selected (finding)
  • Breadcrumbs variant="spacious" communicates padding/density, not purpose (finding)
  • Button/ButtonBase variant mixes appearance names (invisible, link) with semantic ones (primary, danger) (finding)
  • ButtonBase drops the consumer's onClick entirely while loading is true, instead of merging (finding)
  • ButtonGroup silently discards a consumer-forwarded data-component via rest-spread ordering (finding)
  • Card computes aria-labelledby via direct mutation of rest instead of intentional merge precedence (finding)
  • Checkbox forwards checked/defaultChecked to the native <input> without useControllableState (finding)
  • Checkbox.onChange still fires when a group-level onChange calls preventDefault() (finding)
  • CheckboxGroup.onChange uses two positional args instead of one extensible object (finding)
  • CheckboxGroup skips its internal selection-state update entirely when no onChange is provided (finding)
  • CircleBadge.variant duplicates size's scale/appearance role (finding)
  • CircleBadge silently overwrites consumer-supplied style instead of merging it (finding)
  • ConfirmationDialog accepts no rest params to forward to its underlying Dialog root (finding)
  • ConfirmationDialog.onClose uses a bare positional string instead of an extensible object (finding)
  • CounterLabel's deprecated scheme prop still duplicates variant as two mode props for one concept (finding)
  • DataTable.onToggleSort uses two positional args instead of one extensible object (finding)
  • DataTable.Title/DataTable.Subtitle type as accepting full HTMLAttributes but silently drop unrecognized attributes at runtime (finding)
  • Pagination.onPageChange uses bare positional args instead of an extensible object (finding)
  • PageLayout DragHandle.onDrag uses bare positional args instead of an extensible object (finding)
  • ActionBar root has no rest/passthrough props (finding)
  • AvatarStack root has no rest/passthrough props (finding)
  • Blankslate applies rest params to the wrong element (finding)
  • ActionMenu.onOpenChange uses a bare positional boolean (finding)
  • AnchoredOverlay onOpen/onClose use multiple/bare positional arguments (finding)
  • ActionMenu open/onOpenChange use useProvidedStateOrCreate instead of useControllableState (finding)
  • Autocomplete syncs controlled value via an effect instead of useControllableState (finding)
  • ActionList List.variant communicates layout/appearance, not purpose (finding)
  • ActionList.Item size scale omits small (finding)
  • AvatarStack.variant (cascade/stack) communicates appearance, not purpose (finding)
  • ActionList.disableFocusZone is a negated, verb-prefixed boolean (finding)
  • AnchoredOverlay.displayCloseButton should follow durable-default/hide-show naming (finding)
  • AvatarStack.disableExpand mirrors the guide's explicit anti-pattern (finding)
  • Autocomplete restores selection range in an effect rather than the triggering handler (finding)
Run details and coverage

Component counts for this run's newly-reviewed set (51 directories; all 21 principles inspected against current source):

Component Result
Label 1 merge-order finding
LabelGroup pass
Link pass
NavList 3 merge-order findings (Heading, Item, GroupHeading)
Octicon pass (deprecated pass-through wrapper)
Overlay pass
PageHeader 1 merge-order finding (ParentLink)
PageLayout prior DragHandle.onDrag finding retained; rest of component passes
Pagehead pass
Pagination prior onPageChange finding retained; rest of component passes
Popover 1 merge-order finding (Popover.Content)
Portal pass
ProgressBar 2 merge-order findings (Item, root)
Radio pass
RadioGroup 1 extensible-callback finding (onChange)
RelativeTime pass
SegmentedControl pass
Select pass
SelectPanel pass
Skeleton (SkeletonBox) pass
SkeletonAvatar pass
SkeletonText 1 merge-order finding (multi-line branch)
Spinner pass
SplitPageLayout 3 merge-order findings (Root, Pane, Sidebar)
Stack pass
StateLabel pass
SubNav pass
TabNav pass
Text 1 merge-order finding
TextInput pass
TextInputWithTokens 1 merge-order finding (Input)
Textarea pass
Timeline pass
ToggleSwitch 1 controllable-state finding
Token pass
TopicTag pass
TreeView pass
Truncate pass
UnderlineNav pass
deprecated/DialogV1 pass
deprecated/FilteredSearch pass
deprecated/UnderlineNav pass
experimental/IssueLabel pass
experimental/SelectPanel2 pass
experimental/UnderlinePanels pass
live-region pass
FilteredActionList (publicly exported via @primer/react/experimental; missing from inventory file) pass
ScrollableRegion (publicly exported via @primer/react/experimental; missing from inventory file) pass
experimental/Tabs (publicly exported as @primer/react/experimental Tabs; missing from inventory file) pass

Evidence source this run: direct source inspection (file:line evidence for every finding above); cross-checked package exports at packages/react/src/index.ts and packages/react/src/experimental/index.ts against /tmp/gh-aw/data/components.json to identify FilteredActionList, ScrollableRegion, and experimental/Tabs as publicly exported but inventory-missing.

Workflow run: §34825725332

Past findings not rechecked this run

The following components were carried forward unchanged from prior runs without re-inspection this run: Breadcrumbs, Button (+ButtonBase), ButtonGroup, Card, Checkbox, CheckboxGroup, CircleBadge, ConfirmationDialog, CounterLabel, DataTable, ActionBar, ActionList, ActionMenu, AnchoredOverlay, Autocomplete, Avatar, AvatarStack, Banner, Blankslate, BranchName, Details, Dialog, FeatureFlags, Flash, FormControl, Header, Heading, Hidden, InlineMessage, KeybindingHint, SideNav, VisuallyHidden. Their findings remain listed in "Proposed API changes" above and their evidence is preserved in each component's managed comment.

All 83 publicly-exported component surfaces have now been reviewed against the full 21-principle checklist at least once. No further unreviewed components remain in the current inventory.

Generated by 🔎 Primer API Review · copilot · auto · 236.2 AIC · ⌖ 2.43 AIC · ⊞ 7.1K ·

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions