@bisraty/selam-ui
v0.1.0
Published
An accessible-first React component library. Every component follows WAI-ARIA APG patterns.
Maintainers
Readme
Selam UI
An accessible-first React component library. Every component follows WAI-ARIA APG patterns.
"Selam" means hello/peace in Amharic. This isn't a component library that happens to be accessible — accessibility is the whole point. Every interaction pattern here is implemented from scratch against a specific WAI-ARIA Authoring Practices Guide example: correct roles, states, keyboard interactions, and focus management, with zero runtime dependencies besides React.
📖 Browse the components in Storybook →
Install
npm install @bisraty/selam-uiimport '@bisraty/selam-ui/styles.css'; // once, near your app's root
import { Tabs, TabList, Tab, TabPanel } from '@bisraty/selam-ui';
function Example() {
return (
<Tabs defaultValue="account">
<TabList aria-label="Account settings">
<Tab value="account">Account</Tab>
<Tab value="password">Password</Tab>
</TabList>
<TabPanel value="account">Account settings go here.</TabPanel>
<TabPanel value="password">Password settings go here.</TabPanel>
</Tabs>
);
}react/react-dom (^18) are peer dependencies. Full usage docs, a
theming guide, and an accessibility deep-dive live in the
Storybook docs.
Components
| Component | APG pattern | Keyboard support |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| SkipNavLink | Skip link | Focusable via Tab; Enter jumps to main content |
| Tooltip | Tooltip | Opens on hover/focus; Escape closes |
| Accordion | Accordion | Arrow Up/Down between headers, Home/End, Enter/Space toggles |
| Tabs | Tabs | Arrow Left/Right (or Up/Down if vertical), Home/End, roving tabindex |
| Toast / ToastProvider | Alert / status live regions | Dismiss button focusable; auto-dismiss pauses on hover/focus |
| Dialog / AlertDialog | Dialog (Modal) | Tab trapped inside, Escape closes and returns focus |
| DropdownMenu | Menu Button | Arrow Up/Down, Home/End, type-ahead, Escape closes and returns focus |
| Combobox | Combobox (list autocomplete) | Arrow Up/Down (virtual focus), Enter selects, Escape closes |
| DatePicker | Date Picker Dialog | Full arrow-key grid navigation, Home/End (week), PageUp/PageDown (month), Enter, Escape |
| FocusTrap | internal primitive (also exported) — powers Dialog/AlertDialog and DatePicker | Tab/Shift+Tab cycle within, focus returns to the trigger on unmount |
Escape closes every overlay above and restores focus to whatever opened
it. Tab is trapped only inside Dialog/AlertDialog and the
DatePicker popup — everywhere else it moves through the page normally.
Accessibility commitments
- Keyboard support — every component implements its APG pattern's full keyboard interaction model, not just Enter/Escape. See the table above and each component's "Keyboard navigation" story.
- Focus management —
FocusTrap(exported) captures the previously-focused element, traps Tab at the boundaries, and restores focus on close. Composite widgets use roving tabindex (Accordion, Tabs, DropdownMenu, DatePicker's grid) or virtual focus viaaria-activedescendant(Combobox), matching what each APG pattern actually specifies. - Screen reader testing — automated: every component has a
jest-axetest asserting zero violations, plus Storybook'saddon-a11ypanel on every story. Manual testing with a real screen reader (NVDA/Windows or VoiceOver/macOS) has not yet been done — this is an open item before treating the library as production-ready, particularly forDialog,Combobox, andDatePicker. - Reduced motion —
useReducedMotion(exported) is used internally byTooltipandToastto skip animation logic entirely, not just shorten it, whenprefers-reduced-motionis set. - Contrast — the default accent (
#078930) is used for borders and focus rings (3:1 minimum); anywhere text sits directly on a solid accent fill uses the darker--selam-accent-hovershade instead, for a real margin above the 4.5:1 AA text threshold rather than a borderline pass. See the Storybook Theming page for the numbers and how to override the palette.
Roadmap
Planned, not yet built: Select, RadioGroup, Switch.
Development
npm install
npm test # vitest + jest-axe
npm run lint
npm run typecheck
npm run storybook # local dev server
npm run build # tsup: ESM + CJS + .d.ts, plus the compiled stylesheetLicense
MIT © Bisrat Yenehun
