From da628642cfe439b7a6b8f5560e6647948c1748b0 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 17 Jun 2026 11:40:57 -0500 Subject: [PATCH 01/38] docs: add initial modular design system draft --- contributor-docs/adrs/adr-024-modular-ds.md | 56 +++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 contributor-docs/adrs/adr-024-modular-ds.md diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md new file mode 100644 index 00000000000..1c38c2fd3ef --- /dev/null +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -0,0 +1,56 @@ +# Modular Design System + +πŸ“† Date: 2026-05-17 + +## Status + +| Stage | State | +| -------------- | ----------- | +| Status | Proposed ⚠️ | +| Implementation | Pending ⚠️ | + +## Context + +There is a proposal for enabling better pattern creation via modular building +blocks in: https://github.com/github/core-ux/issues/2238. In this proposal, it +details several layers that Primer could be structured into: + +- Layer 3: Ready-made, props-based components that are ready to use +- Layer 2: Parts, presentiontational-style components that one combines with hooks to + create components +- Layer 1: Foundations, unstyled primitives to allow for full markup and style + control +- Layer 0: Hooks, state management, and other logic that can be used to build components + +We took this proposal into a Modular design System Workshop: +https://github.com/github/primer/discussions/6703. In this workshop, we +identified several scenarios that team run into challenges when building with +Primer: + +- It can be difficult to extend or add new functionality to a component +- It can be difficult to customize the appearance, content, or semantics of a + component +- It can be difficult to build new experiences using established patterns (like accessibility primitives) + +These challenges lead to custom implementations, forks, or overrides of Primer +components that reuslt in adoption challenges and ecosystem fragmentation. + +As a result, we applied the layer model to several situations in a +[FigJam](https://www.figma.com/board/mgApQG7vL3imdUIhgyhG2n/Modular-DS-Workshop?node-id=84-152&t=dBKm8jx1UsKRXyzu-1). From this work, we pulled out insights about this model that we want to adopt in Primer today. + +## Decision + +From the Modular Design System workshop, we identified several key areas that we +want to focus on in order to improve Primer: + +- Embrace layer 2 from the layer model, offering parts for our components that + can be combined with hooks for improved flexibility +- Provide low-level components and hooks to make it easier to build + quality, accessible experience +- Create a model for upstreaming work where people build on top of Primer + +### Embracing layer 2 + +### Low-level components and hooks + +### Model for upstreaming work From 8a3230fe2c7d7cda78c12a5f206fad1db7b85578 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 17 Jun 2026 11:56:22 -0500 Subject: [PATCH 02/38] docs: add initial support tables --- contributor-docs/adrs/adr-024-modular-ds.md | 119 ++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 1c38c2fd3ef..e2540a2f05c 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -51,6 +51,125 @@ want to focus on in order to improve Primer: ### Embracing layer 2 +| Entrypoint | Component | Props | Parts | Hooks | Notes | +| :------------- | :------------------ | :---- | :---- | :---- | :---- | +| . | ActionBar | | | | | +| | ActionList | | | | | +| | ActionMenu | | | | | +| | AnchoredOverlay | | | | | +| | Autocomplete | | | | | +| | Avatar | | | | | +| | AvatarStack | | | | | +| | Banner | | | | | +| | BaseStyles | | | | | +| | BranchName | | | | | +| | Breadcrumb | | | | | +| | Breadcrumbs | | | | | +| | Button | | | | | +| | ButtonGroup | | | | | +| | Checkbox | | | | | +| | CheckboxGroup | | | | | +| | CircleBadge | | | | | +| | ConfirmationDialog | | | | | +| | CounterLabel | | | | | +| | Details | | | | | +| | Dialog | | | | | +| | Flash | | | | | +| | FocusKeys | | | | | +| | FormControl | | | | | +| | Header | | | | | +| | Heading | | | | | +| | IconButton | | | | | +| | IssueLabelToken | | | | | +| | Label | | | | | +| | LabelGroup | | | | | +| | Link | | | | | +| | LinkButton | | | | | +| | NavList | | | | | +| | Overlay | | | | | +| | PageHeader | | | | | +| | PageLayout | | | | | +| | Pagination | | | | | +| | Popover | | | | | +| | Portal | | | | | +| | PortalContext | | | | | +| | ProgressBar | | | | | +| | Radio | | | | | +| | RadioGroup | | | | | +| | RelativeTime | | | | | +| | ResponsiveValue | | | | | +| | SegmentedControl | | | | | +| | Select | | | | | +| | SelectPanel | | | | | +| | SideNav | | | | | +| | SkeletonBox | | | | | +| | Spinner | | | | | +| | SplitPageLayout | | | | | +| | Stack | | | | | +| | StateLabel | | | | | +| | SubNav | | | | | +| | Text | | | | | +| | Textarea | | | | | +| | TextInput | | | | | +| | TextInputWithTokens | | | | | +| | ThemeProvider | | | | | +| | Timeline | | | | | +| | ToggleSwitch | | | | | +| | Token | | | | | +| | Tooltip | | | | | +| | TreeView | | | | | +| | Truncate | | | | | +| | UnderlineNav | | | | | +| | VisuallyHidden | | | | | +| ./experimental | ActionBar | | | | | +| | Announce | | | | | +| | AriaAlert | | | | | +| | AriaStatus | | | | | +| | Blankslate | | | | | +| | Card | | | | | +| | DataTable | | | | | +| | Dialog | | | | | +| | FilteredActionList | | | | | +| | Hidden | | | | | +| | InlineMessage | | | | | +| | IssueLabel | | | | | +| | KeybindingHint | | | | | +| | NavList | | | | | +| | PageHeader | | | | | +| | ScrollableRegion | | | | | +| | SelectPanel | | | | | +| | SkeletonAvatar | | | | | +| | SkeletonText | | | | | +| | Stack | | | | | +| | Table | | | | | +| | Tabs | | | | | +| | Tooltip | | | | | +| | TopicTag | | | | | +| | UnderlinePanels | | | | | + ### Low-level components and hooks +We want to offer more low-level components and hooks, in particular with a focus +on accessibility. Providing these low-level abstractions allows for quickly creating +robust experiences instead of having to re-invent them from scratch. + +In particular, we would like to offer the following primitives: + +| Type | Name | Supported | Description | Notes | +| :--------- | :---------------- | :-------- | :---------- | :---- | +| Components | Accordion | | | | +| | Combobox | | | | +| | Disclosure | | | | +| | Dialog | | | | +| | Listbox | | | | +| | Menu | | | | +| | Menubar | | | | +| | Popover | | | | +| | Tabs | | | | +| | Tree | | | | +| Hooks | useFocusTrap | | | | +| | useMergedRefs | | | | +| | useRovingTabIndex | | | | +| | useTabs | | | | + ### Model for upstreaming work From ef931ea5b39fb08bed9495ba60a07d2047465e16 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 17 Jun 2026 12:11:20 -0500 Subject: [PATCH 03/38] docs: begin filling out tables --- contributor-docs/adrs/adr-024-modular-ds.md | 308 +++++++++++++------- 1 file changed, 197 insertions(+), 111 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index e2540a2f05c..0039f4dfaab 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -49,103 +49,112 @@ want to focus on in order to improve Primer: quality, accessible experience - Create a model for upstreaming work where people build on top of Primer +In addition, when authoring components we will offer presentational components +and behavioral hooks that can then be used for higher-level, props-based +components. + ### Embracing layer 2 -| Entrypoint | Component | Props | Parts | Hooks | Notes | -| :------------- | :------------------ | :---- | :---- | :---- | :---- | -| . | ActionBar | | | | | -| | ActionList | | | | | -| | ActionMenu | | | | | -| | AnchoredOverlay | | | | | -| | Autocomplete | | | | | -| | Avatar | | | | | -| | AvatarStack | | | | | -| | Banner | | | | | -| | BaseStyles | | | | | -| | BranchName | | | | | -| | Breadcrumb | | | | | -| | Breadcrumbs | | | | | -| | Button | | | | | -| | ButtonGroup | | | | | -| | Checkbox | | | | | -| | CheckboxGroup | | | | | -| | CircleBadge | | | | | -| | ConfirmationDialog | | | | | -| | CounterLabel | | | | | -| | Details | | | | | -| | Dialog | | | | | -| | Flash | | | | | -| | FocusKeys | | | | | -| | FormControl | | | | | -| | Header | | | | | -| | Heading | | | | | -| | IconButton | | | | | -| | IssueLabelToken | | | | | -| | Label | | | | | -| | LabelGroup | | | | | -| | Link | | | | | -| | LinkButton | | | | | -| | NavList | | | | | -| | Overlay | | | | | -| | PageHeader | | | | | -| | PageLayout | | | | | -| | Pagination | | | | | -| | Popover | | | | | -| | Portal | | | | | -| | PortalContext | | | | | -| | ProgressBar | | | | | -| | Radio | | | | | -| | RadioGroup | | | | | -| | RelativeTime | | | | | -| | ResponsiveValue | | | | | -| | SegmentedControl | | | | | -| | Select | | | | | -| | SelectPanel | | | | | -| | SideNav | | | | | -| | SkeletonBox | | | | | -| | Spinner | | | | | -| | SplitPageLayout | | | | | -| | Stack | | | | | -| | StateLabel | | | | | -| | SubNav | | | | | -| | Text | | | | | -| | Textarea | | | | | -| | TextInput | | | | | -| | TextInputWithTokens | | | | | -| | ThemeProvider | | | | | -| | Timeline | | | | | -| | ToggleSwitch | | | | | -| | Token | | | | | -| | Tooltip | | | | | -| | TreeView | | | | | -| | Truncate | | | | | -| | UnderlineNav | | | | | -| | VisuallyHidden | | | | | -| ./experimental | ActionBar | | | | | -| | Announce | | | | | -| | AriaAlert | | | | | -| | AriaStatus | | | | | -| | Blankslate | | | | | -| | Card | | | | | -| | DataTable | | | | | -| | Dialog | | | | | -| | FilteredActionList | | | | | -| | Hidden | | | | | -| | InlineMessage | | | | | -| | IssueLabel | | | | | -| | KeybindingHint | | | | | -| | NavList | | | | | -| | PageHeader | | | | | -| | ScrollableRegion | | | | | -| | SelectPanel | | | | | -| | SkeletonAvatar | | | | | -| | SkeletonText | | | | | -| | Stack | | | | | -| | Table | | | | | -| | Tabs | | | | | -| | Tooltip | | | | | -| | TopicTag | | | | | -| | UnderlinePanels | | | | | +- Parts provide a clear way for teams to layer on functionality that does not + exist +- It provides a clear path for upstreaming +- The biggest gap is providing behavioral/state hooks for components (if needed) + +`N/A` means that layer is not a useful fit for the component's API shape. + +| Entrypoint | Component | Props | Parts | Hooks | Notes | +| :------------- | :------------------ | :---- | :---- | :---- | :---------------------------------------------------------------------------------------------------------------- | +| . | ActionBar | N/A | Yes | No | Parts include `IconButton`, `Divider`, `Group`, and `Menu`; no public ActionBar behavior hook. | +| | ActionList | N/A | Yes | No | Compound list API with item, group, divider, visual, description, and action parts. | +| | ActionMenu | No | Yes | No | Parts include `Button`, `Anchor`, `Overlay`, and `Divider`; menu contents use `ActionList`. | +| | AnchoredOverlay | Yes | N/A | Yes | Props-driven overlay; behavior can be recreated with `useAnchoredPosition` and `useOverlay`. | +| | Autocomplete | No | Yes | No | Slot API exposes `Input`, `Menu`, and `Overlay`; no public autocomplete hook. | +| | Avatar | Yes | N/A | N/A | Leaf visual component. | +| | AvatarStack | Yes | N/A | No | Props-driven wrapper around `Avatar` children; no public stack behavior hook. | +| | Banner | Yes | Yes | N/A | Supports title, description, and action props plus matching parts. | +| | BaseStyles | Yes | N/A | N/A | Props-driven application wrapper. | +| | BranchName | Yes | N/A | N/A | Leaf display component. | +| | Breadcrumb | N/A | Yes | N/A | Deprecated alias of `Breadcrumbs` with `Item`. | +| | Breadcrumbs | N/A | Yes | N/A | Uses `Breadcrumbs.Item`; no props-only item list API. | +| | Button | Yes | N/A | N/A | Leaf action component. | +| | ButtonGroup | Yes | N/A | N/A | Props-driven grouping wrapper for button children. | +| | Checkbox | Yes | N/A | N/A | Native input wrapper. | +| | CheckboxGroup | N/A | Yes | No | Uses label, caption, and validation parts through the shared choice-group implementation. | +| | ConfirmationDialog | Yes | N/A | Yes | Props-driven dialog with `useConfirm` behavior hook. | +| | CounterLabel | Yes | N/A | N/A | Leaf count display component. | +| | Details | N/A | Yes | Yes | Exposes `Summary` plus `useDetails`. | +| | Dialog | Yes | Yes | No | Props-driven dialog also exposes parts for custom renderers; `useDialog` is internal. | +| | Flash | Yes | N/A | N/A | Props-driven message component. | +| | FocusKeys | N/A | N/A | Yes | Not a component; exported behavior constant used with `useFocusZone`. | +| | FormControl | N/A | Yes | Yes | Parts include `Label`, `Caption`, `LeadingVisual`, and `Validation`; hook forwards form-control props. | +| | Header | N/A | Yes | N/A | Structural API exposes `Item` and `Link`. | +| | Heading | Yes | N/A | N/A | Leaf typography component. | +| | IconButton | Yes | N/A | N/A | Leaf action component. | +| | IssueLabelToken | Yes | N/A | N/A | Props-driven token variant. | +| | Label | Yes | N/A | N/A | Leaf display component. | +| | LabelGroup | Yes | N/A | N/A | Props-driven grouping wrapper for labels. | +| | Link | Yes | N/A | N/A | Leaf navigation component. | +| | LinkButton | Yes | N/A | N/A | Leaf action/navigation component. | +| | NavList | N/A | Yes | No | Compound navigation API with item, group, visual, divider, and subnav parts. | +| | Overlay | Yes | N/A | Yes | Props-driven floating surface backed by `useOverlay`. | +| | PageHeader | N/A | Yes | N/A | Structural API exposes title, context, action, visual, description, and navigation parts. | +| | PageLayout | N/A | Yes | No | Layout parts include `Header`, `Content`, `Pane`, `Sidebar`, and `Footer`; no public layout hook. | +| | Pagination | Yes | N/A | No | Props-driven pagination model; no public pagination hook. | +| | Popover | Yes | Yes | N/A | Props-driven wrapper with `Content` part. | +| | Portal | Yes | N/A | N/A | Low-level rendering utility. | +| | PortalContext | N/A | N/A | N/A | Context export rather than a component API. | +| | ProgressBar | Yes | Yes | N/A | Props-driven bar with `Item` for segmented progress. | +| | Radio | Yes | N/A | N/A | Native input wrapper. | +| | RadioGroup | N/A | Yes | No | Uses label, caption, and validation parts through the shared choice-group implementation. | +| | RelativeTime | Yes | N/A | N/A | Props-driven time display. | +| | ResponsiveValue | N/A | N/A | Yes | Type-level API paired with `useResponsiveValue`. | +| | SegmentedControl | N/A | Yes | No | Compound control with `Button` and `IconButton` parts; no public segmented-control hook. | +| | Select | Yes | Yes | N/A | Native select wrapper with `Option` and `OptGroup` parts. | +| | SelectPanel | Yes | Yes | No | Props-driven selection panel; parts cover messages and secondary actions, not the full behavior. | +| | SideNav | N/A | Yes | N/A | Deprecated structural nav with `Link` part. | +| | SkeletonBox | Yes | N/A | N/A | Leaf skeleton component. | +| | Spinner | Yes | N/A | N/A | Leaf loading component. | +| | SplitPageLayout | N/A | Yes | No | Layout parts mirror `PageLayout`; no public layout hook. | +| | Stack | Yes | Yes | N/A | Props-driven layout primitive with optional `Item` part. | +| | StateLabel | Yes | N/A | N/A | Leaf display component. | +| | SubNav | N/A | Yes | N/A | Structural nav with `Link` and `Links` parts. | +| | Text | Yes | N/A | N/A | Leaf typography component. | +| | Textarea | Yes | N/A | N/A | Native input wrapper. | +| | TextInput | Yes | Yes | N/A | Props-driven input with `Action` as an optional part. | +| | TextInputWithTokens | Yes | N/A | No | Props-driven tokenized input; no public token-input behavior hook. | +| | ThemeProvider | Yes | N/A | Yes | Provider component plus `useTheme` and `useColorSchemeVar`. | +| | Timeline | N/A | Yes | N/A | Structural timeline with item, avatar, badge, body, break, and action parts. | +| | ToggleSwitch | Yes | N/A | N/A | Props-driven switch component. | +| | Token | Yes | N/A | N/A | Leaf token component. | +| | Tooltip | Yes | N/A | No | Props-driven tooltip; no public tooltip behavior hook. | +| | TreeView | N/A | Yes | Yes | Compound tree API with item/subtree/visual parts and `useRovingTabIndex`. | +| | Truncate | Yes | N/A | N/A | Props-driven text utility. | +| | UnderlineNav | N/A | Yes | N/A | Structural nav with `Item` part. | +| | VisuallyHidden | Yes | N/A | N/A | Leaf accessibility utility. | +| ./experimental | Announce | Yes | N/A | N/A | Props-driven live-region primitive; no public announcement hook (available through `@primer/live-region-element` | +| | AriaAlert | Yes | N/A | N/A | Props-driven assertive live-region wrapper. | +| | AriaStatus | Yes | N/A | N/A | Props-driven polite live-region wrapper. | +| | Blankslate | N/A | Yes | N/A | Parts-first API with visual, heading, description, and action parts. | +| | Card | N/A | Yes | N/A | Parts-first card with icon, image, heading, description, action, and metadata parts. | +| | DataTable | Yes | Yes | Yes | Props-driven data grid; presentational table parts are exposed through `Table`. Hook is `useTable` | +| | Dialog | Yes | Yes | No | Same public API shape as the root export. | +| | FilteredActionList | Yes | Yes | No | Props-driven filtered list with `Input` and `BodyLoader` parts. | +| | Hidden | N/A | Yes | N/A | Props-driven responsive visibility utility. | +| | InlineMessage | N/A | Yes | N/A | Leaf message component. | +| | IssueLabel | N/A | Yes | N/A | Props-driven issue label component. | +| | KeybindingHint | Yes | N/A | N/A | Props-driven text utility for keybindings. | +| | NavList | N/A | Yes | No | Same public API shape as the root export. | +| | PageHeader | N/A | Yes | N/A | Same public API shape as the root export. | +| | ScrollableRegion | Yes | N/A | N/A | Props-driven accessibility wrapper. | +| | SelectPanel | N/A | Yes | No | Experimental parts-first panel with button, header, search, footer, loading, message, and secondary action parts. | +| | SkeletonAvatar | Yes | N/A | N/A | Leaf skeleton component. | +| | SkeletonText | Yes | N/A | N/A | Leaf skeleton component. | +| | Stack | Yes | Yes | N/A | Same public API shape as the root export. | +| | Table | N/A | Yes | N/A | Presentational table parts for `DataTable`. | +| | Tabs | N/A | Yes | Yes | Experimental primitive exposes `TabList`, `Tab`, `TabPanel`, and related hooks. | +| | Tooltip | Yes | N/A | No | Same public API shape as the root export. | +| | TopicTag | Yes | Yes | N/A | Props-driven tag with `Group` part. | +| | UnderlinePanels | N/A | Yes | No | Parts-first tab panel API with `Tab` and `Panel`; no public hook. | ### Low-level components and hooks @@ -155,21 +164,98 @@ robust experiences instead of having to re-invent them from scratch. In particular, we would like to offer the following primitives: -| Type | Name | Supported | Description | Notes | -| :--------- | :---------------- | :-------- | :---------- | :---- | -| Components | Accordion | | | | -| | Combobox | | | | -| | Disclosure | | | | -| | Dialog | | | | -| | Listbox | | | | -| | Menu | | | | -| | Menubar | | | | -| | Popover | | | | -| | Tabs | | | | -| | Tree | | | | -| Hooks | useFocusTrap | | | | -| | useMergedRefs | | | | -| | useRovingTabIndex | | | | -| | useTabs | | | | +| Type | Name | Supported | Description | Notes | +| :--------- | :---------------- | :-------- | :----------------------------------------- | :-------------------------------------------------------------------------------------------------- | +| Components | Accordion | No | Disclosure group primitive. | Not exposed today. | +| | Combobox | No | Text input plus listbox popup primitive. | Autocomplete and SelectPanel cover product use cases, but no generic combobox primitive is exposed. | +| | Disclosure | No | Show/hide primitive. | `Details` and `useDetails` exist, but there is no named `Disclosure` primitive. | +| | Dialog | Yes | Modal dialog primitive. | `Dialog` exposes props and custom-renderer parts. | +| | Listbox | No | Option list primitive. | ActionList and SelectPanel cover product use cases, but no generic listbox primitive is exposed. | +| | Menu | No | Menu button and popup primitive. | ActionMenu covers product use cases, but no generic menu primitive is exposed. | +| | Menubar | No | Horizontal menu primitive. | Not exposed today. | +| | Popover | Yes | Anchored floating content primitive. | Covered by `Popover`, `Overlay`, and `AnchoredOverlay`. | +| | Tabs | Yes | Tab state and tab/panel primitive. | Available from `./experimental` with parts and hooks. | +| | Tree | Yes | Tree navigation primitive. | Covered by `TreeView`; no separate unstyled `Tree` primitive is exposed. | +| Hooks | useFocusTrap | Yes | Trap focus within a container. | Exported from the root entrypoint. | +| | useMergedRefs | Yes | Merge multiple refs into one ref callback. | Exported from the root entrypoint. | +| | useRovingTabIndex | Yes | Manage roving tab index behavior. | Exported from the root entrypoint via `TreeView`. | +| | useTabs | Yes | Manage tab state and ARIA wiring. | Available from `./experimental` alongside `useTab`, `useTabList`, and `useTabPanel`. | ### Model for upstreaming work + +### Authoring components + +#### Config components + +These components are "all-in-one"; they provide a high level of abstraction that +support common use-cases making it simple to quickly build out established +patterns and experiences. However, they are often inflexible and difficult to +extend. + +````tsx + { + /* ... */ + }} + onFilter={query => { + /* ... */ + }} +/> + +#### Presentational components + +These components provide a lower level of abstraction and are more flexible and extensible, but require more work to build out common patterns and experiences. + +```tsx +function Example({items}) { + const [state, actions] = useList({ + defaultSelected: [], + }) + + return ( + + { + actions.updateQuery(query) + }} + /> + {items + .filter(item => item.label.includes(state.query)) + .map(item => { + return ( + { + actions.toggleSelect(item.label) + }} + > + + + + {item.label} + + ) + })} + + ) +} +```` + +Presentational components are accompanied by behavior or state hooks for a +component's core functionality. Often times, config components are built by +combining presentational components and the corresponding behavior and state +hooks needed for a feature. + +## Consequences + +TODO + +## Alternatives + +TODO + +## Questions + +TODO From a74d0a57db93b31050cba0e42cbfaff8f2a3f72d Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 19 Jun 2026 10:44:16 -0500 Subject: [PATCH 04/38] docs: begin audit --- contributor-docs/adrs/adr-024-modular-ds.md | 292 +++++++------------- 1 file changed, 93 insertions(+), 199 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 0039f4dfaab..cbb9fcdb008 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -49,213 +49,107 @@ want to focus on in order to improve Primer: quality, accessible experience - Create a model for upstreaming work where people build on top of Primer -In addition, when authoring components we will offer presentational components -and behavioral hooks that can then be used for higher-level, props-based -components. +To do this, we will: -### Embracing layer 2 +- Identify and deliver components to break down into parts that can be used to create more + flexible and extensible components +- Identify and deliver low-level components, hooks, and utilities that can be used to quickly build out accessible experiences +- Embrace the spectrum of abstraction model, authoring presentation components + that combine with hooks to provide flexible patterns and opinionated defaults. -- Parts provide a clear way for teams to layer on functionality that does not - exist -- It provides a clear path for upstreaming -- The biggest gap is providing behavioral/state hooks for components (if needed) +### Breaking components down into parts -`N/A` means that layer is not a useful fit for the component's API shape. +### Delivering low-level components, hooks, and utilities -| Entrypoint | Component | Props | Parts | Hooks | Notes | -| :------------- | :------------------ | :---- | :---- | :---- | :---------------------------------------------------------------------------------------------------------------- | -| . | ActionBar | N/A | Yes | No | Parts include `IconButton`, `Divider`, `Group`, and `Menu`; no public ActionBar behavior hook. | -| | ActionList | N/A | Yes | No | Compound list API with item, group, divider, visual, description, and action parts. | -| | ActionMenu | No | Yes | No | Parts include `Button`, `Anchor`, `Overlay`, and `Divider`; menu contents use `ActionList`. | -| | AnchoredOverlay | Yes | N/A | Yes | Props-driven overlay; behavior can be recreated with `useAnchoredPosition` and `useOverlay`. | -| | Autocomplete | No | Yes | No | Slot API exposes `Input`, `Menu`, and `Overlay`; no public autocomplete hook. | -| | Avatar | Yes | N/A | N/A | Leaf visual component. | -| | AvatarStack | Yes | N/A | No | Props-driven wrapper around `Avatar` children; no public stack behavior hook. | -| | Banner | Yes | Yes | N/A | Supports title, description, and action props plus matching parts. | -| | BaseStyles | Yes | N/A | N/A | Props-driven application wrapper. | -| | BranchName | Yes | N/A | N/A | Leaf display component. | -| | Breadcrumb | N/A | Yes | N/A | Deprecated alias of `Breadcrumbs` with `Item`. | -| | Breadcrumbs | N/A | Yes | N/A | Uses `Breadcrumbs.Item`; no props-only item list API. | -| | Button | Yes | N/A | N/A | Leaf action component. | -| | ButtonGroup | Yes | N/A | N/A | Props-driven grouping wrapper for button children. | -| | Checkbox | Yes | N/A | N/A | Native input wrapper. | -| | CheckboxGroup | N/A | Yes | No | Uses label, caption, and validation parts through the shared choice-group implementation. | -| | ConfirmationDialog | Yes | N/A | Yes | Props-driven dialog with `useConfirm` behavior hook. | -| | CounterLabel | Yes | N/A | N/A | Leaf count display component. | -| | Details | N/A | Yes | Yes | Exposes `Summary` plus `useDetails`. | -| | Dialog | Yes | Yes | No | Props-driven dialog also exposes parts for custom renderers; `useDialog` is internal. | -| | Flash | Yes | N/A | N/A | Props-driven message component. | -| | FocusKeys | N/A | N/A | Yes | Not a component; exported behavior constant used with `useFocusZone`. | -| | FormControl | N/A | Yes | Yes | Parts include `Label`, `Caption`, `LeadingVisual`, and `Validation`; hook forwards form-control props. | -| | Header | N/A | Yes | N/A | Structural API exposes `Item` and `Link`. | -| | Heading | Yes | N/A | N/A | Leaf typography component. | -| | IconButton | Yes | N/A | N/A | Leaf action component. | -| | IssueLabelToken | Yes | N/A | N/A | Props-driven token variant. | -| | Label | Yes | N/A | N/A | Leaf display component. | -| | LabelGroup | Yes | N/A | N/A | Props-driven grouping wrapper for labels. | -| | Link | Yes | N/A | N/A | Leaf navigation component. | -| | LinkButton | Yes | N/A | N/A | Leaf action/navigation component. | -| | NavList | N/A | Yes | No | Compound navigation API with item, group, visual, divider, and subnav parts. | -| | Overlay | Yes | N/A | Yes | Props-driven floating surface backed by `useOverlay`. | -| | PageHeader | N/A | Yes | N/A | Structural API exposes title, context, action, visual, description, and navigation parts. | -| | PageLayout | N/A | Yes | No | Layout parts include `Header`, `Content`, `Pane`, `Sidebar`, and `Footer`; no public layout hook. | -| | Pagination | Yes | N/A | No | Props-driven pagination model; no public pagination hook. | -| | Popover | Yes | Yes | N/A | Props-driven wrapper with `Content` part. | -| | Portal | Yes | N/A | N/A | Low-level rendering utility. | -| | PortalContext | N/A | N/A | N/A | Context export rather than a component API. | -| | ProgressBar | Yes | Yes | N/A | Props-driven bar with `Item` for segmented progress. | -| | Radio | Yes | N/A | N/A | Native input wrapper. | -| | RadioGroup | N/A | Yes | No | Uses label, caption, and validation parts through the shared choice-group implementation. | -| | RelativeTime | Yes | N/A | N/A | Props-driven time display. | -| | ResponsiveValue | N/A | N/A | Yes | Type-level API paired with `useResponsiveValue`. | -| | SegmentedControl | N/A | Yes | No | Compound control with `Button` and `IconButton` parts; no public segmented-control hook. | -| | Select | Yes | Yes | N/A | Native select wrapper with `Option` and `OptGroup` parts. | -| | SelectPanel | Yes | Yes | No | Props-driven selection panel; parts cover messages and secondary actions, not the full behavior. | -| | SideNav | N/A | Yes | N/A | Deprecated structural nav with `Link` part. | -| | SkeletonBox | Yes | N/A | N/A | Leaf skeleton component. | -| | Spinner | Yes | N/A | N/A | Leaf loading component. | -| | SplitPageLayout | N/A | Yes | No | Layout parts mirror `PageLayout`; no public layout hook. | -| | Stack | Yes | Yes | N/A | Props-driven layout primitive with optional `Item` part. | -| | StateLabel | Yes | N/A | N/A | Leaf display component. | -| | SubNav | N/A | Yes | N/A | Structural nav with `Link` and `Links` parts. | -| | Text | Yes | N/A | N/A | Leaf typography component. | -| | Textarea | Yes | N/A | N/A | Native input wrapper. | -| | TextInput | Yes | Yes | N/A | Props-driven input with `Action` as an optional part. | -| | TextInputWithTokens | Yes | N/A | No | Props-driven tokenized input; no public token-input behavior hook. | -| | ThemeProvider | Yes | N/A | Yes | Provider component plus `useTheme` and `useColorSchemeVar`. | -| | Timeline | N/A | Yes | N/A | Structural timeline with item, avatar, badge, body, break, and action parts. | -| | ToggleSwitch | Yes | N/A | N/A | Props-driven switch component. | -| | Token | Yes | N/A | N/A | Leaf token component. | -| | Tooltip | Yes | N/A | No | Props-driven tooltip; no public tooltip behavior hook. | -| | TreeView | N/A | Yes | Yes | Compound tree API with item/subtree/visual parts and `useRovingTabIndex`. | -| | Truncate | Yes | N/A | N/A | Props-driven text utility. | -| | UnderlineNav | N/A | Yes | N/A | Structural nav with `Item` part. | -| | VisuallyHidden | Yes | N/A | N/A | Leaf accessibility utility. | -| ./experimental | Announce | Yes | N/A | N/A | Props-driven live-region primitive; no public announcement hook (available through `@primer/live-region-element` | -| | AriaAlert | Yes | N/A | N/A | Props-driven assertive live-region wrapper. | -| | AriaStatus | Yes | N/A | N/A | Props-driven polite live-region wrapper. | -| | Blankslate | N/A | Yes | N/A | Parts-first API with visual, heading, description, and action parts. | -| | Card | N/A | Yes | N/A | Parts-first card with icon, image, heading, description, action, and metadata parts. | -| | DataTable | Yes | Yes | Yes | Props-driven data grid; presentational table parts are exposed through `Table`. Hook is `useTable` | -| | Dialog | Yes | Yes | No | Same public API shape as the root export. | -| | FilteredActionList | Yes | Yes | No | Props-driven filtered list with `Input` and `BodyLoader` parts. | -| | Hidden | N/A | Yes | N/A | Props-driven responsive visibility utility. | -| | InlineMessage | N/A | Yes | N/A | Leaf message component. | -| | IssueLabel | N/A | Yes | N/A | Props-driven issue label component. | -| | KeybindingHint | Yes | N/A | N/A | Props-driven text utility for keybindings. | -| | NavList | N/A | Yes | No | Same public API shape as the root export. | -| | PageHeader | N/A | Yes | N/A | Same public API shape as the root export. | -| | ScrollableRegion | Yes | N/A | N/A | Props-driven accessibility wrapper. | -| | SelectPanel | N/A | Yes | No | Experimental parts-first panel with button, header, search, footer, loading, message, and secondary action parts. | -| | SkeletonAvatar | Yes | N/A | N/A | Leaf skeleton component. | -| | SkeletonText | Yes | N/A | N/A | Leaf skeleton component. | -| | Stack | Yes | Yes | N/A | Same public API shape as the root export. | -| | Table | N/A | Yes | N/A | Presentational table parts for `DataTable`. | -| | Tabs | N/A | Yes | Yes | Experimental primitive exposes `TabList`, `Tab`, `TabPanel`, and related hooks. | -| | Tooltip | Yes | N/A | No | Same public API shape as the root export. | -| | TopicTag | Yes | Yes | N/A | Props-driven tag with `Group` part. | -| | UnderlinePanels | N/A | Yes | No | Parts-first tab panel API with `Tab` and `Panel`; no public hook. | - -### Low-level components and hooks - -We want to offer more low-level components and hooks, in particular with a focus -on accessibility. Providing these low-level abstractions allows for quickly creating -robust experiences instead of having to re-invent them from scratch. - -In particular, we would like to offer the following primitives: - -| Type | Name | Supported | Description | Notes | -| :--------- | :---------------- | :-------- | :----------------------------------------- | :-------------------------------------------------------------------------------------------------- | -| Components | Accordion | No | Disclosure group primitive. | Not exposed today. | -| | Combobox | No | Text input plus listbox popup primitive. | Autocomplete and SelectPanel cover product use cases, but no generic combobox primitive is exposed. | -| | Disclosure | No | Show/hide primitive. | `Details` and `useDetails` exist, but there is no named `Disclosure` primitive. | -| | Dialog | Yes | Modal dialog primitive. | `Dialog` exposes props and custom-renderer parts. | -| | Listbox | No | Option list primitive. | ActionList and SelectPanel cover product use cases, but no generic listbox primitive is exposed. | -| | Menu | No | Menu button and popup primitive. | ActionMenu covers product use cases, but no generic menu primitive is exposed. | -| | Menubar | No | Horizontal menu primitive. | Not exposed today. | -| | Popover | Yes | Anchored floating content primitive. | Covered by `Popover`, `Overlay`, and `AnchoredOverlay`. | -| | Tabs | Yes | Tab state and tab/panel primitive. | Available from `./experimental` with parts and hooks. | -| | Tree | Yes | Tree navigation primitive. | Covered by `TreeView`; no separate unstyled `Tree` primitive is exposed. | -| Hooks | useFocusTrap | Yes | Trap focus within a container. | Exported from the root entrypoint. | -| | useMergedRefs | Yes | Merge multiple refs into one ref callback. | Exported from the root entrypoint. | -| | useRovingTabIndex | Yes | Manage roving tab index behavior. | Exported from the root entrypoint via `TreeView`. | -| | useTabs | Yes | Manage tab state and ARIA wiring. | Available from `./experimental` alongside `useTab`, `useTabList`, and `useTabPanel`. | - -### Model for upstreaming work - -### Authoring components - -#### Config components - -These components are "all-in-one"; they provide a high level of abstraction that -support common use-cases making it simple to quickly build out established -patterns and experiences. However, they are often inflexible and difficult to -extend. - -````tsx - { - /* ... */ - }} - onFilter={query => { - /* ... */ - }} -/> - -#### Presentational components - -These components provide a lower level of abstraction and are more flexible and extensible, but require more work to build out common patterns and experiences. - -```tsx -function Example({items}) { - const [state, actions] = useList({ - defaultSelected: [], - }) - - return ( - - { - actions.updateQuery(query) - }} - /> - {items - .filter(item => item.label.includes(state.query)) - .map(item => { - return ( - { - actions.toggleSelect(item.label) - }} - > - - - - {item.label} - - ) - })} - - ) -} -```` - -Presentational components are accompanied by behavior or state hooks for a -component's core functionality. Often times, config components are built by -combining presentational components and the corresponding behavior and state -hooks needed for a feature. +### Embracing the spectrum of abstraction model ## Consequences -TODO - ## Alternatives -TODO - ## Questions -TODO +## Audit + +| Entrypoint | Type | Name | Parts | Hooks | Config | Notes | +| :------------- | :------- | :----------------- | :---- | :---- | :----- | :---- | +| . | Compound | ActionBar | βœ… | | | | +| | | ActionList | βœ… | | | | +| | | ActionMenu | βœ… | | | | +| | | Banner | βœ… | | | | +| | | Breadcrumbs | βœ… | | | | +| | | ConfirmationDialog | | | | | +| | | Details | βœ… | | | | +| | | Dialog | | | | | +| | | FormControl | βœ… | | | | +| | | Header | βœ… | | | | +| | | NavList | βœ… | | | | +| | | PageHeader | βœ… | | | | +| | | PageLayout | βœ… | | | | +| | | Pagination | | | | | +| | | Popover | βœ… | | | | +| | | ProgressBar | βœ… | | | | +| | | SegmentedControl | βœ… | | | | +| | | Select | βœ… | | | | +| | | SelectPanel | | | | | +| | | SplitPageLayout | βœ… | | | | +| | | Stack | βœ… | | | | +| | | SubNav | βœ… | | | | +| | | Timeline | βœ… | | | | +| | | TreeView | βœ… | | | | +| | | UnderlineNav | βœ… | | | | +| . | Leaf | Autocomplete | | | | | +| | | Avatar | | | | | +| | | AvatarStack | | | | | +| | | BranchName | | | | | +| | | Button | | | | | +| | | ButtonGroup | | | | | +| | | Checkbox | | | | | +| | | CheckboxGroup | | | | | +| | | CircleBadge | | | | | +| | | CounterLabel | | | | | +| | | Heading | | | | | +| | | IconButton | | | | | +| | | IssueLabelToken | | | | | +| | | Label | | | | | +| | | LabelGroup | | | | | +| | | Link | | | | | +| | | LinkButton | | | | | +| | | Overlay | | | | | +| | | Radio | | | | | +| | | RadioGroup | | | | | +| | | RelativeTime | | | | | +| | | SkeletonBox | | | | | +| | | Spinner | | | | | +| | | StateLabel | | | | | +| | | Text | | | | | +| | | TextInput | | | | | +| | | Textarea | | | | | +| | | ToggleSwitch | | | | | +| | | Token | | | | | +| | | Tooltip | | | | | +| | | Truncate | | | | | +| | Utility | | | | | | +| | | AnchoredOverlay | | | | | +| | | VisuallyHidden | | | | | +| ./experimental | Compound | | | | | | +| | | Blankslate | | | | | +| | | Card | | | | | +| | | DataTable | | | | | +| | | Dialog | | | | | +| | | FilteredActionList | | | | | +| | | KeybindingHint | | | | | +| | | SelectPanel | | | | | +| | | Stack | | | | | +| | | Table | | | | | +| | | Tabs | | | | | +| | | UnderlinePanels | | | | | +| | Leaf | | | | | | +| | | InlineMessage | | | | | +| | | IssueLabel | | | | | +| | | SkeletonAvatar | | | | | +| | | SkeletonText | | | | | +| | | Tooltip | | | | | +| | | TopicTag | | | | | +| | Utility | | | | | | +| | | Hidden | | | | | From a8140fd98eaf6b8904f8848076a146538f187a8d Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 19 Jun 2026 10:50:38 -0500 Subject: [PATCH 05/38] docs: update audit table --- contributor-docs/adrs/adr-024-modular-ds.md | 39 +++++++++------------ 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index cbb9fcdb008..cbc691af026 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -78,20 +78,20 @@ To do this, we will: | | | ActionMenu | βœ… | | | | | | | Banner | βœ… | | | | | | | Breadcrumbs | βœ… | | | | -| | | ConfirmationDialog | | | | | +| | | ConfirmationDialog | | | βœ… | | | | | Details | βœ… | | | | -| | | Dialog | | | | | +| | | Dialog | | | βœ… | | | | | FormControl | βœ… | | | | | | | Header | βœ… | | | | | | | NavList | βœ… | | | | | | | PageHeader | βœ… | | | | | | | PageLayout | βœ… | | | | -| | | Pagination | | | | | +| | | Pagination | | | βœ… | | | | | Popover | βœ… | | | | | | | ProgressBar | βœ… | | | | | | | SegmentedControl | βœ… | | | | | | | Select | βœ… | | | | -| | | SelectPanel | | | | | +| | | SelectPanel | | | βœ… | | | | | SplitPageLayout | βœ… | | | | | | | Stack | βœ… | | | | | | | SubNav | βœ… | | | | @@ -129,27 +129,22 @@ To do this, we will: | | | Token | | | | | | | | Tooltip | | | | | | | | Truncate | | | | | -| | Utility | | | | | | -| | | AnchoredOverlay | | | | | +| | Utility | AnchoredOverlay | | | | | | | | VisuallyHidden | | | | | -| ./experimental | Compound | | | | | | -| | | Blankslate | | | | | -| | | Card | | | | | -| | | DataTable | | | | | -| | | Dialog | | | | | -| | | FilteredActionList | | | | | -| | | KeybindingHint | | | | | -| | | SelectPanel | | | | | -| | | Stack | | | | | -| | | Table | | | | | -| | | Tabs | | | | | -| | | UnderlinePanels | | | | | -| | Leaf | | | | | | -| | | InlineMessage | | | | | +| ./experimental | Compound | Blankslate | βœ… | | | | +| | | Card | βœ… | | | | +| | | DataTable | | | βœ… | | +| | | FilteredActionList | | | βœ… | | +| | | KeybindingHint | | | βœ… | | +| | | SelectPanel | βœ… | | | | +| | | Stack | βœ… | | | | +| | | Table | βœ… | | | | +| | | Tabs | βœ… | βœ… | | | +| | | UnderlinePanels | βœ… | | | | +| | Leaf | InlineMessage | | | | | | | | IssueLabel | | | | | | | | SkeletonAvatar | | | | | | | | SkeletonText | | | | | | | | Tooltip | | | | | | | | TopicTag | | | | | -| | Utility | | | | | | -| | | Hidden | | | | | +| | Utility | Hidden | | | | | From e38e50f51a87c28782d135b8c15032ad61590f52 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 19 Jun 2026 11:07:25 -0500 Subject: [PATCH 06/38] docs: add sections for low-level and spectrum docs --- contributor-docs/adrs/adr-024-modular-ds.md | 175 ++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index cbc691af026..be4a1ac6a95 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -61,8 +61,183 @@ To do this, we will: ### Delivering low-level components, hooks, and utilities +We will immediately deliver high priority components that assist in building accessible experiences, including: + +- **Combobox** for filtering and selection-based experiences +- **Dialog** for modal and nonmodal experiences with built in parts for + wiring up accessibility, behaviors, and semantics +- **Listbox** for selection-based experiences +- **Menu** for action-based experiences +- **Popover** for positioned, transient experiences +- **ScrollableRegion** for scrollable containers with built in accessibility and behaviors +- **Tabs** for building tabs-based experiences +- **ToggleButton**, **ToggleSwitch** for building toggle-based experiences + +We will also deliver documentation for all existing low-level components, +hooks, and utilities. + +In the future, we are investigating the following opportunities for offering +additional low-level components, hooks, and utilities: + +- useFilter +- useSelection + +#### Audit + +| Entrypoint | Type | Name | +| :------------- | :-------- | :--------------------------- | +| . | component | VisuallyHidden | +| | | Announce | +| | | AriaStatus | +| | | AriaAlert | +| | hook | useAnchoredPosition | +| | | useColorSchemeVar | +| | | useConfirm | +| | | useDetails | +| | | useFocusTrap | +| | | useFocusZone | +| | | useFormControlForwardedProps | +| | | useId | +| | | useIsomorphicLayoutEffect | +| | | useMergedRefs | +| | | useOnEscapePress | +| | | useOnOutsideClick | +| | | useOpenAndCloseFocus | +| | | useOverlay | +| | | useProvidedRefOrCreate | +| | | useRefObjectAsForwardedRef | +| | | useResizeObserver | +| | | useResponsiveValue | +| | | useRovingTabIndex | +| | | useSafeTimeout | +| | | useSlots | +| | | useSyncedState | +| | | useTheme | +| ./experimental | component | Hidden | +| | | Tabs | +| | hook | useSlots | +| | | useOverflow | +| | | useTab | +| | | useTabList | +| | | useTabPanel | + ### Embracing the spectrum of abstraction model +We will author components with a spectrum of abstraction in mind, specifically +beginning with presentational components that can combine with hooks to provide +opionated, config-driven components. + +This model allows us to offer opinionated defaults that work for common +scenarios while still allow teams to extend and customize components as needed. + +Default to building presentational components, then add behavior through hooks. +As opinions or defaults are established, create config-driven components that +combine presentational components and hooks to offer a higher level of +abstraction for common use-cases. + +#### Config components + +These components are "all-in-one"; they provide a high level of abstraction that +support common use-cases making it simple to quickly build out established +patterns and experiences. However, they are often inflexible and difficult to +extend. + +```tsx + { + /* ... */ + }} + onFilter={query => { + /* ... */ + }} +/> +``` + +#### Presentational components + +These components provide a lower level of abstraction and are more flexible and extensible, but require more work to build out common patterns and experiences. + +```tsx +function Example({items}) { + const [state, actions] = useList({ + defaultSelected: [], + }) + + return ( + + { + actions.updateQuery(query) + }} + /> + {items + .filter(item => item.label.includes(state.query)) + .map(item => { + return ( + { + actions.toggleSelect(item.label) + }} + > + + + + {item.label} + + ) + })} + + ) +} +``` + +Presentational components are accompanied by behavior or state hooks for a +component's core functionality. Often times, config components are built by +combining presentational components and the corresponding behavior and state +hooks needed for a feature. + +#### Base components + +Base components are unstyled and may optionally provide behavior. These +components are fundamental primitives that are used to build components, such as +accessibility primitives. + +```tsx +function Example() { + return ( + + + + + + + + ) +} +``` + +Other examples include: + +- Combobox (filtering, selection) +- Listbox (selection) +- Popover +- Tabs +- Treeview + +#### Utilities + +There are core utilities, hooks, functions, etc that are commonly used when +building out components or React applications. We provide hooks for established +patterns so that teams can build on solid foundations when authoring new +experiences. These can include hooks such as `useMergedRefs`, +`useOnEscapePress`, `useTimeout` and more. + +These utilities may extend beyond hooks, such as `@primer/behaviors` or custom +elements, where appropriate. + ## Consequences ## Alternatives From 429d646cbe005945bd4ddfcd8e1aa7c66efb7afe Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 19 Jun 2026 11:44:44 -0500 Subject: [PATCH 07/38] Add Disclosure component for content visibility --- contributor-docs/adrs/adr-024-modular-ds.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index be4a1ac6a95..e83de99e7f3 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -66,6 +66,7 @@ We will immediately deliver high priority components that assist in building acc - **Combobox** for filtering and selection-based experiences - **Dialog** for modal and nonmodal experiences with built in parts for wiring up accessibility, behaviors, and semantics +- **Disclosure** for showing/hiding content - **Listbox** for selection-based experiences - **Menu** for action-based experiences - **Popover** for positioned, transient experiences From 81c03ec1973a4ecf3af9c71dc89d002306ad6a6e Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 22 Jun 2026 13:27:00 -0500 Subject: [PATCH 08/38] Update list of breaking components in ADR-024 Added components to the list of breaking components. --- contributor-docs/adrs/adr-024-modular-ds.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index e83de99e7f3..64a04541027 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -59,6 +59,11 @@ To do this, we will: ### Breaking components down into parts +- SelectPanel +- Dialog +- ConfirmationDialog +- Pagination + ### Delivering low-level components, hooks, and utilities We will immediately deliver high priority components that assist in building accessible experiences, including: From 2e25fbe0ab2322e3f2e5c285ec071ea3cb595e80 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 23 Jun 2026 15:51:20 -0500 Subject: [PATCH 09/38] docs: update breaking into parts section and low-level section --- contributor-docs/adrs/adr-024-modular-ds.md | 23 +++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 64a04541027..5d74c6cf3f2 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -59,25 +59,25 @@ To do this, we will: ### Breaking components down into parts +We will break down the following components into parts plus appropriate +behavioral hooks. These changes will make it easier for teams to layer behavior +on top of these components. + - SelectPanel -- Dialog -- ConfirmationDialog -- Pagination +- Dialog (and ConfirmationDialog) +- DataTable (write documentation for `useTable` and `Table.*` components) ### Delivering low-level components, hooks, and utilities We will immediately deliver high priority components that assist in building accessible experiences, including: -- **Combobox** for filtering and selection-based experiences - **Dialog** for modal and nonmodal experiences with built in parts for - wiring up accessibility, behaviors, and semantics -- **Disclosure** for showing/hiding content -- **Listbox** for selection-based experiences -- **Menu** for action-based experiences -- **Popover** for positioned, transient experiences - **ScrollableRegion** for scrollable containers with built in accessibility and behaviors +- **Popover** for positioned, transient experiences - **Tabs** for building tabs-based experiences - **ToggleButton**, **ToggleSwitch** for building toggle-based experiences +- **Disclosure** for showing/hiding content +- **Accordion** for showing/hiding content We will also deliver documentation for all existing low-level components, hooks, and utilities. @@ -85,6 +85,11 @@ hooks, and utilities. In the future, we are investigating the following opportunities for offering additional low-level components, hooks, and utilities: +- **Listbox** for selection-based experiences +- **Combobox** for filtering and selection-based experiences + wiring up accessibility, behaviors, and semantics +- **Menu** for action-based experiences +- \*_Tree_ for tree-based experiences - useFilter - useSelection From 29c322750900bb20510c994a3b55ce3b54472b9a Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 23 Jun 2026 15:54:11 -0500 Subject: [PATCH 10/38] docs: update syntax --- contributor-docs/adrs/adr-024-modular-ds.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 5d74c6cf3f2..0c09746ce0a 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -89,7 +89,7 @@ additional low-level components, hooks, and utilities: - **Combobox** for filtering and selection-based experiences wiring up accessibility, behaviors, and semantics - **Menu** for action-based experiences -- \*_Tree_ for tree-based experiences +- **Tree** for tree-based experiences - useFilter - useSelection From d69d2c8f60e870e32291aec57d544af16a1076eb Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Jun 2026 15:58:39 -0500 Subject: [PATCH 11/38] Apply suggestions from code review Co-authored-by: Brittany L. Houtz <55068883+llastflowers@users.noreply.github.com> --- contributor-docs/adrs/adr-024-modular-ds.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 0c09746ce0a..262025ed39f 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -46,15 +46,15 @@ want to focus on in order to improve Primer: - Embrace layer 2 from the layer model, offering parts for our components that can be combined with hooks for improved flexibility - Provide low-level components and hooks to make it easier to build - quality, accessible experience -- Create a model for upstreaming work where people build on top of Primer + quality, accessible experiences +- Create a model for upstreaming work where people have already built on top of Primer To do this, we will: -- Identify and deliver components to break down into parts that can be used to create more +- Identify and deliver components that can be broken down into parts, which can be used to create more flexible and extensible components - Identify and deliver low-level components, hooks, and utilities that can be used to quickly build out accessible experiences -- Embrace the spectrum of abstraction model, authoring presentation components +- Embrace the spectrum of abstraction model, authoring presentational components that combine with hooks to provide flexible patterns and opinionated defaults. ### Breaking components down into parts @@ -69,10 +69,10 @@ on top of these components. ### Delivering low-level components, hooks, and utilities -We will immediately deliver high priority components that assist in building accessible experiences, including: +We will immediately deliver high-priority components that will assist in building accessible experiences, including: -- **Dialog** for modal and nonmodal experiences with built in parts for -- **ScrollableRegion** for scrollable containers with built in accessibility and behaviors +- **Dialog** for modal and nonmodal experiences with built-in parts for +- **ScrollableRegion** for scrollable containers with built-in accessibility and behaviors - **Popover** for positioned, transient experiences - **Tabs** for building tabs-based experiences - **ToggleButton**, **ToggleSwitch** for building toggle-based experiences @@ -139,7 +139,7 @@ beginning with presentational components that can combine with hooks to provide opionated, config-driven components. This model allows us to offer opinionated defaults that work for common -scenarios while still allow teams to extend and customize components as needed. +scenarios while still allowing teams to extend and customize components as needed. Default to building presentational components, then add behavior through hooks. As opinions or defaults are established, create config-driven components that @@ -206,8 +206,8 @@ function Example({items}) { ``` Presentational components are accompanied by behavior or state hooks for a -component's core functionality. Often times, config components are built by -combining presentational components and the corresponding behavior and state +component's core functionality. Oftentimes, config components are built by +combining presentational components with the corresponding behavior and state hooks needed for a feature. #### Base components From 0d439acb2c78dc37f16ad24e0ccf1682a5dcc1c4 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Jun 2026 16:02:10 -0500 Subject: [PATCH 12/38] Apply suggestion from @llastflowers Co-authored-by: Brittany L. Houtz <55068883+llastflowers@users.noreply.github.com> --- contributor-docs/adrs/adr-024-modular-ds.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 262025ed39f..1ee86358a7b 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -24,7 +24,7 @@ details several layers that Primer could be structured into: We took this proposal into a Modular design System Workshop: https://github.com/github/primer/discussions/6703. In this workshop, we -identified several scenarios that team run into challenges when building with +identified several scenarios where teams run into challenges when building with Primer: - It can be difficult to extend or add new functionality to a component From 3fca49093e52d08338973f08ddd2d8271e7ce4c4 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Jun 2026 16:38:44 -0500 Subject: [PATCH 13/38] chore: update adr --- contributor-docs/adrs/adr-024-modular-ds.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 0c09746ce0a..25f84b6f3c2 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -66,6 +66,7 @@ on top of these components. - SelectPanel - Dialog (and ConfirmationDialog) - DataTable (write documentation for `useTable` and `Table.*` components) +- ActionList (break down into list primitives) ### Delivering low-level components, hooks, and utilities From d72d0c7c0673628da477af0026c11753b9720f56 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Jun 2026 16:39:21 -0500 Subject: [PATCH 14/38] docs: update adr description for dialog --- contributor-docs/adrs/adr-024-modular-ds.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 96e70e47432..86b689f6b53 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -72,7 +72,8 @@ on top of these components. We will immediately deliver high-priority components that will assist in building accessible experiences, including: -- **Dialog** for modal and nonmodal experiences with built-in parts for +- **Dialog** for modal and nonmodal experiences with built-in parts for dialog + headings, content, and actions - **ScrollableRegion** for scrollable containers with built-in accessibility and behaviors - **Popover** for positioned, transient experiences - **Tabs** for building tabs-based experiences From a486d2057f8d179e0490c0320237520c053214d4 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 17 Jun 2026 11:40:57 -0500 Subject: [PATCH 15/38] docs: add initial modular design system draft --- contributor-docs/adrs/adr-024-modular-ds.md | 56 +++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 contributor-docs/adrs/adr-024-modular-ds.md diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md new file mode 100644 index 00000000000..1c38c2fd3ef --- /dev/null +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -0,0 +1,56 @@ +# Modular Design System + +πŸ“† Date: 2026-05-17 + +## Status + +| Stage | State | +| -------------- | ----------- | +| Status | Proposed ⚠️ | +| Implementation | Pending ⚠️ | + +## Context + +There is a proposal for enabling better pattern creation via modular building +blocks in: https://github.com/github/core-ux/issues/2238. In this proposal, it +details several layers that Primer could be structured into: + +- Layer 3: Ready-made, props-based components that are ready to use +- Layer 2: Parts, presentiontational-style components that one combines with hooks to + create components +- Layer 1: Foundations, unstyled primitives to allow for full markup and style + control +- Layer 0: Hooks, state management, and other logic that can be used to build components + +We took this proposal into a Modular design System Workshop: +https://github.com/github/primer/discussions/6703. In this workshop, we +identified several scenarios that team run into challenges when building with +Primer: + +- It can be difficult to extend or add new functionality to a component +- It can be difficult to customize the appearance, content, or semantics of a + component +- It can be difficult to build new experiences using established patterns (like accessibility primitives) + +These challenges lead to custom implementations, forks, or overrides of Primer +components that reuslt in adoption challenges and ecosystem fragmentation. + +As a result, we applied the layer model to several situations in a +[FigJam](https://www.figma.com/board/mgApQG7vL3imdUIhgyhG2n/Modular-DS-Workshop?node-id=84-152&t=dBKm8jx1UsKRXyzu-1). From this work, we pulled out insights about this model that we want to adopt in Primer today. + +## Decision + +From the Modular Design System workshop, we identified several key areas that we +want to focus on in order to improve Primer: + +- Embrace layer 2 from the layer model, offering parts for our components that + can be combined with hooks for improved flexibility +- Provide low-level components and hooks to make it easier to build + quality, accessible experience +- Create a model for upstreaming work where people build on top of Primer + +### Embracing layer 2 + +### Low-level components and hooks + +### Model for upstreaming work From 52040e20f286940b8645d2c41eab2482d9e2907e Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 17 Jun 2026 11:56:22 -0500 Subject: [PATCH 16/38] docs: add initial support tables --- contributor-docs/adrs/adr-024-modular-ds.md | 119 ++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 1c38c2fd3ef..e2540a2f05c 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -51,6 +51,125 @@ want to focus on in order to improve Primer: ### Embracing layer 2 +| Entrypoint | Component | Props | Parts | Hooks | Notes | +| :------------- | :------------------ | :---- | :---- | :---- | :---- | +| . | ActionBar | | | | | +| | ActionList | | | | | +| | ActionMenu | | | | | +| | AnchoredOverlay | | | | | +| | Autocomplete | | | | | +| | Avatar | | | | | +| | AvatarStack | | | | | +| | Banner | | | | | +| | BaseStyles | | | | | +| | BranchName | | | | | +| | Breadcrumb | | | | | +| | Breadcrumbs | | | | | +| | Button | | | | | +| | ButtonGroup | | | | | +| | Checkbox | | | | | +| | CheckboxGroup | | | | | +| | CircleBadge | | | | | +| | ConfirmationDialog | | | | | +| | CounterLabel | | | | | +| | Details | | | | | +| | Dialog | | | | | +| | Flash | | | | | +| | FocusKeys | | | | | +| | FormControl | | | | | +| | Header | | | | | +| | Heading | | | | | +| | IconButton | | | | | +| | IssueLabelToken | | | | | +| | Label | | | | | +| | LabelGroup | | | | | +| | Link | | | | | +| | LinkButton | | | | | +| | NavList | | | | | +| | Overlay | | | | | +| | PageHeader | | | | | +| | PageLayout | | | | | +| | Pagination | | | | | +| | Popover | | | | | +| | Portal | | | | | +| | PortalContext | | | | | +| | ProgressBar | | | | | +| | Radio | | | | | +| | RadioGroup | | | | | +| | RelativeTime | | | | | +| | ResponsiveValue | | | | | +| | SegmentedControl | | | | | +| | Select | | | | | +| | SelectPanel | | | | | +| | SideNav | | | | | +| | SkeletonBox | | | | | +| | Spinner | | | | | +| | SplitPageLayout | | | | | +| | Stack | | | | | +| | StateLabel | | | | | +| | SubNav | | | | | +| | Text | | | | | +| | Textarea | | | | | +| | TextInput | | | | | +| | TextInputWithTokens | | | | | +| | ThemeProvider | | | | | +| | Timeline | | | | | +| | ToggleSwitch | | | | | +| | Token | | | | | +| | Tooltip | | | | | +| | TreeView | | | | | +| | Truncate | | | | | +| | UnderlineNav | | | | | +| | VisuallyHidden | | | | | +| ./experimental | ActionBar | | | | | +| | Announce | | | | | +| | AriaAlert | | | | | +| | AriaStatus | | | | | +| | Blankslate | | | | | +| | Card | | | | | +| | DataTable | | | | | +| | Dialog | | | | | +| | FilteredActionList | | | | | +| | Hidden | | | | | +| | InlineMessage | | | | | +| | IssueLabel | | | | | +| | KeybindingHint | | | | | +| | NavList | | | | | +| | PageHeader | | | | | +| | ScrollableRegion | | | | | +| | SelectPanel | | | | | +| | SkeletonAvatar | | | | | +| | SkeletonText | | | | | +| | Stack | | | | | +| | Table | | | | | +| | Tabs | | | | | +| | Tooltip | | | | | +| | TopicTag | | | | | +| | UnderlinePanels | | | | | + ### Low-level components and hooks +We want to offer more low-level components and hooks, in particular with a focus +on accessibility. Providing these low-level abstractions allows for quickly creating +robust experiences instead of having to re-invent them from scratch. + +In particular, we would like to offer the following primitives: + +| Type | Name | Supported | Description | Notes | +| :--------- | :---------------- | :-------- | :---------- | :---- | +| Components | Accordion | | | | +| | Combobox | | | | +| | Disclosure | | | | +| | Dialog | | | | +| | Listbox | | | | +| | Menu | | | | +| | Menubar | | | | +| | Popover | | | | +| | Tabs | | | | +| | Tree | | | | +| Hooks | useFocusTrap | | | | +| | useMergedRefs | | | | +| | useRovingTabIndex | | | | +| | useTabs | | | | + ### Model for upstreaming work From 045c297fb7ded311ceb96ef6bbd90a53b4faabf0 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 17 Jun 2026 12:11:20 -0500 Subject: [PATCH 17/38] docs: begin filling out tables --- contributor-docs/adrs/adr-024-modular-ds.md | 308 +++++++++++++------- 1 file changed, 197 insertions(+), 111 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index e2540a2f05c..0039f4dfaab 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -49,103 +49,112 @@ want to focus on in order to improve Primer: quality, accessible experience - Create a model for upstreaming work where people build on top of Primer +In addition, when authoring components we will offer presentational components +and behavioral hooks that can then be used for higher-level, props-based +components. + ### Embracing layer 2 -| Entrypoint | Component | Props | Parts | Hooks | Notes | -| :------------- | :------------------ | :---- | :---- | :---- | :---- | -| . | ActionBar | | | | | -| | ActionList | | | | | -| | ActionMenu | | | | | -| | AnchoredOverlay | | | | | -| | Autocomplete | | | | | -| | Avatar | | | | | -| | AvatarStack | | | | | -| | Banner | | | | | -| | BaseStyles | | | | | -| | BranchName | | | | | -| | Breadcrumb | | | | | -| | Breadcrumbs | | | | | -| | Button | | | | | -| | ButtonGroup | | | | | -| | Checkbox | | | | | -| | CheckboxGroup | | | | | -| | CircleBadge | | | | | -| | ConfirmationDialog | | | | | -| | CounterLabel | | | | | -| | Details | | | | | -| | Dialog | | | | | -| | Flash | | | | | -| | FocusKeys | | | | | -| | FormControl | | | | | -| | Header | | | | | -| | Heading | | | | | -| | IconButton | | | | | -| | IssueLabelToken | | | | | -| | Label | | | | | -| | LabelGroup | | | | | -| | Link | | | | | -| | LinkButton | | | | | -| | NavList | | | | | -| | Overlay | | | | | -| | PageHeader | | | | | -| | PageLayout | | | | | -| | Pagination | | | | | -| | Popover | | | | | -| | Portal | | | | | -| | PortalContext | | | | | -| | ProgressBar | | | | | -| | Radio | | | | | -| | RadioGroup | | | | | -| | RelativeTime | | | | | -| | ResponsiveValue | | | | | -| | SegmentedControl | | | | | -| | Select | | | | | -| | SelectPanel | | | | | -| | SideNav | | | | | -| | SkeletonBox | | | | | -| | Spinner | | | | | -| | SplitPageLayout | | | | | -| | Stack | | | | | -| | StateLabel | | | | | -| | SubNav | | | | | -| | Text | | | | | -| | Textarea | | | | | -| | TextInput | | | | | -| | TextInputWithTokens | | | | | -| | ThemeProvider | | | | | -| | Timeline | | | | | -| | ToggleSwitch | | | | | -| | Token | | | | | -| | Tooltip | | | | | -| | TreeView | | | | | -| | Truncate | | | | | -| | UnderlineNav | | | | | -| | VisuallyHidden | | | | | -| ./experimental | ActionBar | | | | | -| | Announce | | | | | -| | AriaAlert | | | | | -| | AriaStatus | | | | | -| | Blankslate | | | | | -| | Card | | | | | -| | DataTable | | | | | -| | Dialog | | | | | -| | FilteredActionList | | | | | -| | Hidden | | | | | -| | InlineMessage | | | | | -| | IssueLabel | | | | | -| | KeybindingHint | | | | | -| | NavList | | | | | -| | PageHeader | | | | | -| | ScrollableRegion | | | | | -| | SelectPanel | | | | | -| | SkeletonAvatar | | | | | -| | SkeletonText | | | | | -| | Stack | | | | | -| | Table | | | | | -| | Tabs | | | | | -| | Tooltip | | | | | -| | TopicTag | | | | | -| | UnderlinePanels | | | | | +- Parts provide a clear way for teams to layer on functionality that does not + exist +- It provides a clear path for upstreaming +- The biggest gap is providing behavioral/state hooks for components (if needed) + +`N/A` means that layer is not a useful fit for the component's API shape. + +| Entrypoint | Component | Props | Parts | Hooks | Notes | +| :------------- | :------------------ | :---- | :---- | :---- | :---------------------------------------------------------------------------------------------------------------- | +| . | ActionBar | N/A | Yes | No | Parts include `IconButton`, `Divider`, `Group`, and `Menu`; no public ActionBar behavior hook. | +| | ActionList | N/A | Yes | No | Compound list API with item, group, divider, visual, description, and action parts. | +| | ActionMenu | No | Yes | No | Parts include `Button`, `Anchor`, `Overlay`, and `Divider`; menu contents use `ActionList`. | +| | AnchoredOverlay | Yes | N/A | Yes | Props-driven overlay; behavior can be recreated with `useAnchoredPosition` and `useOverlay`. | +| | Autocomplete | No | Yes | No | Slot API exposes `Input`, `Menu`, and `Overlay`; no public autocomplete hook. | +| | Avatar | Yes | N/A | N/A | Leaf visual component. | +| | AvatarStack | Yes | N/A | No | Props-driven wrapper around `Avatar` children; no public stack behavior hook. | +| | Banner | Yes | Yes | N/A | Supports title, description, and action props plus matching parts. | +| | BaseStyles | Yes | N/A | N/A | Props-driven application wrapper. | +| | BranchName | Yes | N/A | N/A | Leaf display component. | +| | Breadcrumb | N/A | Yes | N/A | Deprecated alias of `Breadcrumbs` with `Item`. | +| | Breadcrumbs | N/A | Yes | N/A | Uses `Breadcrumbs.Item`; no props-only item list API. | +| | Button | Yes | N/A | N/A | Leaf action component. | +| | ButtonGroup | Yes | N/A | N/A | Props-driven grouping wrapper for button children. | +| | Checkbox | Yes | N/A | N/A | Native input wrapper. | +| | CheckboxGroup | N/A | Yes | No | Uses label, caption, and validation parts through the shared choice-group implementation. | +| | ConfirmationDialog | Yes | N/A | Yes | Props-driven dialog with `useConfirm` behavior hook. | +| | CounterLabel | Yes | N/A | N/A | Leaf count display component. | +| | Details | N/A | Yes | Yes | Exposes `Summary` plus `useDetails`. | +| | Dialog | Yes | Yes | No | Props-driven dialog also exposes parts for custom renderers; `useDialog` is internal. | +| | Flash | Yes | N/A | N/A | Props-driven message component. | +| | FocusKeys | N/A | N/A | Yes | Not a component; exported behavior constant used with `useFocusZone`. | +| | FormControl | N/A | Yes | Yes | Parts include `Label`, `Caption`, `LeadingVisual`, and `Validation`; hook forwards form-control props. | +| | Header | N/A | Yes | N/A | Structural API exposes `Item` and `Link`. | +| | Heading | Yes | N/A | N/A | Leaf typography component. | +| | IconButton | Yes | N/A | N/A | Leaf action component. | +| | IssueLabelToken | Yes | N/A | N/A | Props-driven token variant. | +| | Label | Yes | N/A | N/A | Leaf display component. | +| | LabelGroup | Yes | N/A | N/A | Props-driven grouping wrapper for labels. | +| | Link | Yes | N/A | N/A | Leaf navigation component. | +| | LinkButton | Yes | N/A | N/A | Leaf action/navigation component. | +| | NavList | N/A | Yes | No | Compound navigation API with item, group, visual, divider, and subnav parts. | +| | Overlay | Yes | N/A | Yes | Props-driven floating surface backed by `useOverlay`. | +| | PageHeader | N/A | Yes | N/A | Structural API exposes title, context, action, visual, description, and navigation parts. | +| | PageLayout | N/A | Yes | No | Layout parts include `Header`, `Content`, `Pane`, `Sidebar`, and `Footer`; no public layout hook. | +| | Pagination | Yes | N/A | No | Props-driven pagination model; no public pagination hook. | +| | Popover | Yes | Yes | N/A | Props-driven wrapper with `Content` part. | +| | Portal | Yes | N/A | N/A | Low-level rendering utility. | +| | PortalContext | N/A | N/A | N/A | Context export rather than a component API. | +| | ProgressBar | Yes | Yes | N/A | Props-driven bar with `Item` for segmented progress. | +| | Radio | Yes | N/A | N/A | Native input wrapper. | +| | RadioGroup | N/A | Yes | No | Uses label, caption, and validation parts through the shared choice-group implementation. | +| | RelativeTime | Yes | N/A | N/A | Props-driven time display. | +| | ResponsiveValue | N/A | N/A | Yes | Type-level API paired with `useResponsiveValue`. | +| | SegmentedControl | N/A | Yes | No | Compound control with `Button` and `IconButton` parts; no public segmented-control hook. | +| | Select | Yes | Yes | N/A | Native select wrapper with `Option` and `OptGroup` parts. | +| | SelectPanel | Yes | Yes | No | Props-driven selection panel; parts cover messages and secondary actions, not the full behavior. | +| | SideNav | N/A | Yes | N/A | Deprecated structural nav with `Link` part. | +| | SkeletonBox | Yes | N/A | N/A | Leaf skeleton component. | +| | Spinner | Yes | N/A | N/A | Leaf loading component. | +| | SplitPageLayout | N/A | Yes | No | Layout parts mirror `PageLayout`; no public layout hook. | +| | Stack | Yes | Yes | N/A | Props-driven layout primitive with optional `Item` part. | +| | StateLabel | Yes | N/A | N/A | Leaf display component. | +| | SubNav | N/A | Yes | N/A | Structural nav with `Link` and `Links` parts. | +| | Text | Yes | N/A | N/A | Leaf typography component. | +| | Textarea | Yes | N/A | N/A | Native input wrapper. | +| | TextInput | Yes | Yes | N/A | Props-driven input with `Action` as an optional part. | +| | TextInputWithTokens | Yes | N/A | No | Props-driven tokenized input; no public token-input behavior hook. | +| | ThemeProvider | Yes | N/A | Yes | Provider component plus `useTheme` and `useColorSchemeVar`. | +| | Timeline | N/A | Yes | N/A | Structural timeline with item, avatar, badge, body, break, and action parts. | +| | ToggleSwitch | Yes | N/A | N/A | Props-driven switch component. | +| | Token | Yes | N/A | N/A | Leaf token component. | +| | Tooltip | Yes | N/A | No | Props-driven tooltip; no public tooltip behavior hook. | +| | TreeView | N/A | Yes | Yes | Compound tree API with item/subtree/visual parts and `useRovingTabIndex`. | +| | Truncate | Yes | N/A | N/A | Props-driven text utility. | +| | UnderlineNav | N/A | Yes | N/A | Structural nav with `Item` part. | +| | VisuallyHidden | Yes | N/A | N/A | Leaf accessibility utility. | +| ./experimental | Announce | Yes | N/A | N/A | Props-driven live-region primitive; no public announcement hook (available through `@primer/live-region-element` | +| | AriaAlert | Yes | N/A | N/A | Props-driven assertive live-region wrapper. | +| | AriaStatus | Yes | N/A | N/A | Props-driven polite live-region wrapper. | +| | Blankslate | N/A | Yes | N/A | Parts-first API with visual, heading, description, and action parts. | +| | Card | N/A | Yes | N/A | Parts-first card with icon, image, heading, description, action, and metadata parts. | +| | DataTable | Yes | Yes | Yes | Props-driven data grid; presentational table parts are exposed through `Table`. Hook is `useTable` | +| | Dialog | Yes | Yes | No | Same public API shape as the root export. | +| | FilteredActionList | Yes | Yes | No | Props-driven filtered list with `Input` and `BodyLoader` parts. | +| | Hidden | N/A | Yes | N/A | Props-driven responsive visibility utility. | +| | InlineMessage | N/A | Yes | N/A | Leaf message component. | +| | IssueLabel | N/A | Yes | N/A | Props-driven issue label component. | +| | KeybindingHint | Yes | N/A | N/A | Props-driven text utility for keybindings. | +| | NavList | N/A | Yes | No | Same public API shape as the root export. | +| | PageHeader | N/A | Yes | N/A | Same public API shape as the root export. | +| | ScrollableRegion | Yes | N/A | N/A | Props-driven accessibility wrapper. | +| | SelectPanel | N/A | Yes | No | Experimental parts-first panel with button, header, search, footer, loading, message, and secondary action parts. | +| | SkeletonAvatar | Yes | N/A | N/A | Leaf skeleton component. | +| | SkeletonText | Yes | N/A | N/A | Leaf skeleton component. | +| | Stack | Yes | Yes | N/A | Same public API shape as the root export. | +| | Table | N/A | Yes | N/A | Presentational table parts for `DataTable`. | +| | Tabs | N/A | Yes | Yes | Experimental primitive exposes `TabList`, `Tab`, `TabPanel`, and related hooks. | +| | Tooltip | Yes | N/A | No | Same public API shape as the root export. | +| | TopicTag | Yes | Yes | N/A | Props-driven tag with `Group` part. | +| | UnderlinePanels | N/A | Yes | No | Parts-first tab panel API with `Tab` and `Panel`; no public hook. | ### Low-level components and hooks @@ -155,21 +164,98 @@ robust experiences instead of having to re-invent them from scratch. In particular, we would like to offer the following primitives: -| Type | Name | Supported | Description | Notes | -| :--------- | :---------------- | :-------- | :---------- | :---- | -| Components | Accordion | | | | -| | Combobox | | | | -| | Disclosure | | | | -| | Dialog | | | | -| | Listbox | | | | -| | Menu | | | | -| | Menubar | | | | -| | Popover | | | | -| | Tabs | | | | -| | Tree | | | | -| Hooks | useFocusTrap | | | | -| | useMergedRefs | | | | -| | useRovingTabIndex | | | | -| | useTabs | | | | +| Type | Name | Supported | Description | Notes | +| :--------- | :---------------- | :-------- | :----------------------------------------- | :-------------------------------------------------------------------------------------------------- | +| Components | Accordion | No | Disclosure group primitive. | Not exposed today. | +| | Combobox | No | Text input plus listbox popup primitive. | Autocomplete and SelectPanel cover product use cases, but no generic combobox primitive is exposed. | +| | Disclosure | No | Show/hide primitive. | `Details` and `useDetails` exist, but there is no named `Disclosure` primitive. | +| | Dialog | Yes | Modal dialog primitive. | `Dialog` exposes props and custom-renderer parts. | +| | Listbox | No | Option list primitive. | ActionList and SelectPanel cover product use cases, but no generic listbox primitive is exposed. | +| | Menu | No | Menu button and popup primitive. | ActionMenu covers product use cases, but no generic menu primitive is exposed. | +| | Menubar | No | Horizontal menu primitive. | Not exposed today. | +| | Popover | Yes | Anchored floating content primitive. | Covered by `Popover`, `Overlay`, and `AnchoredOverlay`. | +| | Tabs | Yes | Tab state and tab/panel primitive. | Available from `./experimental` with parts and hooks. | +| | Tree | Yes | Tree navigation primitive. | Covered by `TreeView`; no separate unstyled `Tree` primitive is exposed. | +| Hooks | useFocusTrap | Yes | Trap focus within a container. | Exported from the root entrypoint. | +| | useMergedRefs | Yes | Merge multiple refs into one ref callback. | Exported from the root entrypoint. | +| | useRovingTabIndex | Yes | Manage roving tab index behavior. | Exported from the root entrypoint via `TreeView`. | +| | useTabs | Yes | Manage tab state and ARIA wiring. | Available from `./experimental` alongside `useTab`, `useTabList`, and `useTabPanel`. | ### Model for upstreaming work + +### Authoring components + +#### Config components + +These components are "all-in-one"; they provide a high level of abstraction that +support common use-cases making it simple to quickly build out established +patterns and experiences. However, they are often inflexible and difficult to +extend. + +````tsx + { + /* ... */ + }} + onFilter={query => { + /* ... */ + }} +/> + +#### Presentational components + +These components provide a lower level of abstraction and are more flexible and extensible, but require more work to build out common patterns and experiences. + +```tsx +function Example({items}) { + const [state, actions] = useList({ + defaultSelected: [], + }) + + return ( + + { + actions.updateQuery(query) + }} + /> + {items + .filter(item => item.label.includes(state.query)) + .map(item => { + return ( + { + actions.toggleSelect(item.label) + }} + > + + + + {item.label} + + ) + })} + + ) +} +```` + +Presentational components are accompanied by behavior or state hooks for a +component's core functionality. Often times, config components are built by +combining presentational components and the corresponding behavior and state +hooks needed for a feature. + +## Consequences + +TODO + +## Alternatives + +TODO + +## Questions + +TODO From 3fc010aa2e3b5e858a23f04be0482e2b76d7a161 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 19 Jun 2026 10:44:16 -0500 Subject: [PATCH 18/38] docs: begin audit --- contributor-docs/adrs/adr-024-modular-ds.md | 292 +++++++------------- 1 file changed, 93 insertions(+), 199 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 0039f4dfaab..cbb9fcdb008 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -49,213 +49,107 @@ want to focus on in order to improve Primer: quality, accessible experience - Create a model for upstreaming work where people build on top of Primer -In addition, when authoring components we will offer presentational components -and behavioral hooks that can then be used for higher-level, props-based -components. +To do this, we will: -### Embracing layer 2 +- Identify and deliver components to break down into parts that can be used to create more + flexible and extensible components +- Identify and deliver low-level components, hooks, and utilities that can be used to quickly build out accessible experiences +- Embrace the spectrum of abstraction model, authoring presentation components + that combine with hooks to provide flexible patterns and opinionated defaults. -- Parts provide a clear way for teams to layer on functionality that does not - exist -- It provides a clear path for upstreaming -- The biggest gap is providing behavioral/state hooks for components (if needed) +### Breaking components down into parts -`N/A` means that layer is not a useful fit for the component's API shape. +### Delivering low-level components, hooks, and utilities -| Entrypoint | Component | Props | Parts | Hooks | Notes | -| :------------- | :------------------ | :---- | :---- | :---- | :---------------------------------------------------------------------------------------------------------------- | -| . | ActionBar | N/A | Yes | No | Parts include `IconButton`, `Divider`, `Group`, and `Menu`; no public ActionBar behavior hook. | -| | ActionList | N/A | Yes | No | Compound list API with item, group, divider, visual, description, and action parts. | -| | ActionMenu | No | Yes | No | Parts include `Button`, `Anchor`, `Overlay`, and `Divider`; menu contents use `ActionList`. | -| | AnchoredOverlay | Yes | N/A | Yes | Props-driven overlay; behavior can be recreated with `useAnchoredPosition` and `useOverlay`. | -| | Autocomplete | No | Yes | No | Slot API exposes `Input`, `Menu`, and `Overlay`; no public autocomplete hook. | -| | Avatar | Yes | N/A | N/A | Leaf visual component. | -| | AvatarStack | Yes | N/A | No | Props-driven wrapper around `Avatar` children; no public stack behavior hook. | -| | Banner | Yes | Yes | N/A | Supports title, description, and action props plus matching parts. | -| | BaseStyles | Yes | N/A | N/A | Props-driven application wrapper. | -| | BranchName | Yes | N/A | N/A | Leaf display component. | -| | Breadcrumb | N/A | Yes | N/A | Deprecated alias of `Breadcrumbs` with `Item`. | -| | Breadcrumbs | N/A | Yes | N/A | Uses `Breadcrumbs.Item`; no props-only item list API. | -| | Button | Yes | N/A | N/A | Leaf action component. | -| | ButtonGroup | Yes | N/A | N/A | Props-driven grouping wrapper for button children. | -| | Checkbox | Yes | N/A | N/A | Native input wrapper. | -| | CheckboxGroup | N/A | Yes | No | Uses label, caption, and validation parts through the shared choice-group implementation. | -| | ConfirmationDialog | Yes | N/A | Yes | Props-driven dialog with `useConfirm` behavior hook. | -| | CounterLabel | Yes | N/A | N/A | Leaf count display component. | -| | Details | N/A | Yes | Yes | Exposes `Summary` plus `useDetails`. | -| | Dialog | Yes | Yes | No | Props-driven dialog also exposes parts for custom renderers; `useDialog` is internal. | -| | Flash | Yes | N/A | N/A | Props-driven message component. | -| | FocusKeys | N/A | N/A | Yes | Not a component; exported behavior constant used with `useFocusZone`. | -| | FormControl | N/A | Yes | Yes | Parts include `Label`, `Caption`, `LeadingVisual`, and `Validation`; hook forwards form-control props. | -| | Header | N/A | Yes | N/A | Structural API exposes `Item` and `Link`. | -| | Heading | Yes | N/A | N/A | Leaf typography component. | -| | IconButton | Yes | N/A | N/A | Leaf action component. | -| | IssueLabelToken | Yes | N/A | N/A | Props-driven token variant. | -| | Label | Yes | N/A | N/A | Leaf display component. | -| | LabelGroup | Yes | N/A | N/A | Props-driven grouping wrapper for labels. | -| | Link | Yes | N/A | N/A | Leaf navigation component. | -| | LinkButton | Yes | N/A | N/A | Leaf action/navigation component. | -| | NavList | N/A | Yes | No | Compound navigation API with item, group, visual, divider, and subnav parts. | -| | Overlay | Yes | N/A | Yes | Props-driven floating surface backed by `useOverlay`. | -| | PageHeader | N/A | Yes | N/A | Structural API exposes title, context, action, visual, description, and navigation parts. | -| | PageLayout | N/A | Yes | No | Layout parts include `Header`, `Content`, `Pane`, `Sidebar`, and `Footer`; no public layout hook. | -| | Pagination | Yes | N/A | No | Props-driven pagination model; no public pagination hook. | -| | Popover | Yes | Yes | N/A | Props-driven wrapper with `Content` part. | -| | Portal | Yes | N/A | N/A | Low-level rendering utility. | -| | PortalContext | N/A | N/A | N/A | Context export rather than a component API. | -| | ProgressBar | Yes | Yes | N/A | Props-driven bar with `Item` for segmented progress. | -| | Radio | Yes | N/A | N/A | Native input wrapper. | -| | RadioGroup | N/A | Yes | No | Uses label, caption, and validation parts through the shared choice-group implementation. | -| | RelativeTime | Yes | N/A | N/A | Props-driven time display. | -| | ResponsiveValue | N/A | N/A | Yes | Type-level API paired with `useResponsiveValue`. | -| | SegmentedControl | N/A | Yes | No | Compound control with `Button` and `IconButton` parts; no public segmented-control hook. | -| | Select | Yes | Yes | N/A | Native select wrapper with `Option` and `OptGroup` parts. | -| | SelectPanel | Yes | Yes | No | Props-driven selection panel; parts cover messages and secondary actions, not the full behavior. | -| | SideNav | N/A | Yes | N/A | Deprecated structural nav with `Link` part. | -| | SkeletonBox | Yes | N/A | N/A | Leaf skeleton component. | -| | Spinner | Yes | N/A | N/A | Leaf loading component. | -| | SplitPageLayout | N/A | Yes | No | Layout parts mirror `PageLayout`; no public layout hook. | -| | Stack | Yes | Yes | N/A | Props-driven layout primitive with optional `Item` part. | -| | StateLabel | Yes | N/A | N/A | Leaf display component. | -| | SubNav | N/A | Yes | N/A | Structural nav with `Link` and `Links` parts. | -| | Text | Yes | N/A | N/A | Leaf typography component. | -| | Textarea | Yes | N/A | N/A | Native input wrapper. | -| | TextInput | Yes | Yes | N/A | Props-driven input with `Action` as an optional part. | -| | TextInputWithTokens | Yes | N/A | No | Props-driven tokenized input; no public token-input behavior hook. | -| | ThemeProvider | Yes | N/A | Yes | Provider component plus `useTheme` and `useColorSchemeVar`. | -| | Timeline | N/A | Yes | N/A | Structural timeline with item, avatar, badge, body, break, and action parts. | -| | ToggleSwitch | Yes | N/A | N/A | Props-driven switch component. | -| | Token | Yes | N/A | N/A | Leaf token component. | -| | Tooltip | Yes | N/A | No | Props-driven tooltip; no public tooltip behavior hook. | -| | TreeView | N/A | Yes | Yes | Compound tree API with item/subtree/visual parts and `useRovingTabIndex`. | -| | Truncate | Yes | N/A | N/A | Props-driven text utility. | -| | UnderlineNav | N/A | Yes | N/A | Structural nav with `Item` part. | -| | VisuallyHidden | Yes | N/A | N/A | Leaf accessibility utility. | -| ./experimental | Announce | Yes | N/A | N/A | Props-driven live-region primitive; no public announcement hook (available through `@primer/live-region-element` | -| | AriaAlert | Yes | N/A | N/A | Props-driven assertive live-region wrapper. | -| | AriaStatus | Yes | N/A | N/A | Props-driven polite live-region wrapper. | -| | Blankslate | N/A | Yes | N/A | Parts-first API with visual, heading, description, and action parts. | -| | Card | N/A | Yes | N/A | Parts-first card with icon, image, heading, description, action, and metadata parts. | -| | DataTable | Yes | Yes | Yes | Props-driven data grid; presentational table parts are exposed through `Table`. Hook is `useTable` | -| | Dialog | Yes | Yes | No | Same public API shape as the root export. | -| | FilteredActionList | Yes | Yes | No | Props-driven filtered list with `Input` and `BodyLoader` parts. | -| | Hidden | N/A | Yes | N/A | Props-driven responsive visibility utility. | -| | InlineMessage | N/A | Yes | N/A | Leaf message component. | -| | IssueLabel | N/A | Yes | N/A | Props-driven issue label component. | -| | KeybindingHint | Yes | N/A | N/A | Props-driven text utility for keybindings. | -| | NavList | N/A | Yes | No | Same public API shape as the root export. | -| | PageHeader | N/A | Yes | N/A | Same public API shape as the root export. | -| | ScrollableRegion | Yes | N/A | N/A | Props-driven accessibility wrapper. | -| | SelectPanel | N/A | Yes | No | Experimental parts-first panel with button, header, search, footer, loading, message, and secondary action parts. | -| | SkeletonAvatar | Yes | N/A | N/A | Leaf skeleton component. | -| | SkeletonText | Yes | N/A | N/A | Leaf skeleton component. | -| | Stack | Yes | Yes | N/A | Same public API shape as the root export. | -| | Table | N/A | Yes | N/A | Presentational table parts for `DataTable`. | -| | Tabs | N/A | Yes | Yes | Experimental primitive exposes `TabList`, `Tab`, `TabPanel`, and related hooks. | -| | Tooltip | Yes | N/A | No | Same public API shape as the root export. | -| | TopicTag | Yes | Yes | N/A | Props-driven tag with `Group` part. | -| | UnderlinePanels | N/A | Yes | No | Parts-first tab panel API with `Tab` and `Panel`; no public hook. | - -### Low-level components and hooks - -We want to offer more low-level components and hooks, in particular with a focus -on accessibility. Providing these low-level abstractions allows for quickly creating -robust experiences instead of having to re-invent them from scratch. - -In particular, we would like to offer the following primitives: - -| Type | Name | Supported | Description | Notes | -| :--------- | :---------------- | :-------- | :----------------------------------------- | :-------------------------------------------------------------------------------------------------- | -| Components | Accordion | No | Disclosure group primitive. | Not exposed today. | -| | Combobox | No | Text input plus listbox popup primitive. | Autocomplete and SelectPanel cover product use cases, but no generic combobox primitive is exposed. | -| | Disclosure | No | Show/hide primitive. | `Details` and `useDetails` exist, but there is no named `Disclosure` primitive. | -| | Dialog | Yes | Modal dialog primitive. | `Dialog` exposes props and custom-renderer parts. | -| | Listbox | No | Option list primitive. | ActionList and SelectPanel cover product use cases, but no generic listbox primitive is exposed. | -| | Menu | No | Menu button and popup primitive. | ActionMenu covers product use cases, but no generic menu primitive is exposed. | -| | Menubar | No | Horizontal menu primitive. | Not exposed today. | -| | Popover | Yes | Anchored floating content primitive. | Covered by `Popover`, `Overlay`, and `AnchoredOverlay`. | -| | Tabs | Yes | Tab state and tab/panel primitive. | Available from `./experimental` with parts and hooks. | -| | Tree | Yes | Tree navigation primitive. | Covered by `TreeView`; no separate unstyled `Tree` primitive is exposed. | -| Hooks | useFocusTrap | Yes | Trap focus within a container. | Exported from the root entrypoint. | -| | useMergedRefs | Yes | Merge multiple refs into one ref callback. | Exported from the root entrypoint. | -| | useRovingTabIndex | Yes | Manage roving tab index behavior. | Exported from the root entrypoint via `TreeView`. | -| | useTabs | Yes | Manage tab state and ARIA wiring. | Available from `./experimental` alongside `useTab`, `useTabList`, and `useTabPanel`. | - -### Model for upstreaming work - -### Authoring components - -#### Config components - -These components are "all-in-one"; they provide a high level of abstraction that -support common use-cases making it simple to quickly build out established -patterns and experiences. However, they are often inflexible and difficult to -extend. - -````tsx - { - /* ... */ - }} - onFilter={query => { - /* ... */ - }} -/> - -#### Presentational components - -These components provide a lower level of abstraction and are more flexible and extensible, but require more work to build out common patterns and experiences. - -```tsx -function Example({items}) { - const [state, actions] = useList({ - defaultSelected: [], - }) - - return ( - - { - actions.updateQuery(query) - }} - /> - {items - .filter(item => item.label.includes(state.query)) - .map(item => { - return ( - { - actions.toggleSelect(item.label) - }} - > - - - - {item.label} - - ) - })} - - ) -} -```` - -Presentational components are accompanied by behavior or state hooks for a -component's core functionality. Often times, config components are built by -combining presentational components and the corresponding behavior and state -hooks needed for a feature. +### Embracing the spectrum of abstraction model ## Consequences -TODO - ## Alternatives -TODO - ## Questions -TODO +## Audit + +| Entrypoint | Type | Name | Parts | Hooks | Config | Notes | +| :------------- | :------- | :----------------- | :---- | :---- | :----- | :---- | +| . | Compound | ActionBar | βœ… | | | | +| | | ActionList | βœ… | | | | +| | | ActionMenu | βœ… | | | | +| | | Banner | βœ… | | | | +| | | Breadcrumbs | βœ… | | | | +| | | ConfirmationDialog | | | | | +| | | Details | βœ… | | | | +| | | Dialog | | | | | +| | | FormControl | βœ… | | | | +| | | Header | βœ… | | | | +| | | NavList | βœ… | | | | +| | | PageHeader | βœ… | | | | +| | | PageLayout | βœ… | | | | +| | | Pagination | | | | | +| | | Popover | βœ… | | | | +| | | ProgressBar | βœ… | | | | +| | | SegmentedControl | βœ… | | | | +| | | Select | βœ… | | | | +| | | SelectPanel | | | | | +| | | SplitPageLayout | βœ… | | | | +| | | Stack | βœ… | | | | +| | | SubNav | βœ… | | | | +| | | Timeline | βœ… | | | | +| | | TreeView | βœ… | | | | +| | | UnderlineNav | βœ… | | | | +| . | Leaf | Autocomplete | | | | | +| | | Avatar | | | | | +| | | AvatarStack | | | | | +| | | BranchName | | | | | +| | | Button | | | | | +| | | ButtonGroup | | | | | +| | | Checkbox | | | | | +| | | CheckboxGroup | | | | | +| | | CircleBadge | | | | | +| | | CounterLabel | | | | | +| | | Heading | | | | | +| | | IconButton | | | | | +| | | IssueLabelToken | | | | | +| | | Label | | | | | +| | | LabelGroup | | | | | +| | | Link | | | | | +| | | LinkButton | | | | | +| | | Overlay | | | | | +| | | Radio | | | | | +| | | RadioGroup | | | | | +| | | RelativeTime | | | | | +| | | SkeletonBox | | | | | +| | | Spinner | | | | | +| | | StateLabel | | | | | +| | | Text | | | | | +| | | TextInput | | | | | +| | | Textarea | | | | | +| | | ToggleSwitch | | | | | +| | | Token | | | | | +| | | Tooltip | | | | | +| | | Truncate | | | | | +| | Utility | | | | | | +| | | AnchoredOverlay | | | | | +| | | VisuallyHidden | | | | | +| ./experimental | Compound | | | | | | +| | | Blankslate | | | | | +| | | Card | | | | | +| | | DataTable | | | | | +| | | Dialog | | | | | +| | | FilteredActionList | | | | | +| | | KeybindingHint | | | | | +| | | SelectPanel | | | | | +| | | Stack | | | | | +| | | Table | | | | | +| | | Tabs | | | | | +| | | UnderlinePanels | | | | | +| | Leaf | | | | | | +| | | InlineMessage | | | | | +| | | IssueLabel | | | | | +| | | SkeletonAvatar | | | | | +| | | SkeletonText | | | | | +| | | Tooltip | | | | | +| | | TopicTag | | | | | +| | Utility | | | | | | +| | | Hidden | | | | | From 4ffb4c41daf871db71aad0295da062bae347ab3d Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 19 Jun 2026 10:50:38 -0500 Subject: [PATCH 19/38] docs: update audit table --- contributor-docs/adrs/adr-024-modular-ds.md | 39 +++++++++------------ 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index cbb9fcdb008..cbc691af026 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -78,20 +78,20 @@ To do this, we will: | | | ActionMenu | βœ… | | | | | | | Banner | βœ… | | | | | | | Breadcrumbs | βœ… | | | | -| | | ConfirmationDialog | | | | | +| | | ConfirmationDialog | | | βœ… | | | | | Details | βœ… | | | | -| | | Dialog | | | | | +| | | Dialog | | | βœ… | | | | | FormControl | βœ… | | | | | | | Header | βœ… | | | | | | | NavList | βœ… | | | | | | | PageHeader | βœ… | | | | | | | PageLayout | βœ… | | | | -| | | Pagination | | | | | +| | | Pagination | | | βœ… | | | | | Popover | βœ… | | | | | | | ProgressBar | βœ… | | | | | | | SegmentedControl | βœ… | | | | | | | Select | βœ… | | | | -| | | SelectPanel | | | | | +| | | SelectPanel | | | βœ… | | | | | SplitPageLayout | βœ… | | | | | | | Stack | βœ… | | | | | | | SubNav | βœ… | | | | @@ -129,27 +129,22 @@ To do this, we will: | | | Token | | | | | | | | Tooltip | | | | | | | | Truncate | | | | | -| | Utility | | | | | | -| | | AnchoredOverlay | | | | | +| | Utility | AnchoredOverlay | | | | | | | | VisuallyHidden | | | | | -| ./experimental | Compound | | | | | | -| | | Blankslate | | | | | -| | | Card | | | | | -| | | DataTable | | | | | -| | | Dialog | | | | | -| | | FilteredActionList | | | | | -| | | KeybindingHint | | | | | -| | | SelectPanel | | | | | -| | | Stack | | | | | -| | | Table | | | | | -| | | Tabs | | | | | -| | | UnderlinePanels | | | | | -| | Leaf | | | | | | -| | | InlineMessage | | | | | +| ./experimental | Compound | Blankslate | βœ… | | | | +| | | Card | βœ… | | | | +| | | DataTable | | | βœ… | | +| | | FilteredActionList | | | βœ… | | +| | | KeybindingHint | | | βœ… | | +| | | SelectPanel | βœ… | | | | +| | | Stack | βœ… | | | | +| | | Table | βœ… | | | | +| | | Tabs | βœ… | βœ… | | | +| | | UnderlinePanels | βœ… | | | | +| | Leaf | InlineMessage | | | | | | | | IssueLabel | | | | | | | | SkeletonAvatar | | | | | | | | SkeletonText | | | | | | | | Tooltip | | | | | | | | TopicTag | | | | | -| | Utility | | | | | | -| | | Hidden | | | | | +| | Utility | Hidden | | | | | From 66681b6bb80847f0fa48e235164fa6198c2ceccf Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 19 Jun 2026 11:07:25 -0500 Subject: [PATCH 20/38] docs: add sections for low-level and spectrum docs --- contributor-docs/adrs/adr-024-modular-ds.md | 175 ++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index cbc691af026..be4a1ac6a95 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -61,8 +61,183 @@ To do this, we will: ### Delivering low-level components, hooks, and utilities +We will immediately deliver high priority components that assist in building accessible experiences, including: + +- **Combobox** for filtering and selection-based experiences +- **Dialog** for modal and nonmodal experiences with built in parts for + wiring up accessibility, behaviors, and semantics +- **Listbox** for selection-based experiences +- **Menu** for action-based experiences +- **Popover** for positioned, transient experiences +- **ScrollableRegion** for scrollable containers with built in accessibility and behaviors +- **Tabs** for building tabs-based experiences +- **ToggleButton**, **ToggleSwitch** for building toggle-based experiences + +We will also deliver documentation for all existing low-level components, +hooks, and utilities. + +In the future, we are investigating the following opportunities for offering +additional low-level components, hooks, and utilities: + +- useFilter +- useSelection + +#### Audit + +| Entrypoint | Type | Name | +| :------------- | :-------- | :--------------------------- | +| . | component | VisuallyHidden | +| | | Announce | +| | | AriaStatus | +| | | AriaAlert | +| | hook | useAnchoredPosition | +| | | useColorSchemeVar | +| | | useConfirm | +| | | useDetails | +| | | useFocusTrap | +| | | useFocusZone | +| | | useFormControlForwardedProps | +| | | useId | +| | | useIsomorphicLayoutEffect | +| | | useMergedRefs | +| | | useOnEscapePress | +| | | useOnOutsideClick | +| | | useOpenAndCloseFocus | +| | | useOverlay | +| | | useProvidedRefOrCreate | +| | | useRefObjectAsForwardedRef | +| | | useResizeObserver | +| | | useResponsiveValue | +| | | useRovingTabIndex | +| | | useSafeTimeout | +| | | useSlots | +| | | useSyncedState | +| | | useTheme | +| ./experimental | component | Hidden | +| | | Tabs | +| | hook | useSlots | +| | | useOverflow | +| | | useTab | +| | | useTabList | +| | | useTabPanel | + ### Embracing the spectrum of abstraction model +We will author components with a spectrum of abstraction in mind, specifically +beginning with presentational components that can combine with hooks to provide +opionated, config-driven components. + +This model allows us to offer opinionated defaults that work for common +scenarios while still allow teams to extend and customize components as needed. + +Default to building presentational components, then add behavior through hooks. +As opinions or defaults are established, create config-driven components that +combine presentational components and hooks to offer a higher level of +abstraction for common use-cases. + +#### Config components + +These components are "all-in-one"; they provide a high level of abstraction that +support common use-cases making it simple to quickly build out established +patterns and experiences. However, they are often inflexible and difficult to +extend. + +```tsx + { + /* ... */ + }} + onFilter={query => { + /* ... */ + }} +/> +``` + +#### Presentational components + +These components provide a lower level of abstraction and are more flexible and extensible, but require more work to build out common patterns and experiences. + +```tsx +function Example({items}) { + const [state, actions] = useList({ + defaultSelected: [], + }) + + return ( + + { + actions.updateQuery(query) + }} + /> + {items + .filter(item => item.label.includes(state.query)) + .map(item => { + return ( + { + actions.toggleSelect(item.label) + }} + > + + + + {item.label} + + ) + })} + + ) +} +``` + +Presentational components are accompanied by behavior or state hooks for a +component's core functionality. Often times, config components are built by +combining presentational components and the corresponding behavior and state +hooks needed for a feature. + +#### Base components + +Base components are unstyled and may optionally provide behavior. These +components are fundamental primitives that are used to build components, such as +accessibility primitives. + +```tsx +function Example() { + return ( + + + + + + + + ) +} +``` + +Other examples include: + +- Combobox (filtering, selection) +- Listbox (selection) +- Popover +- Tabs +- Treeview + +#### Utilities + +There are core utilities, hooks, functions, etc that are commonly used when +building out components or React applications. We provide hooks for established +patterns so that teams can build on solid foundations when authoring new +experiences. These can include hooks such as `useMergedRefs`, +`useOnEscapePress`, `useTimeout` and more. + +These utilities may extend beyond hooks, such as `@primer/behaviors` or custom +elements, where appropriate. + ## Consequences ## Alternatives From f714b129b9507ffdd6df4c34d162f5f9edc72eef Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 19 Jun 2026 11:44:44 -0500 Subject: [PATCH 21/38] Add Disclosure component for content visibility --- contributor-docs/adrs/adr-024-modular-ds.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index be4a1ac6a95..e83de99e7f3 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -66,6 +66,7 @@ We will immediately deliver high priority components that assist in building acc - **Combobox** for filtering and selection-based experiences - **Dialog** for modal and nonmodal experiences with built in parts for wiring up accessibility, behaviors, and semantics +- **Disclosure** for showing/hiding content - **Listbox** for selection-based experiences - **Menu** for action-based experiences - **Popover** for positioned, transient experiences From 6be807401f0a6526010f2393db4052a8de7e97b3 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 22 Jun 2026 13:27:00 -0500 Subject: [PATCH 22/38] Update list of breaking components in ADR-024 Added components to the list of breaking components. --- contributor-docs/adrs/adr-024-modular-ds.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index e83de99e7f3..64a04541027 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -59,6 +59,11 @@ To do this, we will: ### Breaking components down into parts +- SelectPanel +- Dialog +- ConfirmationDialog +- Pagination + ### Delivering low-level components, hooks, and utilities We will immediately deliver high priority components that assist in building accessible experiences, including: From 776e79caabd6d00348f79f0e66de9d22ce2016ec Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 23 Jun 2026 15:51:20 -0500 Subject: [PATCH 23/38] docs: update breaking into parts section and low-level section --- contributor-docs/adrs/adr-024-modular-ds.md | 23 +++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 64a04541027..5d74c6cf3f2 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -59,25 +59,25 @@ To do this, we will: ### Breaking components down into parts +We will break down the following components into parts plus appropriate +behavioral hooks. These changes will make it easier for teams to layer behavior +on top of these components. + - SelectPanel -- Dialog -- ConfirmationDialog -- Pagination +- Dialog (and ConfirmationDialog) +- DataTable (write documentation for `useTable` and `Table.*` components) ### Delivering low-level components, hooks, and utilities We will immediately deliver high priority components that assist in building accessible experiences, including: -- **Combobox** for filtering and selection-based experiences - **Dialog** for modal and nonmodal experiences with built in parts for - wiring up accessibility, behaviors, and semantics -- **Disclosure** for showing/hiding content -- **Listbox** for selection-based experiences -- **Menu** for action-based experiences -- **Popover** for positioned, transient experiences - **ScrollableRegion** for scrollable containers with built in accessibility and behaviors +- **Popover** for positioned, transient experiences - **Tabs** for building tabs-based experiences - **ToggleButton**, **ToggleSwitch** for building toggle-based experiences +- **Disclosure** for showing/hiding content +- **Accordion** for showing/hiding content We will also deliver documentation for all existing low-level components, hooks, and utilities. @@ -85,6 +85,11 @@ hooks, and utilities. In the future, we are investigating the following opportunities for offering additional low-level components, hooks, and utilities: +- **Listbox** for selection-based experiences +- **Combobox** for filtering and selection-based experiences + wiring up accessibility, behaviors, and semantics +- **Menu** for action-based experiences +- \*_Tree_ for tree-based experiences - useFilter - useSelection From 412bd6ced53aa68dd3a2faabddb5a58496135e6d Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 23 Jun 2026 15:54:11 -0500 Subject: [PATCH 24/38] docs: update syntax --- contributor-docs/adrs/adr-024-modular-ds.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 5d74c6cf3f2..0c09746ce0a 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -89,7 +89,7 @@ additional low-level components, hooks, and utilities: - **Combobox** for filtering and selection-based experiences wiring up accessibility, behaviors, and semantics - **Menu** for action-based experiences -- \*_Tree_ for tree-based experiences +- **Tree** for tree-based experiences - useFilter - useSelection From f6e7b7767d70edbdedb8987eed5c9cc3de5c0461 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Jun 2026 16:38:44 -0500 Subject: [PATCH 25/38] chore: update adr --- contributor-docs/adrs/adr-024-modular-ds.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 0c09746ce0a..25f84b6f3c2 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -66,6 +66,7 @@ on top of these components. - SelectPanel - Dialog (and ConfirmationDialog) - DataTable (write documentation for `useTable` and `Table.*` components) +- ActionList (break down into list primitives) ### Delivering low-level components, hooks, and utilities From 01660647612c98d14c6490a97e6cb810913de586 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Jun 2026 15:58:39 -0500 Subject: [PATCH 26/38] Apply suggestions from code review Co-authored-by: Brittany L. Houtz <55068883+llastflowers@users.noreply.github.com> --- contributor-docs/adrs/adr-024-modular-ds.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 25f84b6f3c2..99912b7c172 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -46,15 +46,15 @@ want to focus on in order to improve Primer: - Embrace layer 2 from the layer model, offering parts for our components that can be combined with hooks for improved flexibility - Provide low-level components and hooks to make it easier to build - quality, accessible experience -- Create a model for upstreaming work where people build on top of Primer + quality, accessible experiences +- Create a model for upstreaming work where people have already built on top of Primer To do this, we will: -- Identify and deliver components to break down into parts that can be used to create more +- Identify and deliver components that can be broken down into parts, which can be used to create more flexible and extensible components - Identify and deliver low-level components, hooks, and utilities that can be used to quickly build out accessible experiences -- Embrace the spectrum of abstraction model, authoring presentation components +- Embrace the spectrum of abstraction model, authoring presentational components that combine with hooks to provide flexible patterns and opinionated defaults. ### Breaking components down into parts @@ -70,10 +70,10 @@ on top of these components. ### Delivering low-level components, hooks, and utilities -We will immediately deliver high priority components that assist in building accessible experiences, including: +We will immediately deliver high-priority components that will assist in building accessible experiences, including: -- **Dialog** for modal and nonmodal experiences with built in parts for -- **ScrollableRegion** for scrollable containers with built in accessibility and behaviors +- **Dialog** for modal and nonmodal experiences with built-in parts for +- **ScrollableRegion** for scrollable containers with built-in accessibility and behaviors - **Popover** for positioned, transient experiences - **Tabs** for building tabs-based experiences - **ToggleButton**, **ToggleSwitch** for building toggle-based experiences @@ -140,7 +140,7 @@ beginning with presentational components that can combine with hooks to provide opionated, config-driven components. This model allows us to offer opinionated defaults that work for common -scenarios while still allow teams to extend and customize components as needed. +scenarios while still allowing teams to extend and customize components as needed. Default to building presentational components, then add behavior through hooks. As opinions or defaults are established, create config-driven components that @@ -207,8 +207,8 @@ function Example({items}) { ``` Presentational components are accompanied by behavior or state hooks for a -component's core functionality. Often times, config components are built by -combining presentational components and the corresponding behavior and state +component's core functionality. Oftentimes, config components are built by +combining presentational components with the corresponding behavior and state hooks needed for a feature. #### Base components From 7e830162c1ea6d7f3befd793a327fdb08958b239 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Jun 2026 16:02:10 -0500 Subject: [PATCH 27/38] Apply suggestion from @llastflowers Co-authored-by: Brittany L. Houtz <55068883+llastflowers@users.noreply.github.com> --- contributor-docs/adrs/adr-024-modular-ds.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 99912b7c172..96e70e47432 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -24,7 +24,7 @@ details several layers that Primer could be structured into: We took this proposal into a Modular design System Workshop: https://github.com/github/primer/discussions/6703. In this workshop, we -identified several scenarios that team run into challenges when building with +identified several scenarios where teams run into challenges when building with Primer: - It can be difficult to extend or add new functionality to a component From b982185964abc3d0aed39b33ef11c274948bc7fd Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 24 Jun 2026 16:39:21 -0500 Subject: [PATCH 28/38] docs: update adr description for dialog --- contributor-docs/adrs/adr-024-modular-ds.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 96e70e47432..86b689f6b53 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -72,7 +72,8 @@ on top of these components. We will immediately deliver high-priority components that will assist in building accessible experiences, including: -- **Dialog** for modal and nonmodal experiences with built-in parts for +- **Dialog** for modal and nonmodal experiences with built-in parts for dialog + headings, content, and actions - **ScrollableRegion** for scrollable containers with built-in accessibility and behaviors - **Popover** for positioned, transient experiences - **Tabs** for building tabs-based experiences From 4e511a86009a16ce0f788ae4c09d0f1895e9b79c Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 2 Jul 2026 15:20:51 -0500 Subject: [PATCH 29/38] docs: remove planning topics from adr --- contributor-docs/adrs/adr-024-modular-ds.md | 157 -------------------- 1 file changed, 157 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 86b689f6b53..2341b2f7bdd 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -57,83 +57,6 @@ To do this, we will: - Embrace the spectrum of abstraction model, authoring presentational components that combine with hooks to provide flexible patterns and opinionated defaults. -### Breaking components down into parts - -We will break down the following components into parts plus appropriate -behavioral hooks. These changes will make it easier for teams to layer behavior -on top of these components. - -- SelectPanel -- Dialog (and ConfirmationDialog) -- DataTable (write documentation for `useTable` and `Table.*` components) -- ActionList (break down into list primitives) - -### Delivering low-level components, hooks, and utilities - -We will immediately deliver high-priority components that will assist in building accessible experiences, including: - -- **Dialog** for modal and nonmodal experiences with built-in parts for dialog - headings, content, and actions -- **ScrollableRegion** for scrollable containers with built-in accessibility and behaviors -- **Popover** for positioned, transient experiences -- **Tabs** for building tabs-based experiences -- **ToggleButton**, **ToggleSwitch** for building toggle-based experiences -- **Disclosure** for showing/hiding content -- **Accordion** for showing/hiding content - -We will also deliver documentation for all existing low-level components, -hooks, and utilities. - -In the future, we are investigating the following opportunities for offering -additional low-level components, hooks, and utilities: - -- **Listbox** for selection-based experiences -- **Combobox** for filtering and selection-based experiences - wiring up accessibility, behaviors, and semantics -- **Menu** for action-based experiences -- **Tree** for tree-based experiences -- useFilter -- useSelection - -#### Audit - -| Entrypoint | Type | Name | -| :------------- | :-------- | :--------------------------- | -| . | component | VisuallyHidden | -| | | Announce | -| | | AriaStatus | -| | | AriaAlert | -| | hook | useAnchoredPosition | -| | | useColorSchemeVar | -| | | useConfirm | -| | | useDetails | -| | | useFocusTrap | -| | | useFocusZone | -| | | useFormControlForwardedProps | -| | | useId | -| | | useIsomorphicLayoutEffect | -| | | useMergedRefs | -| | | useOnEscapePress | -| | | useOnOutsideClick | -| | | useOpenAndCloseFocus | -| | | useOverlay | -| | | useProvidedRefOrCreate | -| | | useRefObjectAsForwardedRef | -| | | useResizeObserver | -| | | useResponsiveValue | -| | | useRovingTabIndex | -| | | useSafeTimeout | -| | | useSlots | -| | | useSyncedState | -| | | useTheme | -| ./experimental | component | Hidden | -| | | Tabs | -| | hook | useSlots | -| | | useOverflow | -| | | useTab | -| | | useTabList | -| | | useTabPanel | - ### Embracing the spectrum of abstraction model We will author components with a spectrum of abstraction in mind, specifically @@ -256,83 +179,3 @@ elements, where appropriate. ## Alternatives ## Questions - -## Audit - -| Entrypoint | Type | Name | Parts | Hooks | Config | Notes | -| :------------- | :------- | :----------------- | :---- | :---- | :----- | :---- | -| . | Compound | ActionBar | βœ… | | | | -| | | ActionList | βœ… | | | | -| | | ActionMenu | βœ… | | | | -| | | Banner | βœ… | | | | -| | | Breadcrumbs | βœ… | | | | -| | | ConfirmationDialog | | | βœ… | | -| | | Details | βœ… | | | | -| | | Dialog | | | βœ… | | -| | | FormControl | βœ… | | | | -| | | Header | βœ… | | | | -| | | NavList | βœ… | | | | -| | | PageHeader | βœ… | | | | -| | | PageLayout | βœ… | | | | -| | | Pagination | | | βœ… | | -| | | Popover | βœ… | | | | -| | | ProgressBar | βœ… | | | | -| | | SegmentedControl | βœ… | | | | -| | | Select | βœ… | | | | -| | | SelectPanel | | | βœ… | | -| | | SplitPageLayout | βœ… | | | | -| | | Stack | βœ… | | | | -| | | SubNav | βœ… | | | | -| | | Timeline | βœ… | | | | -| | | TreeView | βœ… | | | | -| | | UnderlineNav | βœ… | | | | -| . | Leaf | Autocomplete | | | | | -| | | Avatar | | | | | -| | | AvatarStack | | | | | -| | | BranchName | | | | | -| | | Button | | | | | -| | | ButtonGroup | | | | | -| | | Checkbox | | | | | -| | | CheckboxGroup | | | | | -| | | CircleBadge | | | | | -| | | CounterLabel | | | | | -| | | Heading | | | | | -| | | IconButton | | | | | -| | | IssueLabelToken | | | | | -| | | Label | | | | | -| | | LabelGroup | | | | | -| | | Link | | | | | -| | | LinkButton | | | | | -| | | Overlay | | | | | -| | | Radio | | | | | -| | | RadioGroup | | | | | -| | | RelativeTime | | | | | -| | | SkeletonBox | | | | | -| | | Spinner | | | | | -| | | StateLabel | | | | | -| | | Text | | | | | -| | | TextInput | | | | | -| | | Textarea | | | | | -| | | ToggleSwitch | | | | | -| | | Token | | | | | -| | | Tooltip | | | | | -| | | Truncate | | | | | -| | Utility | AnchoredOverlay | | | | | -| | | VisuallyHidden | | | | | -| ./experimental | Compound | Blankslate | βœ… | | | | -| | | Card | βœ… | | | | -| | | DataTable | | | βœ… | | -| | | FilteredActionList | | | βœ… | | -| | | KeybindingHint | | | βœ… | | -| | | SelectPanel | βœ… | | | | -| | | Stack | βœ… | | | | -| | | Table | βœ… | | | | -| | | Tabs | βœ… | βœ… | | | -| | | UnderlinePanels | βœ… | | | | -| | Leaf | InlineMessage | | | | | -| | | IssueLabel | | | | | -| | | SkeletonAvatar | | | | | -| | | SkeletonText | | | | | -| | | Tooltip | | | | | -| | | TopicTag | | | | | -| | Utility | Hidden | | | | | From 93b8bc77111128b461ec38e3786e15e74a8589f2 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 2 Jul 2026 15:56:05 -0500 Subject: [PATCH 30/38] feat: add initial agents and skills for style guide and modular ds --- .../agents/modular-ds-implementer.agent.md | 41 +++++++++++ .github/agents/modular-ds-reviewer.agent.md | 39 +++++++++++ .github/skills/style-guide/SKILL.md | 18 +++++ contributor-docs/adrs/adr-024-modular-ds.md | 69 ++++++++++++++++++- contributor-docs/style.md | 67 ++++++++++++++++++ 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 .github/agents/modular-ds-implementer.agent.md create mode 100644 .github/agents/modular-ds-reviewer.agent.md create mode 100644 .github/skills/style-guide/SKILL.md diff --git a/.github/agents/modular-ds-implementer.agent.md b/.github/agents/modular-ds-implementer.agent.md new file mode 100644 index 00000000000..f6952a36e69 --- /dev/null +++ b/.github/agents/modular-ds-implementer.agent.md @@ -0,0 +1,41 @@ +--- +name: modular-ds-implementer +description: Builds Primer React components using the modular design system patterns in the Primer style guide. +tools: + - read + - search + - edit + - execute +skills: + - style-guide +--- + +You are a Primer React implementer specializing in the modular design system +model. Before designing or changing a component, read +`contributor-docs/style.md` and align the implementation to its React component +guidance, especially the spectrum of abstraction model. + +Follow these rules: + +- Start with presentational components and behavior hooks for new flexible + component APIs. +- Add config components over time when common use-cases and opinionated defaults + are established, and ensure they compose presentational components and hooks + instead of duplicating behavior. +- Use base components, shared hooks, utilities, and behaviors for accessibility + primitives and low-level behavior before creating custom one-off + implementations. +- Consolidate accessibility primitives for established patterns, such as ARIA + Authoring Practices Guide patterns, instead of reimplementing them across + components. +- Keep markup and accessibility semantics flexible. Preserve native semantics + and expose presentational pieces or slots when consumers need control over + content, appearance, or semantics. +- Search for existing Primer components, hooks, utilities, and accessibility + primitives before adding new ones. +- Include the surfaces needed for adoption: source exports, tests, stories, docs + metadata, and changesets when the published package behavior changes. + +When proposing or implementing work, explain which API type changed, why that +level of abstraction is appropriate, and how the implementation can be extended +without forking or overriding Primer internals. diff --git a/.github/agents/modular-ds-reviewer.agent.md b/.github/agents/modular-ds-reviewer.agent.md new file mode 100644 index 00000000000..24ee35e23c1 --- /dev/null +++ b/.github/agents/modular-ds-reviewer.agent.md @@ -0,0 +1,39 @@ +--- +name: modular-ds-reviewer +description: Reviews Primer React component changes for alignment with the modular design system patterns in the Primer style guide. +tools: + - read + - search + - execute +skills: + - style-guide +--- + +You are a read-only reviewer for the modular design system model. Review the +current changes and determine whether they follow the patterns in +`contributor-docs/style.md`, especially the React component guidance and +spectrum of abstraction model. Never modify files. + +Check for: + +- Whether the change starts with presentational components and behavior hooks + before introducing config-driven APIs. +- Whether config components compose presentational components and hooks instead + of duplicating state, behavior, or markup. +- Whether base components or existing utilities should be reused for + accessibility primitives, state, refs, timers, escape handling, or similar + cross-component behavior. +- Whether accessibility primitives for established patterns, such as ARIA + Authoring Practices Guide patterns, are consolidated instead of reimplemented + across components. +- Whether consumers can customize appearance, content, semantics, and behavior + without forking Primer or relying on overrides. +- Whether native accessibility semantics are preserved and low-level + accessibility behavior is reusable. +- Whether exports, stories, tests, docs metadata, and changesets match the public + API impact of the change. + +Report only actionable findings that show a concrete mismatch with the style +guide. For each finding, cite the file and line, name the relevant API type or +principle, explain the impact, and suggest the smallest design-level correction. +If the change follows the style guide, say so directly. diff --git a/.github/skills/style-guide/SKILL.md b/.github/skills/style-guide/SKILL.md new file mode 100644 index 00000000000..7d9fb9348f6 --- /dev/null +++ b/.github/skills/style-guide/SKILL.md @@ -0,0 +1,18 @@ +--- +name: style-guide +description: 'Use when: authoring, modifying, or reviewing Primer React components, hooks, utilities, or component APIs. Covers applying contributor-docs/style.md, including the spectrum of abstraction, presentational components, behavior hooks, config components, accessibility primitives, props, hooks, SSR, and CSS conventions.' +--- + +# Primer React Style Guide + +Use this skill when creating, modifying, or reviewing Primer React components, +hooks, utilities, or component APIs. + +Before authoring component code, read `contributor-docs/style.md` and apply the +guidelines that are relevant to the change. The style guide is the source of +truth for component API design and implementation conventions in this +repository. + +When proposing, implementing, or reviewing a component change, explicitly apply +the relevant style-guide principles to the design. If a change intentionally +deviates from the style guide, call out the reason. diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 2341b2f7bdd..9c6a8b4a85a 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -61,7 +61,7 @@ To do this, we will: We will author components with a spectrum of abstraction in mind, specifically beginning with presentational components that can combine with hooks to provide -opionated, config-driven components. +opinionated, config-driven components. This model allows us to offer opinionated defaults that work for common scenarios while still allowing teams to extend and customize components as needed. @@ -90,6 +90,15 @@ extend. /> ``` +- High-level abstraction: consumers describe intent through props/data rather than composing markup directly. +- Opinionated defaults: Primer owns the default structure, behavior, accessibility, styling, and interaction patterns. +- Fast path for common use cases: optimized for teams that want to implement an established pattern quickly and correctly. +- Props-driven customization: extension happens through supported props, slots/render props, or configurationβ€”not arbitrary internal composition. +- Limited flexibility by design: they should not try to support every variation; unusual needs may require dropping down to presentational parts. +- Stable product pattern: best suited for patterns Primer understands well and expects many teams to reuse. +- Integrated behavior: state management, keyboard behavior, selection, filtering, validation, etc. are usually bundled in. +- Clear escape boundary: when consumers need to change structure, semantics, or behavior beyond the config API, they should move to presentational components. + #### Presentational components These components provide a lower level of abstraction and are more flexible and extensible, but require more work to build out common patterns and experiences. @@ -135,6 +144,17 @@ component's core functionality. Oftentimes, config components are built by combining presentational components with the corresponding behavior and state hooks needed for a feature. +These components are defined by: + +- Mid-level abstraction: consumers compose Primer-provided parts directly while Primer still owns styling and semantics for each part. +- Structure-first API: consumers control layout, ordering, conditional rendering, and content by assembling parts. +- Behavior via hooks: state and interactions are usually provided separately through hooks, letting teams choose how much behavior to adopt. +- Flexible composition: supports variants that config components cannot reasonably expose through props. +- Primer-owned building blocks: parts still encode design-system qualityβ€”styling, accessibility expectations, data attributes, and component contracts. +- Best for emerging patterns: useful when a pattern is known, but the right high-level API has not stabilized yet. +- Foundation for config components: config components should often be implemented by composing presentational parts plus behavior hooks. +- More consumer responsibility: consumers gain flexibility but must wire state, events, filtering, selection, and edge cases themselves + #### Base components Base components are unstyled and may optionally provide behavior. These @@ -176,6 +196,53 @@ elements, where appropriate. ## Consequences +This approach is a consolidation of our existing approach to building +components. It highlights changes that need to happen in existing components, +namely SelectPanel and Dialog, that would require changes in order for them to +accomodate the spectrum of abstraction model. + +This decision also impacts future component development as components must start +with this in mind. One critical part to this is that authoring components as +parts makes migration over time more difficult. For example, consider an +existing Card component implementation: + +```tsx + + Title + Body + Footer + +``` + +Over time, there may be a request to add support for a `Card.Action`. This may +require us to add a `Card.Header` part to the Card component. + +```tsx + + + Title + Edit + + Body + Footer + +``` + +As a result, we would need to manage how we support this kind of breaking +change. We could coordinate migrating existing implementations to +the new structure as a part of the change. We could also consider supporting +both structures which would increase the surface area of the component. + ## Alternatives +The main alternatives to this approach would be: + +- Do nothing, continue our current approach which is causing the challenges + mentioned at the beginning of the ADR +- Fully adopt the layer model as proposed + +Most likely we'll never find a model that will exactly work across components. +As a result, we should develop techniques and learn how to apply them as the +design system grows. + ## Questions diff --git a/contributor-docs/style.md b/contributor-docs/style.md index d15375bd5bf..77d2980084d 100644 --- a/contributor-docs/style.md +++ b/contributor-docs/style.md @@ -61,6 +61,11 @@ row before the line. - [Server-side rendering](#server-side-rendering) - [Prefer managing focus through event handlers instead of effects](#prefer-managing-focus-through-event-handlers-instead-of-effects) - [Prefer the `useControllableState` hook when authoring components that can be controlled or uncontrolled](#prefer-the-usecontrollablestate-hook-when-authoring-components-that-can-be-controlled-or-uncontrolled) + - [Prefer building components across a spectrum of abstraction](#prefer-building-components-across-a-spectrum-of-abstraction) + - [Config components](#config-components) + - [Presentational components](#presentational-components) + - [Base components](#base-components) + - [Utilities](#utilities) - [Props](#props) - [Prefer applying component rest parameters to the root element rendered by a component](#prefer-applying-component-rest-parameters-to-the-root-element-rendered-by-a-component) - [Prefer authoring callback prop types with arguments that can be extended](#prefer-authoring-callback-prop-types-with-arguments-that-can-be-extended) @@ -152,6 +157,68 @@ function Example({defaultValue, onChange, value: controlledValue}: ExampleProps) } ``` +### Prefer building components across a spectrum of abstraction + +When authoring Primer components, prefer starting with flexible presentational +components and companion behavior hooks. As opinions or defaults become +established, create config components that compose those presentational +components and hooks instead of duplicating their behavior. This keeps common +patterns easy to adopt while still giving consumers a clear path when they need +to customize appearance, content, semantics, or behavior. + +Use the following API types when designing component APIs: + +| API type | Use for | +| ------------------------- | ----------------------------------------------------------------------------------- | +| Config components | Ready-made, props-based components for stable product patterns and common use-cases | +| Presentational components | Styled pieces that consumers compose directly, often with companion behavior hooks | +| Base components | Unstyled primitives, often for accessibility structure or low-level behavior | +| Utilities | Hooks, state management, behaviors, and functions used to build components | + +#### Config components + +Config components are "all-in-one" APIs that let consumers describe intent +through props or data rather than composing markup directly. They should provide +opinionated defaults for structure, behavior, accessibility, styling, and +interactions so teams can implement established patterns quickly and correctly. + +Use config components when Primer understands the pattern well and expects many +teams to reuse it. Keep the supported customization surface explicit through +props, slots, render props, or configuration. Do not make config components +support every variation; if consumers need to change structure, semantics, or +behavior beyond the config API, they should be able to drop down to +presentational components and behavior hooks. + +#### Presentational components + +Presentational components are styled pieces that consumers compose directly. +They should let consumers control layout, ordering, conditional rendering, and +content while Primer still owns the styling, accessibility expectations, data +attributes, and component contracts for each piece. + +Prefer presentational components and behavior hooks for emerging patterns or +flexible APIs where a single high-level config API has not stabilized. Build +config components over time by composing those components and hooks after common +use-cases and defaults become clear. + +#### Base components + +Base components are unstyled primitives used to build higher-level components. +Use them for accessibility primitives and low-level behaviors that need full +markup and style control. Accessibility primitives for established patterns, +such as ARIA Authoring Practices Guide patterns, should be consolidated and +reused rather than reimplemented across components. Before adding custom +behavior to a component, look for an existing base component, hook, utility, or +behavior that can provide the foundation. + +#### Utilities + +Utilities include hooks, functions, behaviors, and other reusable logic. Provide +utilities for established patterns so teams can build accessible experiences on +solid foundations. Examples include hooks such as `useMergedRefs`, +`useOnEscapePress`, and `useTimeout`, or lower-level packages such as +`@primer/behaviors`. + ### Props #### Prefer applying component rest parameters to the root element rendered by a component From e30b0b002320c0d6648f5e7a629374f04c17e23b Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 2 Jul 2026 16:18:20 -0500 Subject: [PATCH 31/38] docs: add layering example --- contributor-docs/adrs/adr-024-modular-ds.md | 105 +++++++++++++++----- 1 file changed, 79 insertions(+), 26 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 9c6a8b4a85a..45ca7516e6f 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -84,9 +84,6 @@ extend. onSelect={item => { /* ... */ }} - onFilter={query => { - /* ... */ - }} /> ``` @@ -111,29 +108,21 @@ function Example({items}) { return ( - { - actions.updateQuery(query) - }} - /> - {items - .filter(item => item.label.includes(state.query)) - .map(item => { - return ( - { - actions.toggleSelect(item.label) - }} - > - - - - {item.label} - - ) - })} + {items.map(item => { + return ( + { + actions.toggleSelect(item.label) + }} + > + + + + {item.label} + + ) + })} ) } @@ -194,6 +183,70 @@ experiences. These can include hooks such as `useMergedRefs`, These utilities may extend beyond hooks, such as `@primer/behaviors` or custom elements, where appropriate. +#### Layered example + +When using this model, the config component should cover the default version of +a pattern, while presentational components let teams extend the pattern when +they need behavior that is not yet part of the high-level API. + +For example, we may have a `List` config component that supports selection as +the default interaction: + +```tsx + { + /* ... */ + }} +/> +``` + +If a team needs to add filtering, they can build on the presentational `List` +parts and a lower-level behavior hook instead of forking the config component or +asking the config API to support every possible variation. For example: + +```tsx +function FilterableList({items}) { + const { getInputProps, getListboxProps, getOptionProps } = useCombobox({ + items, + getItemLabel: item => item.label, + selectionMode: 'multiple', + }) + + return ( + + + + {combobox.items.map(item => { + return ( + + + + + {item.label} + + ) + })} + + + ) +} +``` + +The important part is that the `List` parts continue to provide the selected +item structure while the team layers filtering on top of it. If that filtering +control needs richer accessibility or interaction behavior, the team can keep +using the same `List` parts while moving more of the interaction model into +lower-level primitives or hooks. + +As this filtering behavior becomes common and well-understood, we could decide +to move it up the spectrum by adding it to the config component. Until then, the +presentational API gives teams a supported path to compose the experience +without relying on internal implementation details. + ## Consequences This approach is a consolidation of our existing approach to building From c94e242bed5929f7047dede9428d0c8dc222632b Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 2 Jul 2026 16:41:40 -0500 Subject: [PATCH 32/38] chore: update agents with feedback round --- .../agents/modular-ds-implementer.agent.md | 20 ++++++++++++++++--- .github/agents/modular-ds-reviewer.agent.md | 14 ++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.github/agents/modular-ds-implementer.agent.md b/.github/agents/modular-ds-implementer.agent.md index f6952a36e69..f8e8ec9f9cb 100644 --- a/.github/agents/modular-ds-implementer.agent.md +++ b/.github/agents/modular-ds-implementer.agent.md @@ -28,11 +28,25 @@ Follow these rules: - Consolidate accessibility primitives for established patterns, such as ARIA Authoring Practices Guide patterns, instead of reimplementing them across components. -- Keep markup and accessibility semantics flexible. Preserve native semantics - and expose presentational pieces or slots when consumers need control over - content, appearance, or semantics. +- Match the accessibility pattern to the component contract. For established + ARIA Authoring Practices Guide patterns such as accordions, prefer the APG + semantics and structure expected by the pattern. +- Keep markup and accessibility semantics flexible. Preserve native semantics, + including heading structure, and expose presentational pieces or slots when + consumers need control over content, appearance, or semantics. - Search for existing Primer components, hooks, utilities, and accessibility primitives before adding new ones. +- Do not expose public hooks for subcomponent internals unless there is a clear + consumer need. Internal hooks are fine when they keep behavior reusable without + expanding the public API. +- Do not expose `data-component` as a customizable prop. Primer owns + `data-component` values as component identifiers. +- Avoid inventing visual styling without a concrete design reference, image, or + specification. If styling is not specified, keep styles minimal and structural + so the component API and accessibility model can be evaluated independently. +- Prefer `HTMLElement` for default root refs and polymorphic component typing. + Use narrower element types only when the API or behavior requires a specific + element. - Include the surfaces needed for adoption: source exports, tests, stories, docs metadata, and changesets when the published package behavior changes. diff --git a/.github/agents/modular-ds-reviewer.agent.md b/.github/agents/modular-ds-reviewer.agent.md index 24ee35e23c1..614a5607d54 100644 --- a/.github/agents/modular-ds-reviewer.agent.md +++ b/.github/agents/modular-ds-reviewer.agent.md @@ -26,10 +26,22 @@ Check for: - Whether accessibility primitives for established patterns, such as ARIA Authoring Practices Guide patterns, are consolidated instead of reimplemented across components. +- Whether established ARIA Authoring Practices Guide patterns use the expected + pattern semantics and structure instead of defaulting to native elements that + produce a different component model. - Whether consumers can customize appearance, content, semantics, and behavior without forking Primer or relying on overrides. - Whether native accessibility semantics are preserved and low-level - accessibility behavior is reusable. + accessibility behavior is reusable, including heading-first structures when a + heading labels an interactive control. +- Whether public hooks are justified by a clear consumer need instead of + exposing subcomponent internals by default. +- Whether `data-component` values are owned by Primer and not exposed as + customizable public props. +- Whether visual styling is supported by a concrete design reference, image, or + specification instead of being invented by the implementation. +- Whether root refs and element types default to `HTMLElement` unless a narrower + element type is required. - Whether exports, stories, tests, docs metadata, and changesets match the public API impact of the change. From 02af4ad712bd748c5b3d9a965c086563530e2680 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 2 Jul 2026 16:46:14 -0500 Subject: [PATCH 33/38] Apply suggestions from code review Co-authored-by: Leslie Cohn-Wein --- contributor-docs/adrs/adr-024-modular-ds.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 45ca7516e6f..67453734e79 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -16,13 +16,13 @@ blocks in: https://github.com/github/core-ux/issues/2238. In this proposal, it details several layers that Primer could be structured into: - Layer 3: Ready-made, props-based components that are ready to use -- Layer 2: Parts, presentiontational-style components that one combines with hooks to +- Layer 2: Parts, presentational-style components that one combines with hooks to create components - Layer 1: Foundations, unstyled primitives to allow for full markup and style control - Layer 0: Hooks, state management, and other logic that can be used to build components -We took this proposal into a Modular design System Workshop: +We took this proposal into a Modular Design System Workshop: https://github.com/github/primer/discussions/6703. In this workshop, we identified several scenarios where teams run into challenges when building with Primer: @@ -33,7 +33,7 @@ Primer: - It can be difficult to build new experiences using established patterns (like accessibility primitives) These challenges lead to custom implementations, forks, or overrides of Primer -components that reuslt in adoption challenges and ecosystem fragmentation. +components that result in adoption challenges and ecosystem fragmentation. As a result, we applied the layer model to several situations in a [FigJam](https://www.figma.com/board/mgApQG7vL3imdUIhgyhG2n/Modular-DS-Workshop?node-id=84-152&t=dBKm8jx1UsKRXyzu-1). From this work, we pulled out insights about this model that we want to adopt in Primer today. @@ -45,7 +45,7 @@ want to focus on in order to improve Primer: - Embrace layer 2 from the layer model, offering parts for our components that can be combined with hooks for improved flexibility -- Provide low-level components and hooks to make it easier to build +- Provide low-level components and hooks to make it easier to build customized, quality, accessible experiences - Create a model for upstreaming work where people have already built on top of Primer From 465f6e6a67198b9093b09a180d7c01bfb00a1090 Mon Sep 17 00:00:00 2001 From: joshblack <3901764+joshblack@users.noreply.github.com> Date: Thu, 2 Jul 2026 21:51:14 +0000 Subject: [PATCH 34/38] chore: auto-fix lint and formatting issues --- contributor-docs/adrs/adr-024-modular-ds.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 67453734e79..9404c8cb32f 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -45,7 +45,7 @@ want to focus on in order to improve Primer: - Embrace layer 2 from the layer model, offering parts for our components that can be combined with hooks for improved flexibility -- Provide low-level components and hooks to make it easier to build customized, +- Provide low-level components and hooks to make it easier to build customized, quality, accessible experiences - Create a model for upstreaming work where people have already built on top of Primer From 7482985e09f14a452b0b5135488b14ea9743e14a Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 2 Jul 2026 17:05:21 -0500 Subject: [PATCH 35/38] docs: update questions section --- contributor-docs/adrs/adr-024-modular-ds.md | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index 45ca7516e6f..e2963939921 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -299,3 +299,26 @@ As a result, we should develop techniques and learn how to apply them as the design system grows. ## Questions + +### Will this be the de facto standard for all components in the Design System going forward? + +Yes, our team will prioritize authoring components as presentational components +with hooks as the starting point for new components. As patterns and opinions +emerge, we create config components to simplify adoption of those patterns. + +### Does this only apply to certain type of components? + +Not every component will need to be authored with config, presentational, and +base components in mind. Not all components will need their own hooks, either. +Generally, when writing a component we will want to start with the +presentational components first. We'll focus on markup and styles before adding +in behavior and state. For some components, this can be baked into the +component. For others, they will need to be extracted into hooks. + +There is no clear test for this yet, but one concept that seems to work is when +state does not have a clear component that owns it. For example, if we have a +`List` component and we want to manage selection then we have two options: + +- Bake it into the `List` component (this would be our config approach) +- Provide a `useSelection` hook (or bake selection into a `useList` hook) that + can then be used with `List` components to provide selection From e91056d4a03ff61c6626c4dc8743ec163f126442 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 2 Jul 2026 17:08:36 -0500 Subject: [PATCH 36/38] docs: tweak agent instructions --- .github/agents/modular-ds-implementer.agent.md | 9 ++++++--- .github/agents/modular-ds-reviewer.agent.md | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/agents/modular-ds-implementer.agent.md b/.github/agents/modular-ds-implementer.agent.md index f8e8ec9f9cb..cac4121f864 100644 --- a/.github/agents/modular-ds-implementer.agent.md +++ b/.github/agents/modular-ds-implementer.agent.md @@ -17,8 +17,9 @@ guidance, especially the spectrum of abstraction model. Follow these rules: -- Start with presentational components and behavior hooks for new flexible - component APIs. +- Start with presentational components for new flexible component APIs. Use + behavior hooks to keep implementation logic reusable, but do not make those + hooks public unless the requested API or a clear consumer need requires it. - Add config components over time when common use-cases and opinionated defaults are established, and ensure they compose presentational components and hooks instead of duplicating behavior. @@ -38,7 +39,9 @@ Follow these rules: primitives before adding new ones. - Do not expose public hooks for subcomponent internals unless there is a clear consumer need. Internal hooks are fine when they keep behavior reusable without - expanding the public API. + expanding the public API. The style guide's recommendation to pair + presentational components with behavior hooks does not mean every internal + behavior hook should become a package export. - Do not expose `data-component` as a customizable prop. Primer owns `data-component` values as component identifiers. - Avoid inventing visual styling without a concrete design reference, image, or diff --git a/.github/agents/modular-ds-reviewer.agent.md b/.github/agents/modular-ds-reviewer.agent.md index 614a5607d54..0a54fee9985 100644 --- a/.github/agents/modular-ds-reviewer.agent.md +++ b/.github/agents/modular-ds-reviewer.agent.md @@ -18,6 +18,8 @@ Check for: - Whether the change starts with presentational components and behavior hooks before introducing config-driven APIs. +- Whether behavior hooks are kept internal unless a public hook is explicitly + requested or clearly justified by consumer needs. - Whether config components compose presentational components and hooks instead of duplicating state, behavior, or markup. - Whether base components or existing utilities should be reused for @@ -35,7 +37,8 @@ Check for: accessibility behavior is reusable, including heading-first structures when a heading labels an interactive control. - Whether public hooks are justified by a clear consumer need instead of - exposing subcomponent internals by default. + exposing subcomponent internals by default, and whether any public hook has + docs metadata and tests matching its API. - Whether `data-component` values are owned by Primer and not exposed as customizable public props. - Whether visual styling is supported by a concrete design reference, image, or From 537641c7793995fbfb67b80ba1bf72a3abda1f08 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 2 Jul 2026 17:08:59 -0500 Subject: [PATCH 37/38] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- contributor-docs/adrs/adr-024-modular-ds.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributor-docs/adrs/adr-024-modular-ds.md b/contributor-docs/adrs/adr-024-modular-ds.md index ee8dcefeda5..718847c24ba 100644 --- a/contributor-docs/adrs/adr-024-modular-ds.md +++ b/contributor-docs/adrs/adr-024-modular-ds.md @@ -252,7 +252,7 @@ without relying on internal implementation details. This approach is a consolidation of our existing approach to building components. It highlights changes that need to happen in existing components, namely SelectPanel and Dialog, that would require changes in order for them to -accomodate the spectrum of abstraction model. +accommodate the spectrum of abstraction model. This decision also impacts future component development as components must start with this in mind. One critical part to this is that authoring components as From 02ec605dd596ab6d0e1a4640bd75d534cbe68943 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 6 Jul 2026 11:54:24 -0500 Subject: [PATCH 38/38] docs: add refinements from loop --- .../agents/modular-ds-implementer.agent.md | 21 ++++++++++++++++++- .github/agents/modular-ds-reviewer.agent.md | 16 +++++++++++++- contributor-docs/style.md | 12 +++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/.github/agents/modular-ds-implementer.agent.md b/.github/agents/modular-ds-implementer.agent.md index cac4121f864..78e58b74b15 100644 --- a/.github/agents/modular-ds-implementer.agent.md +++ b/.github/agents/modular-ds-implementer.agent.md @@ -26,17 +26,29 @@ Follow these rules: - Use base components, shared hooks, utilities, and behaviors for accessibility primitives and low-level behavior before creating custom one-off implementations. +- Prefer existing base primitives over recreating native elements and their + reset styles. For example, use shared button primitives such as `ButtonBase` + when a component needs Primer-owned button semantics, interaction behavior, + and reset styling instead of hand-rolling a button reset in CSS. - Consolidate accessibility primitives for established patterns, such as ARIA Authoring Practices Guide patterns, instead of reimplementing them across components. - Match the accessibility pattern to the component contract. For established ARIA Authoring Practices Guide patterns such as accordions, prefer the APG - semantics and structure expected by the pattern. + semantics and structure expected by the pattern. Treat optional APG semantics + such as accordion panel `role="region"` as opt-in when the component can + render many panels or landmarks. - Keep markup and accessibility semantics flexible. Preserve native semantics, including heading structure, and expose presentational pieces or slots when consumers need control over content, appearance, or semantics. - Search for existing Primer components, hooks, utilities, and accessibility primitives before adding new ones. +- Do not add slots, `useSlots`, or `__SLOT__` markers by default. Prefer normal + React composition for flexible presentational components. Only add slots when + the requested API explicitly needs child extraction or a parent component must + identify a specific child part. Do not reorder consumer-authored children in + presentational components; preserve author order and document recommended + structure instead. - Do not expose public hooks for subcomponent internals unless there is a clear consumer need. Internal hooks are fine when they keep behavior reusable without expanding the public API. The style guide's recommendation to pair @@ -52,6 +64,13 @@ Follow these rules: element. - Include the surfaces needed for adoption: source exports, tests, stories, docs metadata, and changesets when the published package behavior changes. +- New components and new public exports require a `minor` changeset for the + affected package. +- Public export changes must update any matching export snapshots or tests. +- When an accessibility pattern includes optional semantic tradeoffs, cover the + chosen default and opt-in behavior in tests, stories, or docs metadata. +- When using base primitives, avoid passing opinionated layout props unless the + component API exposes that choice or a concrete design reference requires it. When proposing or implementing work, explain which API type changed, why that level of abstraction is appropriate, and how the implementation can be extended diff --git a/.github/agents/modular-ds-reviewer.agent.md b/.github/agents/modular-ds-reviewer.agent.md index 0a54fee9985..6354d3b0cc5 100644 --- a/.github/agents/modular-ds-reviewer.agent.md +++ b/.github/agents/modular-ds-reviewer.agent.md @@ -25,17 +25,27 @@ Check for: - Whether base components or existing utilities should be reused for accessibility primitives, state, refs, timers, escape handling, or similar cross-component behavior. +- Whether the change reuses existing base primitives, such as `ButtonBase`, when + it needs Primer-owned native element semantics, interaction behavior, or reset + styling instead of recreating those details with custom CSS. - Whether accessibility primitives for established patterns, such as ARIA Authoring Practices Guide patterns, are consolidated instead of reimplemented across components. - Whether established ARIA Authoring Practices Guide patterns use the expected pattern semantics and structure instead of defaulting to native elements that produce a different component model. +- Whether optional APG semantics, such as accordion panel `role="region"`, are + opt-in when defaulting them would create landmark proliferation or other + semantic side effects. - Whether consumers can customize appearance, content, semantics, and behavior without forking Primer or relying on overrides. - Whether native accessibility semantics are preserved and low-level accessibility behavior is reusable, including heading-first structures when a heading labels an interactive control. +- Whether slots, `useSlots`, and `__SLOT__` markers are avoided by default and + only added when the requested API explicitly needs child extraction or a parent + must identify a specific child part. Flexible presentational components should + preserve consumer-authored child order rather than reordering children. - Whether public hooks are justified by a clear consumer need instead of exposing subcomponent internals by default, and whether any public hook has docs metadata and tests matching its API. @@ -46,7 +56,11 @@ Check for: - Whether root refs and element types default to `HTMLElement` unless a narrower element type is required. - Whether exports, stories, tests, docs metadata, and changesets match the public - API impact of the change. + API impact of the change, including a `minor` changeset for new components or + new public exports and updated export snapshots when public exports change. +- Whether base primitives are used without locking in opinionated layout unless + the component API exposes that choice or a concrete design reference requires + it. Report only actionable findings that show a concrete mismatch with the style guide. For each finding, cite the file and line, name the relevant API type or diff --git a/contributor-docs/style.md b/contributor-docs/style.md index 77d2980084d..42b67a5defb 100644 --- a/contributor-docs/style.md +++ b/contributor-docs/style.md @@ -201,6 +201,13 @@ flexible APIs where a single high-level config API has not stabilized. Build config components over time by composing those components and hooks after common use-cases and defaults become clear. +Do not add slots by default. Prefer normal React composition first, and only +introduce slots when a parent component must identify a specific child part or +the requested API explicitly needs child extraction. Do not reorder +consumer-authored children in flexible presentational components just to enforce +a preferred structure; document the recommended structure or add a dev warning +instead. + #### Base components Base components are unstyled primitives used to build higher-level components. @@ -211,6 +218,11 @@ reused rather than reimplemented across components. Before adding custom behavior to a component, look for an existing base component, hook, utility, or behavior that can provide the foundation. +Prefer existing base primitives over recreating native elements and their reset +styles. For example, use shared button primitives such as `ButtonBase` when a +new component needs Primer-owned button semantics, interaction behavior, and +reset styling instead of hand-rolling a button with custom reset CSS. + #### Utilities Utilities include hooks, functions, behaviors, and other reusable logic. Provide