@afkarbase/ui
v0.3.0
Published
Modern Angular Material Design 3 UI component library with comprehensive theming and accessibility features
Downloads
187
Maintainers
Readme
@afkarbase/ui
Angular UI component library — Material Design 3 inspired, zero Angular Material dependency.
v0.2.0 · Angular 19/20/21 · MIT License
Installation
npm install @afkarbase/uiRequired peer dependencies:
npm install @angular/core @angular/commonOptional peer dependencies (install if you use the corresponding features):
npm install @angular/forms # form components (inputs, select, checkbox…)
npm install @angular/animations # animated transitions
npm install @angular/router # breadcrumbs, navigationQuick Start
1. Import the library styles in your angular.json or global stylesheet:
// styles.scss
@use '@afkarbase/ui/styles'; // base styles + component styles
@use '@afkarbase/ui/themes/light'; // light theme tokensFor dark theme, add the dark stylesheet or let ThemeService toggle it at runtime:
@use '@afkarbase/ui/themes/dark';2. Import the component you need (all components are standalone):
import { UiButton } from '@afkarbase/ui';
@Component({
imports: [UiButton],
// ...
})3. Use it in your template:
<button ui-button="filled">Get started</button>
<button ui-button="outlined" [loading]="saving">Save</button>
<a ui-button="text" href="/docs">Documentation</a>Features
| Feature | Description |
|---|---|
| Zero Material dependency | No @angular/material or @angular/cdk — no version conflicts |
| Standalone components | Each component is fully standalone; import only what you use |
| CSS token theming | Every visual property is a --ui-* CSS custom property |
| Light / Dark / High-contrast | Built-in theme stylesheets, runtime switching via ThemeService |
| RTL & LTR | Full bidirectional layout support via UiDirectionality |
| Density variants | Compact, default, and comfortable density via CSS token overrides |
| Accessibility | ARIA roles, keyboard navigation, focus rings, reduced-motion support |
| Signal-based services | ThemeService and UiDirectionality expose Angular signals |
| Angular 19 / 20 / 21 | Tested across supported versions |
| Testing harnesses | UiButtonHarness, UiCheckboxHarness, UiInputHarness, and more |
Component Inventory
Actions
| Component | Selector | Description |
|---|---|---|
| Button | button[ui-button], a[ui-button] | Five variants: filled, outlined, text, elevated, tonal |
| IconButton | button[ui-icon-button], a[ui-icon-button] | Icon-only button with standard/filled/outlined/tonal variants |
| FAB | ui-fab | Floating action button |
| ButtonToggle | ui-button-toggle / ui-button-toggle-group | Toggle button group |
| SegmentedButton | ui-segmented-button / ui-segmented-button-group | Segmented control |
Display
| Component | Selector | Description |
|---|---|---|
| Card | ui-card | Surface container with header, content, actions, media slots |
| Avatar | ui-avatar / ui-avatar-group | User avatars with image, initials, and icon support |
| Icon | ui-icon | Inline SVG / ligature icon |
| Badge | ui-badge | Numeric or dot indicator |
| Chip | ui-chip / ui-chip-set / ui-chip-listbox | Input, filter, and suggestion chips |
| Divider | ui-divider | Horizontal or vertical separator |
| ProgressBar | ui-progress-bar | Determinate and indeterminate progress |
| Timeline | ui-timeline / ui-timeline-item | Vertical timeline with custom icons |
| ExpansionPanel | ui-expansion-panel / ui-accordion | Collapsible content panels |
| Toolbar | ui-toolbar | App bar / page toolbar |
Form
| Component | Selector | Description |
|---|---|---|
| FormField | ui-form-field | Wrapper providing label, hint, error, and prefix/suffix slots |
| TextInput | ui-text-input | Single-line text field |
| Textarea | ui-textarea | Multi-line text field with auto-resize |
| PasswordInput | ui-password-input | Password field with show/hide toggle |
| NumberInput | ui-number-input | Numeric input with stepper |
| SearchInput | ui-search-input | Search field with clear button |
| PhoneInput | ui-phone-input | Phone number input with country code |
| Select | ui-select | Single and multi-select dropdown |
| Autocomplete | ui-autocomplete | Combobox with filtered suggestions |
| Checkbox | ui-checkbox | Material checkbox with indeterminate state |
| Radio | ui-radio-button / ui-radio-group | Radio button group |
| ToggleSwitch | ui-toggle-switch | On/off toggle (slide toggle) |
| Slider | ui-slider | Single and range value slider |
| WYSIWYG | ui-wysiwyg | Rich text editor |
Feedback & Overlays
| Component | Selector / Service | Description |
|---|---|---|
| Alert | ui-alert | Inline alert with severity variants |
| Tooltip | [uiTooltip] | Lightweight hover/focus tooltip |
| Dialog | UiDialog service | Modal dialog with custom content |
| Snackbar | UiSnackBar service | Brief dismissible notification |
| Toast | UiToastService service | Stacked toast notifications |
| BottomSheet | service | Bottom sheet overlay |
| ProgressSpinner | ui-progress-spinner | Circular determinate/indeterminate spinner |
| Skeleton | ui-skeleton | Content placeholder |
| EmptyState | ui-empty-state | Zero-result / no-data illustration |
Navigation
| Component | Selector | Description |
|---|---|---|
| Drawer | ui-drawer-container / ui-drawer | Side navigation drawer (modal and persistent) |
| NavRail | ui-nav-rail / ui-nav-rail-item | Vertical navigation rail |
| Menu | ui-menu / [uiMenuTriggerFor] | Contextual dropdown menu |
| Breadcrumbs | ui-breadcrumbs | Page hierarchy breadcrumb trail |
| Stepper | ui-stepper | Linear/non-linear wizard stepper |
| Paginator | ui-paginator | Page navigation with size control |
| Tree | ui-tree / ui-tree-node | Hierarchical tree view |
List
| Component | Selector | Description |
|---|---|---|
| List | ui-list / ui-list-item | Structured list with leading/trailing slots |
| SelectionList | ui-selection-list / ui-selection-list-option | Checkable list items |
Utilities & Extras
| Component | Selector | Description |
|---|---|---|
| DatePicker | ui-date | Calendar date picker |
| TimePicker | ui-time | Clock time picker |
| ColorPicker | ui-color-picker | Color selection widget |
| FileUpload | ui-file-upload | Drag-and-drop file input |
| CommandPalette | ui-command-palette | Searchable command menu (Cmd+K style) |
| CodeBlock | ui-code | Syntax-highlighted code block |
| Popover | ui-popover / [uiPopoverTriggerFor] | Anchored floating content panel |
| ErrorBoundary | ui-error-boundary | Catches and displays Angular render errors |
| Chips | ui-chip-input / ui-chip-option-listbox | Tag input and multi-select chip list |
Directives
| Directive | Selector | Description |
|---|---|---|
| Ripple | [uiRipple] | Material ink ripple effect |
| TrapFocus | [uiTrapFocus] | Keyboard focus trap for overlays |
| Autofocus | [uiAutofocus] | Focus element on render |
| ClickOutside | [uiClickOutside] | Emit event on outside click |
| LongPress | [uiLongPress] | Detect long-press gestures |
| Drag & Drop | [uiDrag] / [uiDropList] / [uiDragHandle] | Drag-and-drop primitives |
Theming
The library uses a three-layer CSS custom property system:
core tokens (--ui-palette-*, --ui-spacing-*, …)
↓
semantic tokens (--ui-sys-color-primary, --ui-sys-color-surface, …)
↓
component tokens (--ui-button-container-color, …)Built-in themes
@use '@afkarbase/ui/styles'; // required: base + component styles
@use '@afkarbase/ui/themes/light'; // light color scheme
@use '@afkarbase/ui/themes/dark'; // dark color scheme
@use '@afkarbase/ui/themes/high-contrast'; // high contrastOverriding tokens
Override at any level using CSS custom properties:
:root {
/* Semantic tokens */
--ui-sys-color-primary: #1a73e8;
--ui-sys-color-secondary: #5f6368;
/* Component-level override */
--ui-button-container-color: #1a73e8;
--ui-button-label-text-color: #ffffff;
/* Density */
--ui-density: -1; /* -2 compact | 0 default | 2 comfortable */
}ThemeService
Inject ThemeService to control the active theme at runtime:
import { ThemeService } from '@afkarbase/ui';
@Component({ /* … */ })
export class AppComponent {
private theme = inject(ThemeService);
// Switch mode
setDark() { this.theme.setTheme('dark'); }
setLight() { this.theme.setTheme('light'); }
setSystem() { this.theme.setTheme('system'); }
toggle() { this.theme.toggleTheme(); }
// Dynamic source color (generates full M3 palette from one seed color)
setBrand(color: string) { this.theme.setSourceColor(color); }
// Accessibility
enableHighContrast() { this.theme.setHighContrast(true); }
enableReducedMotion() { this.theme.setReducedMotion(true); }
// Signals (use in templates with Angular's signal reactivity)
isDark = this.theme.isDark; // Signal<boolean>
currentMode = this.theme.currentTheme; // Signal<'light' | 'dark' | 'system'>
}<button (click)="toggle()">
{{ isDark() ? 'Switch to Light' : 'Switch to Dark' }}
</button>RTL Support
All components flip their layout automatically based on the document dir attribute. Use UiDirectionality to read or set direction at runtime:
import { UiDirectionality } from '@afkarbase/ui';
@Component({ /* … */ })
export class AppComponent {
private dir = inject(UiDirectionality);
isRtl = this.dir.isRtl; // Signal<boolean>
dirVal = this.dir.direction; // Signal<'ltr' | 'rtl'>
setRtl() { this.dir.setDirection('rtl'); }
setLtr() { this.dir.setDirection('ltr'); }
}To set the initial direction, add dir="rtl" to your <html> element:
<html lang="ar" dir="rtl">UiDirectionality observes <html dir="…"> via MutationObserver and updates its signals when the attribute changes at runtime.
Accessibility
- All interactive components are keyboard operable and follow ARIA authoring practices.
- Focus rings are visible by default and styled via
--ui-focus-ring-color/--ui-focus-ring-width. - Reduced motion: set via
ThemeService.setReducedMotion(true)or the OS preference is respected automatically. - Form components implement
ControlValueAccessorwith correctaria-label,aria-describedby, andaria-invalidwiring. LiveAnnouncerservice is available for dynamic screen-reader announcements.
Contributing
See CONTRIBUTING.md for development setup, coding standards, and the PR workflow.
Issues and feature requests: github.com/afkarbase/ui/issues
License
MIT © Afkarbase
