Completed Design-System API Review
Scenario
The design-system team proposes a new Combobox primitive used by search, filters, assignment workflows, and GenUI-generated forms. The component must support keyboard navigation, async results, multi-select, grouped options, validation, virtualization, and theming.
Decision
Approve the component as a headless primitive plus styled product wrappers. Do not expose a single prop-heavy component for all use cases. Require an accessibility contract, state model, async loading contract, and migration plan before broad adoption.
API boundary
@atlas/ui-primitives/combobox
-> behavior, keyboard, ARIA, state machine, test fixtures
@atlas/design-system/combobox
-> tokens, visual states, density, theming, icons
product wrappers
-> CustomerCombobox, UserAssignCombobox, ReportFilterCombobox
Reviewed API shape
Required stable concepts:
valueinputValueoptionsselectedOptionsloadingdisabledinvalidonInputChangeonSelectionChangegetOptionKeygetOptionLabelrenderOptionemptyStateloadingStateerrorState
Rejected API choices:
| Proposal | Reason rejected |
|---|---|
any[] options with implicit label field | brittle across product domains |
| built-in data fetching | couples primitive to app data layer |
| visual-only invalid state | inaccessible error handling |
| automatic virtualization always on | unnecessary complexity for small lists |
generic metadata prop | encourages hidden product behavior inside primitive |
Accessibility contract
The component must define:
- label and accessible name requirements
- keyboard behavior for open, close, navigate, select, clear, and escape
- focus behavior after selection and validation errors
- screen-reader announcement for result count, loading, empty, and error states
- disabled and read-only behavior
- reduced-motion behavior
- touch target expectations
- form integration and error description mapping
State model
Test matrix
| Test | Required |
|---|---|
| keyboard open/navigate/select/escape | yes |
| label and error description | yes |
| async loading and stale response ordering | yes |
| empty state | yes |
| grouped options | yes |
| multi-select chips | yes |
| virtualization with keyboard navigation | yes for large list wrapper |
| high contrast and forced colors | yes |
| reduced motion | yes |
| screen-reader smoke test | manual before stable release |
Versioning policy
- primitive starts at
0.xfor two product pilots - stable
1.0requires two successful adoptions and no critical accessibility defects - breaking API changes require migration guide and codemod where possible
- product wrappers can evolve faster than primitive behavior
Adoption plan
- Pilot in report filters and user assignment.
- Collect defects, unsupported states, and API friction.
- Stabilize primitive behavior and visual wrapper separately.
- Migrate legacy select/search components by workflow priority.
- Add design-system docs with do/don't examples.
- Mark old components deprecated with removal date.
Approval criteria
- headless primitive and styled wrapper boundaries are clear
- accessibility contract is documented and tested
- async behavior does not own product data fetching
- product wrappers own domain-specific rendering
- migration plan exists for legacy select/search components
- component is compatible with GenUI registry only through validated props
Source lens
This review is grounded in Part VII design-system architecture, accessibility contracts, the enterprise design-system solution pack, and GenUI registry constraints from Part XIII.