Skip to content

Ensure every component exposes stable class/state hooks. #1856

Description

@kotAPI

Description

We should define and test the stable public hooks and state surfaces that components expose as part of their supported contract.

This issue is not about encouraging arbitrary selector-based styling against component internals. Instead, it is about identifying the public hooks, attributes, and state indicators that consumers and tests are allowed to rely on, and ensuring those behaviors are protected with automated tests.

Where Rad UI intentionally exposes stable hooks or state-related attributes, those surfaces should be treated as part of the public API and covered by tests so they do not regress during refactors.

Problem

Without clearly defined and tested public hooks/state surfaces:

  • consumers may rely on undocumented internal structure
  • maintainers may accidentally break supported hooks during internal refactors
  • tests may miss regressions in public state signaling
  • it becomes unclear which attributes/classes are stable contract versus incidental implementation detail
  • component behavior becomes harder to verify consistently across the library

This is especially relevant for stateful and interactive components where public state indicators are often necessary for composition, testing, and supported customization.

Intended Position

The documentation and tests should make the following explicit:

  • only documented hooks/state surfaces are considered stable
  • internal DOM structure and incidental classes are not automatically public API
  • public hooks may include stable classes, data attributes, ARIA/state attributes, slot markers, or other intentionally exposed selectors
  • those public hooks must be validated by automated tests
  • unsupported internal implementation details should not be treated as stable contract

Proposed Scope

Audit the component library and define which stable hooks/state surfaces are intentionally supported, then add test coverage for them.

This may include, where applicable:

  • stable root/slot class hooks
  • public data-* state attributes
  • disabled/selected/open/checked/orientation state markers
  • attributes used as part of the documented component contract
  • other intentionally exposed behavior/state indicators

Behavior and Test Expectations

This issue should cover behavior, not just stylability.

Examples of what should be validated:

  1. Public state attributes appear when the component enters a given state
  2. State attributes update correctly as component state changes
  3. Disabled/interactive markers reflect actual component behavior
  4. Supported hooks remain present on the expected public element
  5. Documented hooks are not removed or renamed unintentionally

Example Cases

For a disclosure-style component, tests may validate behavior like:

render(
  <Accordion.Root>
    <Accordion.Item value="item-1">
      <Accordion.Trigger>Toggle</Accordion.Trigger>
      <Accordion.Content>Content</Accordion.Content>
    </Accordion.Item>
  </Accordion.Root>
);

Expected assertions could include:

  • trigger exposes documented closed-state hook initially
  • trigger/content update to documented open-state hook after interaction
  • state markers reflect actual open/closed behavior

For a selectable component, tests may validate:

render(
  <Tabs.Root defaultValue="account">
    <Tabs.List>
      <Tabs.Trigger value="account">Account</Tabs.Trigger>
      <Tabs.Trigger value="password">Password</Tabs.Trigger>
    </Tabs.List>
  </Tabs.Root>
);

Expected assertions could include:

  • active trigger exposes the documented selected-state hook
  • inactive trigger exposes the documented unselected state
  • state markers update correctly when selection changes

Proposed Deliverables

  1. Define the stable hook/state contract for relevant components.
  2. Add automated tests covering those documented public surfaces.
  3. Update docs to distinguish stable public hooks from internal implementation details.
  4. Ensure examples and guidance reflect the supported contract accurately.

Acceptance Criteria

  1. Public stable hooks/state surfaces are identified for relevant components.
  2. Automated tests are added to verify those hooks and state behaviors.
  3. Tests cover state transitions, not just static presence.
  4. Docs clarify which hooks are supported and which internal details are not guaranteed.
  5. Unsupported internal selectors/classes are not implicitly treated as public API.

Notes

This issue should protect intentional public behavior, not expand the public API unnecessarily. The goal is to make supported hooks explicit, test them rigorously, and avoid accidental regressions while preserving freedom to refactor internal implementation details.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions