Skip to content

Web components : Add usa-alert component#271

Merged
ethangardner merged 12 commits into
uswds:developfrom
ericsorenson:develop
Jun 3, 2026
Merged

Web components : Add usa-alert component#271
ethangardner merged 12 commits into
uswds:developfrom
ericsorenson:develop

Conversation

@ericsorenson

@ericsorenson ericsorenson commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary

New alert component. Added usa-alert with support for info, warning, error, success, and emergency statuses, including slim, no-icon, and closeable variants.

Related issue

N/A

Preview link

N/A

Problem statement

The library currently has one real component (usa-banner). The alert pattern is one of the most commonly used USWDS components and its absence blocks adoption. Additionally, the architecture needed validation from a second non-trivial component to confirm that patterns around slots, CSS custom properties, variant attributes, and event dispatch work correctly.
Solution

Ported the VADS va-alert (Stencil) component to Lit as usa-alert, using USWDS core's alert pattern as the visual/behavioral reference. Key design decisions:

Reactive rendering only — no imperative DOM manipulation (avoids the anti-pattern present in the original banner)
Context-appropriate ARIA roles — role="alert" for error/warning/emergency, role="status" for info/success
CSS custom properties follow --usa-alert-* naming convention
Status icons via CSS masks (no SVG imports per-status, keeps bundle small)
Closeable via close event — consumers control focus management and element lifecycle
2 kB brotli — well within 3 kB budget

Major changes

src/components/usa-alert/index.ts — LitElement component class
src/components/usa-alert/usa-alert.css — Styles with 5 status variants, slim, no-icon, forced-colors support
src/components/usa-alert/usa-alert.spec.ts — 24 unit tests
src/components/usa-alert/usa-alert.stories.ts — 9 Storybook stories
config/vite.config.ts — Entry point with 3 kB size budget
src/components/index.ts — Export added
custom-elements.json — Regenerated manifest (auto-generated)

Testing and review

npm run test:ci — 46 tests pass (24 for usa-alert)
npm run build — succeeds, all bundle size checks pass, framework wrappers generated
Recommend reviewing in Storybook (npm start) to verify visual appearance of all variants
Feedback welcome on: component API surface, CSS custom property naming, status-to-role mapping

ericsorenson and others added 4 commits May 14, 2026 09:10
Move "sass" from dependencies to devDependencies in package.json to reflect it's a build-time tool. Update UsaBanner to use Lit's boolean attribute binding (?hidden) driven by isOpen and remove the manual DOM attribute toggle in toggle(). Correct spacing token "05" from .025rem to .25rem in tokens/dimension/spacing.json.
Introduce a new UsaAlert web component (LitElement) with status variants (info, warning, error, success, emergency), slim, no-icon, and closeable options. Ship styles, Storybook stories, and unit tests; dispatches a close event and uses role=alert/status for accessibility. Also register/export the component, add a Vite build entry with size limit, and update custom-elements.json and a changeset.
@changeset-bot

changeset-bot Bot commented May 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e1abe06

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ericsorenson ericsorenson marked this pull request as ready for review May 18, 2026 18:28
Comment thread src/components/usa-alert/usa-alert.spec.ts Outdated
Comment thread src/components/usa-alert/index.ts Outdated
Comment thread src/components/usa-alert/usa-alert.css Outdated
Comment thread src/components/usa-alert/index.ts Outdated
Comment thread src/components/usa-alert/usa-alert.css
Comment thread src/components/usa-alert/index.ts
Comment thread src/components/usa-alert/index.ts
Introduce an async updateComplete() helper that awaits getAlert().updateComplete in src/components/usa-alert/usa-alert.spec.ts, and replace repeated await getAlert().updateComplete calls with await updateComplete() to reduce duplication and improve test readability/maintainability.
Update usa-alert.css to replace calc(var(--usa-alert-padding-x) + 1.5rem + 0.5rem) with calc(var(--usa-alert-padding-x) + 2rem) for .usa-alert--slim .usa-alert__body. This simplifies the expression by combining the two fixed offsets into a single value, keeping the computed spacing unchanged and improving readability.
Switch the private role getter from `_role` to the ECMAScript private accessor `#role` and update the template reference accordingly. Also remove the `aria-hidden="true"` attribute from the close-icon span so the icon is not explicitly hidden; the close button still uses an `aria-label` for its accessible name.
@annepetersen

annepetersen commented May 19, 2026

Copy link
Copy Markdown
Contributor

Localization-strategy-wise, let's ping @ethangardner and/or @heymatthenry to chat with me to determine a call on that. May take us a minute to reply, but we'll put it on the list. Thanks for this work, both of you!

Comment thread .changeset/add-usa-alert-component.md Outdated
@caseywatts

Copy link
Copy Markdown

I did a spike of an Alert component last year, and I wanted to share my PR in case it's helpful!
#187

There are a bunch of remaining issues with my approach that I haven't gotten to resolve though, I've got those tracked over here:
open-government-design-system/ogds-elements#5

@ethangardner ethangardner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice work here, @ericsorenson. I think you made some great design choices, and I appreciated your test coverage and thoroughness. There are a few changes below, but this is a great start on this component. Also, thanks for your patience waiting for the code review.

Comment thread src/components/usa-alert/index.ts Outdated

private _handleClose() {
this.dispatchEvent(
new CustomEvent("close", { bubbles: true, composed: true }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the first instance of a custom event in this project, and we don't have an established convention for naming them yet. I'll write an ADR for this in a follow up PR, but researching other design systems, the safest option in terms of avoiding a collision with other libraries is to prefix the event with the tag name i.e. new CustomEvent("usa-alert-close", { bubbles: true, composed: true }).

That way, if someone uses document.addEventListener("close", () => {}) we will avoid any unwanted side effects. The native <dialog> tag fires a close event for example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

Comment thread src/components/usa-alert/index.ts Outdated
this._visible = true;
}

private get #role(): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that we don't have a build check running in CI, but when I built this locally with npm run build I got the error below.

src/components/usa-alert/index.ts:70:3 - error TS18010: An accessibility modifier cannot be used with a private identifier.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, I'll be sure to check for this in the future.

Comment thread src/components/usa-alert/usa-alert.css Outdated
--usa-alert-icon-color: var(--usa-alert-emergency-icon);

& .usa-alert__body {
color: #fff;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the background and border colors are configurable, we should also expose the body color via a prop so there's a way to ensure adequate contrast if the bg color is changed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

Comment thread src/components/usa-alert/usa-alert.css
Comment thread src/components/usa-alert/usa-alert.css
expect(alertDiv?.classList.contains("usa-alert--info")).toBe(true);
});

it("has role=status for info alerts", () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this seems to be similar to L82-87 below. I know this tests when there is no status attribute present whereas the one below uses an explicit status=info. Is there a way to rephrase the description so it's clear that this is the default? I'd be ok with consolidating this in the test on L32 if that makes more sense.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

Drop the redundant 'private' modifier from the '#role' getter in the UsaAlert component. Combining the TS 'private' keyword with an ECMAScript private identifier ('#') is invalid and caused a syntax/compile error, so this change makes the accessor valid.
Rename the private field in custom-elements.json from `_role` to `#role` to match private field syntax. In usa-alert.css add a new `--usa-alert-emergency-text-color` variable (defaulting to #fff) and replace hardcoded `#fff` uses in emergency alert styles (body text, slotted headline, and close-icon background) so the emergency text color can be themed via CSS variables.
Merge the default info status and role assertion into a single test and remove the duplicate ARIA role test for info in src/components/usa-alert/usa-alert.spec.ts. This reduces redundancy and simplifies the test suite by combining related assertions into one spec.
Rename the alert close event from "close" to "usa-alert-close" to avoid generic event name collisions. Update the @fires JSDoc, the dispatched CustomEvent in UsaAlert, and the unit test to listen for "usa-alert-close".

@ethangardner ethangardner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @ericsorenson . LGTM!

@ethangardner ethangardner merged commit d67e894 into uswds:develop Jun 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants