import { LitElement, html, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; // Import USWDS core styles // Import USWDS web components import '@uswds-wc/feedback'; import '@uswds-wc/navigation'; import '@uswds-wc/layout'; import '@uswds-wc/actions'; /** * USA Error Template * * Error page template for 404, 500, and other error states. * Provides a minimal, focused layout for error messaging. * * **Template Structure (from USWDS):** * - Skip navigation + Banner + Header (optional) * - Error content with code, title, message * - Action buttons (home link, etc.) * - Footer + Identifier (optional) * * @element usa-error-template * * @slot error-content - Custom error content * @slot header - Site header * @slot footer - Site footer * * @fires {CustomEvent} template-ready - Fired when template initializes * * @example 404 Error * ```html * * ``` * * @example 500 Error * ```html * * ``` * * @uswds-template https://designsystem.digital.gov/templates/error-page/ */ @customElement('usa-error-template') export class USAErrorTemplate extends LitElement { // Use Light DOM for USWDS compatibility protected override createRenderRoot() { return this; } /** * Page language attribute */ @property({ type: String }) override lang = 'en'; /** * Whether to show the government banner */ @property({ type: Boolean, attribute: 'show-banner' }) showBanner = true; /** * Whether to show the identifier at the bottom */ @property({ type: Boolean, attribute: 'show-identifier' }) showIdentifier = false; /** * Error code (e.g., "404", "500") */ @property({ type: String, attribute: 'error-code' }) errorCode = '404'; /** * Error title/heading */ @property({ type: String, attribute: 'error-title' }) errorTitle = 'Page not found'; /** * Error description message */ @property({ type: String, attribute: 'error-message' }) errorMessage = "We couldn't find the page you're looking for."; /** * Home page URL */ @property({ type: String, attribute: 'home-url' }) homeUrl = '/'; /** * Home button text */ @property({ type: String, attribute: 'home-text' }) homeText = 'Return to home'; /** * Whether to show a contact link */ @property({ type: Boolean, attribute: 'show-contact' }) showContact = false; /** * Contact page URL */ @property({ type: String, attribute: 'contact-url' }) contactUrl = '/contact'; /** * Contact link text */ @property({ type: String, attribute: 'contact-text' }) contactText = 'Contact us'; /** * Additional help text */ @property({ type: String, attribute: 'help-text' }) helpText = ''; override connectedCallback() { super.connectedCallback(); if (this.lang) { this.setAttribute('lang', this.lang); } } override firstUpdated(changedProperties: Map) { super.firstUpdated(changedProperties); this.dispatchEvent( new CustomEvent('template-ready', { detail: { template: 'error', errorCode: this.errorCode }, bubbles: true, composed: true, }) ); } /** * Check if a slot has content */ private hasSlotContent(slotName: string): boolean { return this.querySelector(`[slot="${slotName}"]`) !== null; } /** * Render the error content */ private renderErrorContent() { if (this.hasSlotContent('error-content')) { return html``; } return html` ${this.errorCode} ${this.errorTitle} ${this.errorMessage} ${this.helpText ? html`${this.helpText}` : nothing} ${this.homeText} ${this.showContact ? html` ${this.contactText} ` : nothing} `; } override render() { return html` ${this.showBanner ? html`` : nothing} ${this.renderErrorContent()} ${this.showIdentifier ? html`` : nothing} `; } /** * Public API: Set error state */ setError(code: string, title: string, message: string): void { this.errorCode = code; this.errorTitle = title; this.errorMessage = message; } }
${this.errorMessage}
${this.helpText}