Use when: building or evaluating unstyled Primer React primitives, especially accessibility primitives or low-level behavior that other components should build on. Covers when to create a base component instead of baking behavior into a styled component, the unstyled CSS-reset convention, deciding which parts need a base equivalent, and consolidating ARIA Authoring Practices Guide patterns.
65
78%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.github/skills/modular-ds-base-components/SKILL.mdBase components are unstyled primitives used to build higher-level components. They carry no visual styling and enforce structural accessibility constraints, similar in spirit to Base UI or Radix Primitives.
function Example() {
return (
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Close />
</Dialog.Content>
</Dialog.Root>
)
}Other examples of things suited to base components: Combobox (filtering, selection), Listbox (selection), Popover, Tabs, Treeview.
Shown above with dot-notation for readability — ship flat named exports per the RSC-safe convention in modular-ds-presentational-components, not an Object.assign composed export.
Use base components for accessibility primitives and low-level behaviors that need full markup and style control. Before adding custom behavior to a component, look for an existing base component, hook, or utility that can already provide the foundation — don't reimplement it.
Prefer existing base primitives over recreating native elements and their reset styles. Where a component needs Primer-owned button semantics, interaction behavior, and reset styling, build on a shared primitive such as ButtonBase rather than hand-rolling a button reset in CSS. When you do build on a base primitive, don't pass opinionated layout props through to it unless the component's own API deliberately exposes that choice — the primitive should stay visually neutral.
Accessibility primitives for established patterns (e.g. ARIA Authoring Practices Guide patterns) should be consolidated and reused rather than reimplemented across components. If you find yourself re-solving a pattern that already has a base component elsewhere in the repo, use it instead of writing a parallel implementation.
modular-ds-presentational-components) build on top and add Primer's design tokens and layout.Not every presentational sub-part needs a base component. A base primitive earns its place when there's accessibility behavior or interactivity tied to it (e.g. a dialog root, a close control, an overlay). Purely structural parts — a label, a heading, a message wrapper — usually don't need one, since consumers can render their own markup and the surrounding base components continue to wire ARIA correctly via context.
When it's not obvious whether a given part warrants its own base component, surface the decision explicitly rather than assuming.
Where a base component needs to remove browser defaults (e.g. native <dialog>/<button> styling) without adding visual opinion, ship a minimal CSS reset using :where() selectors so the state part of the selector contributes no specificity and consumer/presentational styles always win. Note .Component:where([data-variant='x']) is 0,1,0 overall, not zero — the class still counts; it's the wrapped part that stops escalating. ADR-021 proposes replacing this convention with CSS layers (primer.components.<component-name>, per ADR-022), but both are still 🚧 and a single .module.css file in the package uses @layer today, against dozens still using :where(). Follow :where() for now, and expect these files to be retrofitted if ADR-021 is adopted.
Base components wrap the compound behavior hook for their component (see modular-ds-utilities) — they are the thin, JSX-shaped API most consumers reach for, while the hook itself stays available directly for the rarer "I need full markup control, no wrapping component at all" case. Both are first-class; a base component should not duplicate behavior that already lives in its hook. Where the hook wraps a native element with its own built-in behavior, follow the controlled component contract in modular-ds-utilities.
2909f19
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.