{"id":28883007,"url":"https://github.com/georapbox/alert-element","last_synced_at":"2026-04-27T08:06:39.810Z","repository":{"id":295556049,"uuid":"984075655","full_name":"georapbox/alert-element","owner":"georapbox","description":"A custom HTML element for displaying dismissible alerts and toast notifications.","archived":false,"fork":false,"pushed_at":"2026-03-01T10:16:40.000Z","size":430,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-01T14:28:40.190Z","etag":null,"topics":["alert","custom-elements","toast-notifications","web-components"],"latest_commit_sha":null,"homepage":"https://georapbox.github.io/alert-element/","language":"JavaScript","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/georapbox.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":"2025-05-15T11:04:55.000Z","updated_at":"2026-03-01T10:16:43.000Z","dependencies_parsed_at":"2025-05-26T07:16:09.554Z","dependency_job_id":null,"html_url":"https://github.com/georapbox/alert-element","commit_stats":null,"previous_names":["georapbox/alert-element"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/georapbox/alert-element","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2Falert-element","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2Falert-element/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2Falert-element/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2Falert-element/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/georapbox","download_url":"https://codeload.github.com/georapbox/alert-element/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/georapbox%2Falert-element/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32327718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["alert","custom-elements","toast-notifications","web-components"],"created_at":"2025-06-20T21:02:16.555Z","updated_at":"2026-04-27T08:06:37.833Z","avatar_url":"https://github.com/georapbox.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm version](https://img.shields.io/npm/v/@georapbox/alert-element.svg)](https://www.npmjs.com/package/@georapbox/alert-element)\n[![npm license](https://img.shields.io/npm/l/@georapbox/alert-element.svg)](https://www.npmjs.com/package/@georapbox/alert-element)\n\n[demo]: https://georapbox.github.io/alert-element/\n[license]: https://github.com/georapbox/alert-element/blob/main/LICENSE\n[changelog]: https://github.com/georapbox/alert-element/blob/main/CHANGELOG.md\n\n# \u0026lt;alert-element\u0026gt;\n\nA custom HTML element for displaying dismissible alerts and toast notifications\n\n\u003e [!NOTE]\n\u003e The element is heavily inspired by the `sl-alert` element from [Shoelace](https://shoelace.style/components/alert), especially the API for creating toast notifications.\n\u003e However, it is not a direct copy and is build from scratch without the use of any external libraries.\n\n[API documentation](#api) \u0026bull; [Demo][demo]\n\n## Install\n\n```sh\nnpm install --save @georapbox/alert-element\n```\n\n## Usage\n\n### Script\n\n```js\nimport { AlertElement } from './node_modules/@georapbox/alert-element/dist/alert-element.js';\n\n// Manually define the element.\nAlertElement.defineCustomElement();\n```\n\nAlternatively, you can import the automatically defined custom element.\n\n```js\nimport './node_modules/@georapbox/alert-element/dist/alert-element-defined.js';\n```\n\n### Markup\n\n```html\n\u003calert-element variant=\"success\" open closable\u003e\n  \u003cstrong\u003eYour profile has been updated\u003c/strong\u003e\u003cbr\u003e\n  All changes have been saved and will take effect immediately.\n\u003c/alert-element\u003e\n```\n\n### Style\n\nBy default, the component comes with some basic default styling. However, you can customise the styles of the various elements of the component using either [CSS Parts](#css-parts) or [CSS Custom Properties](#css-custom-properties).\n\n## API\n\n### Properties\n| Name | Reflects | Type | Required | Default | Description |\n| ---- | -------- | ---- | -------- | ------- | ----------- |\n| `closable` | ✓ | Boolean | - | `false` | Indicates whether the alert can be closed by the user by providing a close button. |\n| `open` | ✓ | Boolean | - | `false` | Indicates whether the alert is open or not. |\n| `duration` | ✓ | Number | - | `Infinity` | The duration in milliseconds for which the alert will be displayed before automatically closing. If the user interacts with the alert before it closes, the duration is reset. Defaults to `Infinity`, which means the alert will not close on its own. |\n| `variant` | ✓ | String | - | `\"\"` | The alert's theme variant. Can be one of `info`, `success`, `neutral`, `warning`, or `danger`. |\n| `closeLabel`\u003cbr\u003e*`close-label`* | ✓ | String | - | `\"Close\"` | The label for the default close button. It is used as the `aria-label` attribute of the close button. If user provides text content for the close button using the `close` slot, this property is ignored and the `aria-label` attribute is removed. |\n| `announce` | ✓ | String | - | `\"alert\"` | Defines how the alert should be announced to screen readers. Can be one of `alert`, `status`, or `none`. |\n| `countdown` | ✓ | Boolean | - | `false` | Indicates whether to show a countdown displaying the remaining time before the alert automatically closes. |\n| `noAnimations`\u003cbr\u003e*`no-animations`* | ✓ | Boolean | - | `false` | Disables all animations (default or custom) for showing and hiding the alert. |\n| `customAnimations` | - | Object | - | `undefined` | Custom animation keyframes and options for show/hide. The object should contain two properties: `show` and `hide`, each containing an object with `keyframes` and `options` properties. See [Animations](#animations) for more details. Set to `null` to disable animations altogether. |\n\n### Slots\n\n| Name | Description |\n| ---- | ----------- |\n| default/unnamed | The default slot for the alert message. |\n| `icon` | Slot to display an icon before the alert message. |\n| `close` | Slot to display custom content for the close button. |\n\n### CSS Parts\n\n| Name | Description |\n| ---- | ----------- |\n| `base` | The component's base wrapper. |\n| `icon` | The icon element of the alert. |\n| `message` | The message element of the alert. |\n| `close` | The close button element of the alert. |\n| `countdown` | The countdown bar element of the alert. |\n| `countdown-elapsed` | The elapsed portion of the countdown bar. |\n\n### CSS Custom Properties\n\n| Name | Description | Default |\n| ---- | ----------- | ------- |\n| `--alert-border-radius` | The border radius of the alert. | `0.25rem` |\n| `--alert-top-border-width` | The width of the top border of the alert. | `0.1875rem` |\n| `--alert-countdown-height` | The height of the countdown bar. | `0.1875rem` |\n| `--alert-fg-color` | The foreground color of the alert. | Light: `#3f3f46` - Dark: `#b6b6be` |\n| `--alert-bg-color` | The background color of the alert. | Light: `#ffffff` - Dark: `#252528` |\n| `--alert-border-color` | The border color of the alert. | Light: `#e4e4e7` - Dark: `#36363a` |\n| `--alert-base-variant-color` | The base color variant for alerts. | `var(--alert-fg-color)` |\n| `--alert-info-variant-color` | The color variant for info alerts. | Light: `#0584c7` - Dark: `#27bbfc` |\n| `--alert-success-variant-color` | The color variant for success alerts. | Light: `#16a34a` - Dark: `#3ae075` |\n| `--alert-neutral-variant-color` | The color variant for neutral alerts. | Light: `#52525b` - Dark: `#8e8e9a` |\n| `--alert-warning-variant-color` | The color variant for warning alerts. | Light: `#d87708` - Dark: `#ffbd11` |\n| `--alert-danger-variant-color` | The color variant for danger alerts. | Light: `#dc2626` - Dark: `#fe5c5c` |\n\n### Methods\n\n| Name | Type | Description | Arguments |\n| ---- | ---- | ----------- | --------- |\n| `defineCustomElement` | Static | Defines/registers the custom element with the name provided. If no name is provided, the default name is used. The method checks if the element is already defined, hence will skip trying to redefine it. | `elementName='alert-element'` |\n| `show`\u003csup\u003e1\u003c/sup\u003e | Instance | Shows the alert. Returns a promise that resolves when the alert is fully shown and all animations are complete. | - |\n| `hide`\u003csup\u003e1\u003c/sup\u003e | Instance | Hides the alert. Returns a promise that resolves when the alert is fully hidden and all animations are complete. | - |\n| `toast`\u003csup\u003e1\u003c/sup\u003e | Instance | Displays the alert as a toast notification. See [Toast notifications](#toast-notifications) for more details. | `{ forceRestart: false }` |\n\n\u003csup\u003e1\u003c/sup\u003e Instance methods are only available after the component has been defined. To ensure the component is defined, you can use `whenDefined` method of the `CustomElementRegistry` interface, eg `customElements.whenDefined('alert-element').then(() =\u003e { /* call methods here */ });`\n\n### Events\n\n| Name | Description | Event Detail |\n| ---- | ----------- | ------------ |\n| `alert-show` | Emitted when the alert is shown. | `null` |\n| `alert-after-show` | Emitted after the alert is shown and all animations are complete. | `null` |\n| `alert-hide` | Emitted when the alert is hidden. | `{ reason: 'user' \\| 'timeout' \\| 'api' }` |\n| `alert-after-hide` | Emitted after the alert is hidden and all animations are complete. | `{ reason: 'user' \\| 'timeout' \\| 'api' }` |\n\nThe events `alert-hide` and `alert-after-hide` have a `reason` property in the event detail that indicates how the alert was hidden. The possible values are:\n- `user` - The alert was closed by the user using the close button.\n- `timeout` - The alert was closed automatically after the duration (if set) has elapsed.\n- `api` - The alert was closed programmatically by using either the `hide()` method, or the `open` property, or the Invoker Commands API.\n\n## Toast notifications\n\nTo display an alert as a toast notification, create an instance of the alert element and call the `toast()` method.\nThis will move the alert out of its initial position in the DOM into the toast stack and display it as a toast notification.\nWhen there are more than one toast notifications, they will stack vertically in the toast stack.\n\nThe toast stack is a container element that is created and managed internally by the alert element and it will be \nadded in the DOM when there is at least one toast notification to display. If there are no toast notifications to display,\nthe toast stack will be removed from the DOM.\n\nBy default, the toast stack is a fixed positioned element that is displayed at the top-right corner of the viewport,\nbut you can override its position by targeting `.alert-toast-stack` class in your stylesheet.\n\n\u003e [!NOTE]\n\u003e The toast stack has a shadow DOM root, so you need to use the `::part` pseudo-element to style it.\n\u003e The reason for this is to encapsulate the styles of the toast stack and prevent them from leaking into the rest of your application.\n\nBelow are some examples of how to position the toast stack in the viewport.\n\n```css\n/* Position toast stack at the top-center of the viewport */\n.alert-toast-stack::part(base) {\n  right: 50%;\n  transform: translateX(50%);\n}\n\n/* Position toast stack at the top-left of the viewport */\n.alert-toast-stack::part(base) {\n  right: auto;\n  left: 0;\n}\n\n/* Position toast stack at the bottom-left of the viewport */\n.alert-toast-stack::part(base) {\n  right: auto;\n  left: 0;\n  top: auto;\n  bottom: 0;\n}\n\n/* Position toast stack at the bottom-center of the viewport */\n.alert-toast-stack::part(base) {\n  right: 50%;\n  transform: translateX(50%);\n  top: auto;\n  bottom: 0;\n}\n\n/* Position toast stack at the bottom-right of the viewport */\n.alert-toast-stack::part(base) {\n  top: auto;\n  bottom: 0;\n}\n```\n\nThe default styles of the toast stack are as follows:\n\n```css\n.alert-toast-stack::part(base) {\n  position: fixed;\n  top: 0;\n  right: 0;\n  z-index: 1000;\n  width: 30rem;\n  max-width: 100%;\n  max-height: 100%;\n  overflow: auto;\n  scroll-behavior: smooth;\n  scrollbar-width: none;\n}\n```\n\n## Creating toasts imperatively\n\nFor convenience, you can create a utility function that creates a toast notification imperatively,\ninstead of creating the alert elements in your markup. To do this, you can generate the alert element\nusing JavaScript and call the `toast()` method on it.\n\n#### HTML\n\n```html\n\u003cbutton type=\"button\"\u003eCreate toast\u003c/button\u003e\n```\n\n#### JavaScript\n\n```js\nconst button = document.querySelector('button');\n\nbutton.addEventListener('click', () =\u003e {\n  toastify('This is a toast notification', {\n    variant: 'success',\n    duration: 3000,\n    icon: `\u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"1.25em\" height=\"1.25em\" fill=\"currentColor\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"\u003e\n      \u003cpath d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16\"/\u003e\n      \u003cpath d=\"m10.97 4.97-.02.022-3.473 4.425-2.093-2.094a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05\"/\u003e\n    \u003c/svg\u003e`\n  });\n});\n\nfunction escapeHtml(html) {\n  const div = document.createElement('div');\n  div.textContent = html;\n  return div.innerHTML;\n}\n\nfunction toastify(message, options = {}) {\n  const defaults = {\n    duration: 3000,\n    variant: 'neutral',\n    icon: ''\n  };\n\n  options = { ...defaults, ...options };\n\n  const icon = options.icon ? `\u003cspan slot=\"icon\"\u003e${options.icon}\u003c/span\u003e` : '';\n\n  const alert = Object.assign(document.createElement('alert-element'), {\n    closable: true, // Always provide a way to close the toast notification\n    duration: options.duration,\n    variant: options.variant,\n    innerHTML: `${icon}${escapeHtml(message)}` // Escape message to prevent XSS ig you don't control the content\n  });\n\n  return alert.toast();\n}\n```\n\n## Animations\n\nThe element uses the [Web Animations API](https://developer.mozilla.org/docs/Web/API/Web_Animations_API) to animate the alert when it is shown or hidden.\nIt comes with a default animation, but you can override it by providing your own animation options, either globally using the static `AlertElement.customAnimations` property or per instance using the `customAnimations` property.\nFor example, you can override the default animation options for the `show` and `hide` animations as follows:\n\n```js\nconst customAnimations = {\n  show: {\n    keyframes: [\n      { opacity: 0, transform: 'rotateX(90deg) scale(0.8)' },\n      { opacity: 1, transform: 'rotateX(-10deg) scale(1.05)' },\n      { opacity: 1, transform: 'rotateX(5deg) scale(0.97)' },\n      { opacity: 1, transform: 'rotateX(0deg) scale(1)' }\n    ],\n    options: {\n      duration: 600,\n      easing: 'cubic-bezier(0.22, 1, 0.36, 1)'\n    }\n  },\n  hide: {\n    keyframes: [\n      { opacity: 1, transform: 'scale(1)' },\n      { opacity: 0, transform: 'scale(0.8)' }\n    ],\n    options: {\n      duration: 400,\n      easing: 'ease-in'\n    }\n  }\n};\n\n// Set the custom animations globally\nAlertElement.customAnimations = customAnimations;\n\n// Or set the custom animations for a specific instance\nconst alert = document.querySelector('alert-element');\nalert.customAnimations = customAnimations;\n````\n\n\u003e [!NOTE]\n\u003e Animations respect the users' `prefers-reduced-motion` setting.\n\u003e If the user has enabled the setting on their system, the animations will be disabled and the element will be shown/hidden instantly.\n\u003e To disable animations for all users no matter their settings, you can set the `customAnimations` property to `null`.\n\u003e\n\u003e As of **version 1.2.0**, you can also disable animations using the `no-animations` attribute or `noAnimations` property.\n\u003e This provides a more declarative way to disable animations without having to set custom animations to `null`,\n\u003e although setting `customAnimations` to `null` will still work as before.\n\n## User interaction\n\nThe alert element reacts gracefully to user interactions, making sure it stays visible and accessible while the user is engaged with it.\n\nWhen an alert has a finite `duration`, it automatically closes after the specified time has elapsed. However, if the user interacts with the alert — for example, by hovering over it with the mouse or focusing it using the keyboard — the auto-dismiss timer temporarily pauses, ensuring that the alert does not close while the user is reading or interacting with it.\n\nOnce the interaction ends (e.g., when the mouse leaves or focus moves away from the alert), the timer resumes from where it left off, continuing the countdown until the remaining time expires.\n\nThis behavior applies to all focusable elements inside the alert (such as the close button or any custom slotted controls), ensuring that keyboard and assistive-technology users receive the same non-interruptive experience as pointer users.\n\n\u003e [!NOTE]\n\u003e The interaction handling is implemented using both `mouseenter`/`mouseleave` and `focusin`/`focusout` events.\n\u003e In modern browsers, focus events from within the alert's shadow DOM are automatically retargeted to the host element,\n\u003e so the component can pause and resume correctly even when the focus is inside shadow DOM content.\n\n## Invoker Commands (Experimental)\n\nThe element supports the Invoker Commands API, enabling a more declarative way to control alerts. Use the following commands:\n\n- `--alert-show` - shows the alert element\n- `--alert-hide` - hides the alert element\n\nTo trigger these commands, add the `commandfor` and `command` attributes to a button, targeting the alert element by its `id` attribute.\n\n```html\n\u003cbutton type=\"button\" commandfor=\"myAlert\" command=\"--alert-show\"\u003eShow Alert\u003c/button\u003e\n\u003cbutton type=\"button\" commandfor=\"myAlert\" command=\"--alert-hide\"\u003eHide Alert\u003c/button\u003e\n\n\u003calert-element variant=\"info\" id=\"myAlert\"\u003e\n  \u003cstrong\u003eAlert with invoker commands\u003c/strong\u003e\u003cbr\u003e\n  This alert uses invoker commands to toggle its visibility.\n\u003c/alert-element\u003e\n```\n\n\u003e [!IMPORTANT]\n\u003e This is an experimental feature based on the [Invoker Commands API](https://developer.mozilla.org/docs/Web/API/Invoker_Commands_API).\n\u003e The API is still in the early stages and might change in the future.\n\u003e Check [browser compatibility](https://developer.mozilla.org/docs/Web/API/Invoker_Commands_API#browser_compatibility) before using it in production.\n\n## Changelog\n\nFor API updates and breaking changes, check the [CHANGELOG][changelog].\n\n## Development setup\n\n### Prerequisites\n\nThe project requires `Node.js` and `npm` to be installed on your environment. Preferrably, use [nvm](https://github.com/nvm-sh/nvm) Node Version Manager and use the version of Node.js specified in the `.nvmrc` file by running `nvm use`.\n\n### Install dependencies\n\nInstall the project dependencies by running the following command.\n\n```sh\nnpm install\n```\n\n### Build for development\n\nWatch for changes and start a development server by running the following command.\n\n```sh\nnpm start\n```\n\n### Linting\n\nLint the code by running the following command.\n\n```sh\nnpm run lint\n```\n\n### Testing\n\nRun the tests by running any of the following commands.\n\n```sh\nnpm test\nnpm run test:watch # watch mode\n```\n\n### Build for production\n\nCreate a production build by running the following command.\n\n```sh\nnpm run build\n```\n\n## License\n\n[The MIT License (MIT)][license]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeorapbox%2Falert-element","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeorapbox%2Falert-element","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeorapbox%2Falert-element/lists"}