{"id":47529845,"url":"https://github.com/mwiraszka/eagami-design-system","last_synced_at":"2026-04-14T14:01:10.139Z","repository":{"id":343138554,"uuid":"1176433469","full_name":"mwiraszka/eagami-design-system","owner":"mwiraszka","description":"Lightweight, accessible Angular UI component library built on CSS custom properties","archived":false,"fork":false,"pushed_at":"2026-04-11T17:03:07.000Z","size":3986,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-11T17:25:52.655Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mwiraszka.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-09T02:38:47.000Z","updated_at":"2026-04-05T19:52:57.000Z","dependencies_parsed_at":"2026-04-02T03:08:01.066Z","dependency_job_id":null,"html_url":"https://github.com/mwiraszka/eagami-design-system","commit_stats":null,"previous_names":["mwiraszka/eagami-design-system"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/mwiraszka/eagami-design-system","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiraszka%2Feagami-design-system","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiraszka%2Feagami-design-system/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiraszka%2Feagami-design-system/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiraszka%2Feagami-design-system/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mwiraszka","download_url":"https://codeload.github.com/mwiraszka/eagami-design-system/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiraszka%2Feagami-design-system/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31799411,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T11:13:53.975Z","status":"ssl_error","status_checked_at":"2026-04-14T11:13:53.299Z","response_time":153,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-27T22:00:28.420Z","updated_at":"2026-04-14T14:01:10.128Z","avatar_url":"https://github.com/mwiraszka.png","language":"TypeScript","funding_links":[],"categories":["Third Party Components"],"sub_categories":["UI Libraries"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/images/eagami-header.png\" alt=\"eagami design system — elegant web design\" width=\"800\" /\u003e\n\u003c/p\u003e\n\nA lightweight, accessible Angular component library built on CSS custom properties. Ready to use out of the box — install, import, and start building.\n\nEvery component is standalone, signal-based, and fully themed via design tokens. No wrapping modules, no complex setup, no runtime style conflicts. Designed to be AI-friendly with clear APIs, consistent patterns, and comprehensive documentation that makes it easy for both developers and AI assistants to work with.\n\n## Why @eagami/ui?\n\n**Approx. component sizes (gzipped)¹**\n\n| Component | **@eagami/ui** | Angular Material | PrimeNG | ng-bootstrap | ng-zorro |\n|---|---|---|---|---|---|\n| Button | ~2 KB | ~12 KB | ~8 KB | ~10 KB | ~18 KB |\n| Input | ~4 KB | ~25 KB | ~14 KB | ~20 KB | ~35 KB |\n| Checkbox | ~2 KB | ~15 KB | ~9 KB | ~12 KB | ~22 KB |\n| Dropdown / Select | ~4 KB | ~30 KB | ~20 KB | ~25 KB | ~40 KB |\n| Dialog / Modal | ~2 KB | ~20 KB | ~15 KB | ~18 KB | ~30 KB |\n\n\u003e ¹ Approximate — depends on configuration, tree-shaking, and Angular version. @eagami/ui sizes measured from production build.\n\n| | **@eagami/ui** | Angular Material | PrimeNG | ng-bootstrap | ng-zorro |\n|---|---|---|---|---|---|\n| External CSS dependency | No | No | Optional | Bootstrap (~30 KB) | No |\n| CSS custom property theming | Yes | Partial (MDC) | Yes | No (Sass vars) | No |\n| Signals-first API | Yes | Partial | No | No | No |\n| `OnPush` by default | Yes | Partial | No | No | No |\n| Runtime dependencies | 0 | CDK + animations | PrimeIcons² | Bootstrap CSS | CDK |\n\u003e ² PrimeNG components are tree-shakable but PrimeIcons font (~50 KB) is typically included globally.\n\n## Features\n\n- **Zero configuration** — works immediately after install with sensible defaults\n- **Standalone components** — no `NgModule` boilerplate, just import and use\n- **Signal-based** — built on Angular's modern reactivity primitives (`input()`, `model()`, `output()`, `effect()`)\n- **Full theming via CSS custom properties** — override any design token on `:root` or scope overrides to individual components\n- **Dark mode built in** — automatic via `prefers-color-scheme`, no extra setup\n- **Accessible** — ARIA attributes, keyboard navigation, focus management, and screen reader support throughout\n- **Form-ready** — `ControlValueAccessor` on all form components (Input, Textarea, Checkbox, Switch, Radio, Dropdown)\n- **Lightweight** — zero runtime dependencies beyond Angular\n- **Tree-shakeable** — only the components you import end up in your bundle\n\n## Installation\n\n```bash\nnpm install @eagami/ui\n# or\npnpm add @eagami/ui\n```\n\nAdd the global stylesheet to your `angular.json` (or import it in your root SCSS):\n\n```json\n\"styles\": [\"node_modules/@eagami/ui/src/styles/eagami-ui.scss\"]\n```\n\nLoad the fonts in your `index.html`:\n\n```html\n\u003clink rel=\"preconnect\" href=\"https://fonts.googleapis.com\" /\u003e\n\u003clink rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin /\u003e\n\u003clink rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,400\u0026family=Syne:wght@400;500;600;700\u0026display=swap\" /\u003e\n```\n\n## Quick start\n\n```typescript\nimport { ButtonComponent } from '@eagami/ui';\n\n@Component({\n  imports: [ButtonComponent],\n  template: `\u003cea-button variant=\"primary\" (clicked)=\"save()\"\u003eSave\u003c/ea-button\u003e`,\n})\nexport class MyComponent {\n  save() { /* ... */ }\n}\n```\n\nNo modules to register, no providers to configure. Every component works the same way — import it, drop it in your template.\n\n## Components\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAccordion\u003c/strong\u003e — expandable content sections\u003c/summary\u003e\n\nSupports single or `multi` expand mode. Built-in chevron animation and disabled state.\n\n```html\n\u003cea-accordion\u003e\n  \u003cea-accordion-item label=\"Section 1\"\u003eContent for section 1\u003c/ea-accordion-item\u003e\n  \u003cea-accordion-item label=\"Section 2\"\u003eContent for section 2\u003c/ea-accordion-item\u003e\n\u003c/ea-accordion\u003e\n```\n\n\u003cimg src=\"docs/images/accordion.png\" alt=\"Accordion component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAlert\u003c/strong\u003e — semantic alert banners with optional dismiss\u003c/summary\u003e\n\nVariants: `default` | `success` | `warning` | `error` | `info`. Two-way `visible` binding.\n\n```html\n\u003cea-alert variant=\"success\"\u003eChanges saved successfully.\u003c/ea-alert\u003e\n\u003cea-alert variant=\"error\" [dismissible]=\"true\"\u003eSomething went wrong.\u003c/ea-alert\u003e\n```\n\n\u003cimg src=\"docs/images/alert.png\" alt=\"Alert component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAutocomplete\u003c/strong\u003e — text input with filtered suggestion dropdown\u003c/summary\u003e\n\nArrow key navigation, case-insensitive substring matching, configurable `minLength` and `maxResults`. Full `ControlValueAccessor` support.\n\n```html\n\u003cea-autocomplete\n  label=\"Country\"\n  placeholder=\"Start typing…\"\n  [options]=\"countries\"\n  [(value)]=\"selectedCountry\"\n  (optionSelected)=\"onSelect($event)\" /\u003e\n```\n\n\u003cimg src=\"docs/images/autocomplete.png\" alt=\"Autocomplete component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAvatar\u003c/strong\u003e — image with initials or icon fallback\u003c/summary\u003e\n\nSizes: `xs` | `sm` | `md` | `lg` | `xl`. Shapes: `circle` | `square`.\n\n```html\n\u003cea-avatar src=\"/photo.jpg\" alt=\"User\" size=\"lg\" /\u003e\n\u003cea-avatar initials=\"MW\" shape=\"square\" /\u003e\n\u003cea-avatar /\u003e  \u003c!-- shows fallback user icon --\u003e\n```\n\n\u003cimg src=\"docs/images/avatar.png\" alt=\"Avatar component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eAvatar Editor\u003c/strong\u003e — canvas-based image editor with pan, zoom, and crop\u003c/summary\u003e\n\nDrag-and-drop upload, zoom via slider or scroll wheel. Outputs a `Blob` and data URL.\n\n```html\n\u003cea-avatar-editor\n  shape=\"circle\"\n  [canvasSize]=\"200\"\n  (cropped)=\"onCropped($event)\" /\u003e\n```\n\n\u003cimg src=\"docs/images/avatar-editor.png\" alt=\"Avatar editor component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eBadge\u003c/strong\u003e — semantic status indicators\u003c/summary\u003e\n\nVariants: `default` | `success` | `warning` | `error` | `info`. Sizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-badge variant=\"success\"\u003eActive\u003c/ea-badge\u003e\n\u003cea-badge variant=\"error\"\u003eFailed\u003c/ea-badge\u003e\n```\n\n\u003cimg src=\"docs/images/badge.png\" alt=\"Badge component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eBreadcrumbs\u003c/strong\u003e — navigation trail with chevron or slash separators\u003c/summary\u003e\n\nSeparators: `chevron` | `slash`. Items can be links (`href`), buttons (no `href`), or disabled. The last item is automatically rendered as the current page.\n\n```html\n\u003cea-breadcrumbs\n  [items]=\"[\n    { label: 'Home', href: '/' },\n    { label: 'Products', href: '/products' },\n    { label: 'MacBook Pro' }\n  ]\"\n  (itemClicked)=\"navigate($event)\" /\u003e\n```\n\n\u003cimg src=\"docs/images/breadcrumbs.png\" alt=\"Breadcrumbs component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eButton\u003c/strong\u003e — primary, secondary, ghost, danger variants with loading state\u003c/summary\u003e\n\nSizes: `sm` | `md` | `lg`. Supports `loading`, `disabled`, and `fullWidth` states.\n\n```html\n\u003cea-button variant=\"primary\" size=\"md\" [loading]=\"isSaving\" (clicked)=\"save()\"\u003e\n  Save changes\n\u003c/ea-button\u003e\n```\n\n\u003cimg src=\"docs/images/button.png\" alt=\"Button component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eCard\u003c/strong\u003e — content container with elevated, outlined, and filled variants\u003c/summary\u003e\n\nPadding: `none` | `sm` | `md` | `lg` | `xl`. Customizable shadow via `--ea-card-shadow`.\n\n```html\n\u003cea-card variant=\"elevated\"\u003e\n  \u003cspan eaCardHeader\u003eCard Title\u003c/span\u003e\n  Card body content goes here.\n  \u003cspan eaCardFooter\u003e\n    \u003cea-button variant=\"secondary\" size=\"sm\"\u003eCancel\u003c/ea-button\u003e\n    \u003cea-button size=\"sm\"\u003eSave\u003c/ea-button\u003e\n  \u003c/span\u003e\n\u003c/ea-card\u003e\n```\n\n\u003cimg src=\"docs/images/card.png\" alt=\"Card component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eCheckbox\u003c/strong\u003e — with indeterminate state and ControlValueAccessor\u003c/summary\u003e\n\nSizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-checkbox label=\"Accept terms and conditions\" [(checked)]=\"accepted\" /\u003e\n```\n\n\u003cimg src=\"docs/images/checkbox.png\" alt=\"Checkbox component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eCode Input\u003c/strong\u003e — verification code entry with auto-advance and paste support\u003c/summary\u003e\n\nConfigurable `length` (default 6). Full `ControlValueAccessor` support.\n\n```html\n\u003cea-code-input [(value)]=\"code\" [length]=\"6\" (completed)=\"verify()\" /\u003e\n```\n\n\u003cimg src=\"docs/images/code-input.png\" alt=\"Code input component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eData Table\u003c/strong\u003e — sortable columns, sticky headers, density modes\u003c/summary\u003e\n\nStriped, bordered, and hoverable rows. Custom cell templates via `ng-template`. Density: `compact` | `comfortable` | `spacious`. Two-way `sort` binding.\n\n```html\n\u003cea-data-table\n  [columns]=\"columns\"\n  [data]=\"users\"\n  [stickyHeader]=\"true\"\n  [striped]=\"true\"\n  [(sort)]=\"sortState\"\n  trackBy=\"id\" /\u003e\n```\n\n\u003cimg src=\"docs/images/data-table.png\" alt=\"Data table component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDialog\u003c/strong\u003e — native dialog element with focus trapping\u003c/summary\u003e\n\nSizes: `sm` | `md` | `lg` | `full`. Two-way `open` binding.\n\n```html\n\u003cea-button (clicked)=\"dialogOpen.set(true)\"\u003eOpen\u003c/ea-button\u003e\n\n\u003cea-dialog [(open)]=\"dialogOpen\" size=\"md\"\u003e\n  \u003cspan slot=\"header\"\u003eConfirm\u003c/span\u003e\n  \u003cp\u003eAre you sure?\u003c/p\u003e\n  \u003cspan slot=\"footer\"\u003e\n    \u003cea-button variant=\"secondary\" (clicked)=\"dialogOpen.set(false)\"\u003eCancel\u003c/ea-button\u003e\n    \u003cea-button (clicked)=\"confirm()\"\u003eConfirm\u003c/ea-button\u003e\n  \u003c/span\u003e\n\u003c/ea-dialog\u003e\n```\n\n\u003cimg src=\"docs/images/dialog.png\" alt=\"Dialog component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDivider\u003c/strong\u003e — visual separator with optional label\u003c/summary\u003e\n\nOrientation: `horizontal` | `vertical`.\n\n```html\n\u003cea-divider /\u003e\n\u003cea-divider label=\"or\" /\u003e\n\u003cea-divider orientation=\"vertical\" /\u003e\n```\n\n\u003cimg src=\"docs/images/divider.png\" alt=\"Divider component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDrawer\u003c/strong\u003e — side panel using native dialog with focus trapping\u003c/summary\u003e\n\nPositions: `left` | `right` | `top` | `bottom`. Sizes: `sm` | `md` | `lg` | `full`. Two-way `open` binding.\n\n```html\n\u003cea-button (clicked)=\"drawerOpen.set(true)\"\u003eOpen\u003c/ea-button\u003e\n\n\u003cea-drawer [(open)]=\"drawerOpen\" position=\"right\" size=\"md\"\u003e\n  \u003cspan slot=\"header\"\u003eDetails\u003c/span\u003e\n  \u003cp\u003eDrawer body content…\u003c/p\u003e\n  \u003cspan slot=\"footer\"\u003e\n    \u003cea-button variant=\"secondary\" (clicked)=\"drawerOpen.set(false)\"\u003eCancel\u003c/ea-button\u003e\n    \u003cea-button (clicked)=\"save()\"\u003eSave\u003c/ea-button\u003e\n  \u003c/span\u003e\n\u003c/ea-drawer\u003e\n```\n\n\u003cimg src=\"docs/images/drawer.png\" alt=\"Drawer component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDropdown\u003c/strong\u003e — select with ControlValueAccessor and keyboard navigation\u003c/summary\u003e\n\nArrow keys, Enter/Space to select, Escape to close. Sizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-dropdown\n  label=\"Country\"\n  placeholder=\"Select a country…\"\n  [options]=\"countries\"\n  [(value)]=\"selectedCountry\" /\u003e\n```\n\n\u003cimg src=\"docs/images/dropdown.png\" alt=\"Dropdown component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eEagami Wordmark\u003c/strong\u003e — branded logo wordmark linking to eagami.com\u003c/summary\u003e\n\nVariants: `logo` (icon only) | `signature` (handcrafted by eagami) | `brand` (eagami with tagline). Three discrete sizes (`sm`, `md`, `lg`) scale both the logo and the text proportionally.\n\n```html\n\u003cea-eagami-wordmark variant=\"brand\" size=\"md\" /\u003e\n```\n\n\u003cimg src=\"docs/images/eagami-wordmark.png\" alt=\"Eagami Wordmark component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eInput\u003c/strong\u003e — text field with ControlValueAccessor and password toggle\u003c/summary\u003e\n\nTypes: `text` | `email` | `password` | `number` | `search` | `tel` | `url`. Sizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-input\n  label=\"Email\"\n  type=\"email\"\n  placeholder=\"you@example.com\"\n  hint=\"We'll never share your email\"\n  [(value)]=\"email\" /\u003e\n```\n\n\u003cimg src=\"docs/images/input.png\" alt=\"Input component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eMenu\u003c/strong\u003e — popup action menu with trigger slot and menu items\u003c/summary\u003e\n\nPlacements: `bottom-start` | `bottom-end` | `top-start` | `top-end`. Menu items support icons, disabled state, and a `danger` variant. Closes on outside click or Escape.\n\n```html\n\u003cea-menu placement=\"bottom-end\"\u003e\n  \u003cea-button slot=\"trigger\" variant=\"secondary\"\u003eActions\u003c/ea-button\u003e\n  \u003cea-menu-item (itemClicked)=\"edit()\"\u003e\n    \u003cea-icon-pencil slot=\"icon\" /\u003e\n    Edit\n  \u003c/ea-menu-item\u003e\n  \u003cea-menu-item variant=\"danger\" (itemClicked)=\"delete()\"\u003e\n    \u003cea-icon-trash slot=\"icon\" /\u003e\n    Delete\n  \u003c/ea-menu-item\u003e\n\u003c/ea-menu\u003e\n```\n\n\u003cimg src=\"docs/images/menu.png\" alt=\"Menu component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003ePaginator\u003c/strong\u003e — page navigation with configurable page sizes\u003c/summary\u003e\n\nPlacement: `left` | `center` | `right`. Emits `pageChange` events with current page, page size, and total.\n\n```html\n\u003cea-paginator\n  [total]=\"100\"\n  [pageSize]=\"10\"\n  placement=\"center\"\n  (pageChange)=\"onPageChange($event)\" /\u003e\n```\n\n\u003cimg src=\"docs/images/paginator.png\" alt=\"Paginator component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eProgress Bar\u003c/strong\u003e — determinate and indeterminate linear indicator\u003c/summary\u003e\n\nVariants: `default` | `success` | `warning` | `error` | `info`. Sizes: `sm` | `md` | `lg`. Optional `label` and `showValue` display.\n\n```html\n\u003cea-progress-bar [value]=\"72\" label=\"Uploading\" [showValue]=\"true\" /\u003e\n\u003cea-progress-bar variant=\"success\" [value]=\"100\" /\u003e\n\u003cea-progress-bar [indeterminate]=\"true\" label=\"Processing…\" /\u003e\n```\n\n\u003cimg src=\"docs/images/progress-bar.png\" alt=\"Progress bar component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eRadio Group\u003c/strong\u003e — composite pattern with ControlValueAccessor\u003c/summary\u003e\n\nSupports `vertical` and `horizontal` orientation. Sizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-radio-group [(value)]=\"plan\"\u003e\n  \u003cea-radio value=\"free\" label=\"Free\" /\u003e\n  \u003cea-radio value=\"pro\" label=\"Pro\" /\u003e\n  \u003cea-radio value=\"enterprise\" label=\"Enterprise\" /\u003e\n\u003c/ea-radio-group\u003e\n```\n\n\u003cimg src=\"docs/images/radio.png\" alt=\"Radio group component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eSkeleton\u003c/strong\u003e — loading placeholder with animated pulse\u003c/summary\u003e\n\nVariants: `text` | `circle` | `rect`. Custom `width` and `height`. Respects `prefers-reduced-motion`.\n\n```html\n\u003cea-skeleton variant=\"text\" width=\"200px\" /\u003e\n\u003cea-skeleton variant=\"circle\" width=\"48px\" height=\"48px\" /\u003e\n\u003cea-skeleton variant=\"rect\" width=\"100%\" height=\"120px\" /\u003e\n```\n\n\u003cimg src=\"docs/images/skeleton.png\" alt=\"Skeleton component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eSpinner\u003c/strong\u003e — SVG loading indicator with accessible role\u003c/summary\u003e\n\nSizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-spinner size=\"md\" label=\"Loading data\" /\u003e\n```\n\n\u003cimg src=\"docs/images/spinner.png\" alt=\"Spinner component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eSwitch\u003c/strong\u003e — toggle with ControlValueAccessor\u003c/summary\u003e\n\nSizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-switch label=\"Enable notifications\" [(checked)]=\"notificationsOn\" /\u003e\n```\n\n\u003cimg src=\"docs/images/switch.png\" alt=\"Switch component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTabs\u003c/strong\u003e — tab navigation with keyboard support\u003c/summary\u003e\n\nVariants: `underline` | `filled`. Sizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-tabs activeTab=\"account\" variant=\"underline\"\u003e\n  \u003cea-tab value=\"account\" label=\"Account\"\u003eAccount content\u003c/ea-tab\u003e\n  \u003cea-tab value=\"security\" label=\"Security\"\u003eSecurity content\u003c/ea-tab\u003e\n\u003c/ea-tabs\u003e\n```\n\n\u003cimg src=\"docs/images/tabs.png\" alt=\"Tabs component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTag\u003c/strong\u003e — inline label with optional remove button\u003c/summary\u003e\n\nVariants: `default` | `primary` | `success` | `warning` | `error` | `info`. Sizes: `sm` | `md` | `lg`.\n\n```html\n\u003cea-tag variant=\"primary\"\u003eTypeScript\u003c/ea-tag\u003e\n\u003cea-tag variant=\"success\" [removable]=\"true\" (removed)=\"onRemove()\"\u003eActive\u003c/ea-tag\u003e\n```\n\n\u003cimg src=\"docs/images/tag.png\" alt=\"Tag component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTextarea\u003c/strong\u003e — multiline text with ControlValueAccessor\u003c/summary\u003e\n\nMirrors the Input API. Configurable `rows`, `resize` (`none` | `vertical` | `horizontal` | `both`), and `maxlength`.\n\n```html\n\u003cea-textarea\n  label=\"Message\"\n  placeholder=\"Enter your message…\"\n  hint=\"Maximum 500 characters\"\n  [rows]=\"4\"\n  [(value)]=\"message\" /\u003e\n```\n\n\u003cimg src=\"docs/images/textarea.png\" alt=\"Textarea component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eToast\u003c/strong\u003e — notification system via injectable ToastService\u003c/summary\u003e\n\nVariants: `default` | `success` | `warning` | `error` | `info`. Auto-dismiss with configurable duration. Full-width on mobile, independent widths on desktop.\n\n```typescript\nimport { ToastService } from '@eagami/ui';\n\nexport class MyComponent {\n  private toast = inject(ToastService);\n\n  save() {\n    this.toast.success('Changes saved');\n  }\n\n  handleError() {\n    this.toast.error('Something went wrong');\n  }\n}\n```\n\nAdd the toast outlet once in your root template:\n\n```html\n\u003cea-toast /\u003e\n```\n\n\u003cimg src=\"docs/images/toast.png\" alt=\"Toast component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTooltip\u003c/strong\u003e — positioned tooltip on hover and focus\u003c/summary\u003e\n\nPositions: `top` | `bottom` | `left` | `right`.\n\n```html\n\u003cea-button eaTooltip=\"Save your changes\" tooltipPosition=\"top\"\u003eSave\u003c/ea-button\u003e\n```\n\n\u003cimg src=\"docs/images/tooltip.png\" alt=\"Tooltip component\" width=\"560\" /\u003e\n\n\u003c/details\u003e\n\n## Icons\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003e52 built-in SVG icon components\u003c/strong\u003e — Feather-style (24x24, stroke-based, inherits \u003ccode\u003ecurrentColor\u003c/code\u003e)\u003c/summary\u003e\n\n| Tag | Preview |\n|---|---|\n| `\u003cea-icon-alert-circle /\u003e` | \u003cimg src=\"docs/images/icons/alert-circle.png\" width=\"48\" height=\"48\" alt=\"alert-circle\" /\u003e |\n| `\u003cea-icon-alert-triangle /\u003e` | \u003cimg src=\"docs/images/icons/alert-triangle.png\" width=\"48\" height=\"48\" alt=\"alert-triangle\" /\u003e |\n| `\u003cea-icon-apple /\u003e` | \u003cimg src=\"docs/images/icons/apple.png\" width=\"48\" height=\"48\" alt=\"apple\" /\u003e |\n| `\u003cea-icon-arrow-down /\u003e` | \u003cimg src=\"docs/images/icons/arrow-down.png\" width=\"48\" height=\"48\" alt=\"arrow-down\" /\u003e |\n| `\u003cea-icon-arrow-left /\u003e` | \u003cimg src=\"docs/images/icons/arrow-left.png\" width=\"48\" height=\"48\" alt=\"arrow-left\" /\u003e |\n| `\u003cea-icon-arrow-right /\u003e` | \u003cimg src=\"docs/images/icons/arrow-right.png\" width=\"48\" height=\"48\" alt=\"arrow-right\" /\u003e |\n| `\u003cea-icon-arrow-up /\u003e` | \u003cimg src=\"docs/images/icons/arrow-up.png\" width=\"48\" height=\"48\" alt=\"arrow-up\" /\u003e |\n| `\u003cea-icon-bell /\u003e` | \u003cimg src=\"docs/images/icons/bell.png\" width=\"48\" height=\"48\" alt=\"bell\" /\u003e |\n| `\u003cea-icon-calendar /\u003e` | \u003cimg src=\"docs/images/icons/calendar.png\" width=\"48\" height=\"48\" alt=\"calendar\" /\u003e |\n| `\u003cea-icon-camera /\u003e` | \u003cimg src=\"docs/images/icons/camera.png\" width=\"48\" height=\"48\" alt=\"camera\" /\u003e |\n| `\u003cea-icon-check /\u003e` | \u003cimg src=\"docs/images/icons/check.png\" width=\"48\" height=\"48\" alt=\"check\" /\u003e |\n| `\u003cea-icon-check-circle /\u003e` | \u003cimg src=\"docs/images/icons/check-circle.png\" width=\"48\" height=\"48\" alt=\"check-circle\" /\u003e |\n| `\u003cea-icon-chevron-down /\u003e` | \u003cimg src=\"docs/images/icons/chevron-down.png\" width=\"48\" height=\"48\" alt=\"chevron-down\" /\u003e |\n| `\u003cea-icon-chevron-left /\u003e` | \u003cimg src=\"docs/images/icons/chevron-left.png\" width=\"48\" height=\"48\" alt=\"chevron-left\" /\u003e |\n| `\u003cea-icon-chevron-right /\u003e` | \u003cimg src=\"docs/images/icons/chevron-right.png\" width=\"48\" height=\"48\" alt=\"chevron-right\" /\u003e |\n| `\u003cea-icon-chevron-up /\u003e` | \u003cimg src=\"docs/images/icons/chevron-up.png\" width=\"48\" height=\"48\" alt=\"chevron-up\" /\u003e |\n| `\u003cea-icon-chevrons-up-down /\u003e` | \u003cimg src=\"docs/images/icons/chevrons-up-down.png\" width=\"48\" height=\"48\" alt=\"chevrons-up-down\" /\u003e |\n| `\u003cea-icon-clock /\u003e` | \u003cimg src=\"docs/images/icons/clock.png\" width=\"48\" height=\"48\" alt=\"clock\" /\u003e |\n| `\u003cea-icon-copy /\u003e` | \u003cimg src=\"docs/images/icons/copy.png\" width=\"48\" height=\"48\" alt=\"copy\" /\u003e |\n| `\u003cea-icon-download /\u003e` | \u003cimg src=\"docs/images/icons/download.png\" width=\"48\" height=\"48\" alt=\"download\" /\u003e |\n| `\u003cea-icon-eagami /\u003e` | \u003cimg src=\"docs/images/icons/eagami.png\" width=\"48\" height=\"48\" alt=\"eagami\" /\u003e |\n| `\u003cea-icon-external-link /\u003e` | \u003cimg src=\"docs/images/icons/external-link.png\" width=\"48\" height=\"48\" alt=\"external-link\" /\u003e |\n| `\u003cea-icon-eye /\u003e` | \u003cimg src=\"docs/images/icons/eye.png\" width=\"48\" height=\"48\" alt=\"eye\" /\u003e |\n| `\u003cea-icon-eye-off /\u003e` | \u003cimg src=\"docs/images/icons/eye-off.png\" width=\"48\" height=\"48\" alt=\"eye-off\" /\u003e |\n| `\u003cea-icon-facebook /\u003e` | \u003cimg src=\"docs/images/icons/facebook.png\" width=\"48\" height=\"48\" alt=\"facebook\" /\u003e |\n| `\u003cea-icon-file /\u003e` | \u003cimg src=\"docs/images/icons/file.png\" width=\"48\" height=\"48\" alt=\"file\" /\u003e |\n| `\u003cea-icon-filter /\u003e` | \u003cimg src=\"docs/images/icons/filter.png\" width=\"48\" height=\"48\" alt=\"filter\" /\u003e |\n| `\u003cea-icon-github /\u003e` | \u003cimg src=\"docs/images/icons/github.png\" width=\"48\" height=\"48\" alt=\"github\" /\u003e |\n| `\u003cea-icon-google /\u003e` | \u003cimg src=\"docs/images/icons/google.png\" width=\"48\" height=\"48\" alt=\"google\" /\u003e |\n| `\u003cea-icon-heart /\u003e` | \u003cimg src=\"docs/images/icons/heart.png\" width=\"48\" height=\"48\" alt=\"heart\" /\u003e |\n| `\u003cea-icon-image /\u003e` | \u003cimg src=\"docs/images/icons/image.png\" width=\"48\" height=\"48\" alt=\"image\" /\u003e |\n| `\u003cea-icon-info /\u003e` | \u003cimg src=\"docs/images/icons/info.png\" width=\"48\" height=\"48\" alt=\"info\" /\u003e |\n| `\u003cea-icon-link /\u003e` | \u003cimg src=\"docs/images/icons/link.png\" width=\"48\" height=\"48\" alt=\"link\" /\u003e |\n| `\u003cea-icon-loader /\u003e` | \u003cimg src=\"docs/images/icons/loader.png\" width=\"48\" height=\"48\" alt=\"loader\" /\u003e |\n| `\u003cea-icon-log-out /\u003e` | \u003cimg src=\"docs/images/icons/log-out.png\" width=\"48\" height=\"48\" alt=\"log-out\" /\u003e |\n| `\u003cea-icon-mail /\u003e` | \u003cimg src=\"docs/images/icons/mail.png\" width=\"48\" height=\"48\" alt=\"mail\" /\u003e |\n| `\u003cea-icon-menu /\u003e` | \u003cimg src=\"docs/images/icons/menu.png\" width=\"48\" height=\"48\" alt=\"menu\" /\u003e |\n| `\u003cea-icon-microsoft /\u003e` | \u003cimg src=\"docs/images/icons/microsoft.png\" width=\"48\" height=\"48\" alt=\"microsoft\" /\u003e |\n| `\u003cea-icon-minus /\u003e` | \u003cimg src=\"docs/images/icons/minus.png\" width=\"48\" height=\"48\" alt=\"minus\" /\u003e |\n| `\u003cea-icon-more-horizontal /\u003e` | \u003cimg src=\"docs/images/icons/more-horizontal.png\" width=\"48\" height=\"48\" alt=\"more-horizontal\" /\u003e |\n| `\u003cea-icon-pencil /\u003e` | \u003cimg src=\"docs/images/icons/pencil.png\" width=\"48\" height=\"48\" alt=\"pencil\" /\u003e |\n| `\u003cea-icon-plus /\u003e` | \u003cimg src=\"docs/images/icons/plus.png\" width=\"48\" height=\"48\" alt=\"plus\" /\u003e |\n| `\u003cea-icon-rotate-ccw /\u003e` | \u003cimg src=\"docs/images/icons/rotate-ccw.png\" width=\"48\" height=\"48\" alt=\"rotate-ccw\" /\u003e |\n| `\u003cea-icon-search /\u003e` | \u003cimg src=\"docs/images/icons/search.png\" width=\"48\" height=\"48\" alt=\"search\" /\u003e |\n| `\u003cea-icon-settings /\u003e` | \u003cimg src=\"docs/images/icons/settings.png\" width=\"48\" height=\"48\" alt=\"settings\" /\u003e |\n| `\u003cea-icon-star /\u003e` | \u003cimg src=\"docs/images/icons/star.png\" width=\"48\" height=\"48\" alt=\"star\" /\u003e |\n| `\u003cea-icon-trash /\u003e` | \u003cimg src=\"docs/images/icons/trash.png\" width=\"48\" height=\"48\" alt=\"trash\" /\u003e |\n| `\u003cea-icon-upload /\u003e` | \u003cimg src=\"docs/images/icons/upload.png\" width=\"48\" height=\"48\" alt=\"upload\" /\u003e |\n| `\u003cea-icon-user /\u003e` | \u003cimg src=\"docs/images/icons/user.png\" width=\"48\" height=\"48\" alt=\"user\" /\u003e |\n| `\u003cea-icon-x /\u003e` | \u003cimg src=\"docs/images/icons/x.png\" width=\"48\" height=\"48\" alt=\"x\" /\u003e |\n| `\u003cea-icon-x-circle /\u003e` | \u003cimg src=\"docs/images/icons/x-circle.png\" width=\"48\" height=\"48\" alt=\"x-circle\" /\u003e |\n| `\u003cea-icon-x-twitter /\u003e` | \u003cimg src=\"docs/images/icons/x-twitter.png\" width=\"48\" height=\"48\" alt=\"x-twitter\" /\u003e |\n\n\u003c/details\u003e\n\n## Theming\n\nAll visual properties are controlled through CSS custom properties defined on `:root`. Override any token to customize the entire library:\n\n```css\n:root {\n  --color-primary-600: #2563eb;\n  --font-family-sans: 'Inter', sans-serif;\n  --radius-md: 0.5rem;\n}\n```\n\nComponent-level overrides are available where useful:\n\n```css\n.my-card {\n  --ea-card-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);\n  --ea-button-font-weight: 600;\n}\n```\n\nSee [`src/styles/tokens/`](src/styles/tokens/) for the full token reference.\n\n## Peer dependencies\n\n| Package | Version |\n|---------|---------|\n| `@angular/common` | `^21.0.0` |\n| `@angular/core` | `^21.0.0` |\n| `@angular/forms` | `^21.0.0` |\n\n## Development\n\n```bash\npnpm install       # Install dependencies\npnpm sandbox       # Run sandbox dev app\npnpm storybook     # Run Storybook\npnpm test          # Run tests\npnpm build         # Build the library\npnpm lint          # Lint\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwiraszka%2Feagami-design-system","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmwiraszka%2Feagami-design-system","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwiraszka%2Feagami-design-system/lists"}