@stihl-design-system/components
Version:
Welcome to the STIHL Design System react component library.
112 lines (111 loc) • 6.02 kB
TypeScript
export declare const ARIA_LIVE_REGION_ALERT_ID = "ds-alert-live-region";
export declare const ARIA_LIVE_REGION_STATUS_ID = "ds-status-live-region";
export declare const getAriaAlertLiveRegionElement: () => HTMLElement | null | undefined;
export declare const getAriaStatusLiveRegionElement: () => HTMLElement | null | undefined;
export declare const validateMultipleDSAriaLiveRegionsOnPage: () => void;
/**
* Ensures the two DS live regions exist exactly once on the page, directly under <body>.
* - If both exist: no-op.
* - If none exist: injects them under document.body.
* - If only one exists: throws (broken page state).
* - If duplicates exist: throws (existing validation).
*
* ## Background: Micro-Frontend / AEM Web Component Support
*
* This function was introduced to support complex hosting environments where React components
* are compiled into Web Components and embedded via systems like AEM (Adobe Experience Manager).
*
* In these setups:
* - React components become self-contained widgets, not full SPAs
* - Multiple independent micro-frontends may exist on a single page
* - Each micro-frontend has its own encapsulated React/VDOM render process
* - Web Components render sequentially based on DOM registration order
* - Shadow DOM is typically disabled, so elements are detectable across components
*
* ### The Problem
*
* The standard `<DSAriaLiveRegions />` placement guideline ("as close to `<body>` as possible")
* is impossible to follow when components can only be placed at the root of each Web Component
* wrapper, not the actual document body. This creates two failure modes:
*
* 1. **Missing live regions**: If no static HTML snippet exists and the component isn't rendered,
* dependent components (DSChipGroup, DSToast, etc.) throw errors.
* 2. **Duplicate live regions**: Multiple Web Components each rendering their own live regions
* creates invalid HTML (duplicate IDs) and unpredictable screen reader behavior.
*
* ### Why Placement Matters for Accessibility
*
* A mode that allows `DSAriaLiveRegions` to render inside arbitrary micro-frontend wrappers
* is risky for accessibility. Those wrappers can later become `aria-hidden`, `inert`, `hidden`,
* or be moved under a `<dialog>`, and at that point announcements can become intermittent or
* stop entirely across screen readers. This is exactly why the requirement to place live regions
* as close to `<body>` as possible exists.
*
* Additionally, treating duplicates as "warn and keep going" is not harmless. As soon as
* duplicate IDs exist, `getElementById()` effectively selects one instance based on document
* order. If that instance lives in a subtree that later becomes hidden, announcements will
* silently break even though "a live region exists somewhere" on the page.
*
* ### The Solution: `microFrontendMode`
*
* Instead of rendering where mounted (which could be inside problematic wrappers), this mode:
*
* 1. **Renders nothing locally** - The component returns `null`
* 2. **Injects directly under `document.body`** - Guarantees correct placement regardless of
* where the component is used in the React tree
* 3. **Is idempotent** - Whichever MFE instance runs first installs the body-level regions,
* all subsequent calls no-op safely
* 4. **Maintains strict validation** - True duplicates remain a hard error
*
* This provides rollout safety for MFEs and templates without compromising accessibility
* correctness. The exact render order doesn't matter as long as the logic is idempotent
* and synchronous.
*
* ### Recommended Integration Pattern
*
* 1. **Ideal**: Add static HTML live regions to the root `index.html` or AEM page template
* 2. **Fallback**: Use `<DSAriaLiveRegions microFrontendMode />` at the root of each
* micro-frontend as a self-healing fallback
*
* With this pattern:
* - If static HTML exists, all `microFrontendMode` instances no-op
* - If no static HTML exists, the first MFE to render installs the regions
* - Subsequent MFEs detect the existing regions and no-op
* - No duplicates, no missing regions, no placement issues
*/
export declare const ensureAriaLiveRegions: (srOnlyClassName: string) => void;
/**
* Gets the alert live region element if present on the page and returns it.
* Throws an error with the component name if the element is not found.
*
* @param componentName - The component name for error messages if live region is missing
* @returns The alert live region HTMLElement
* @throws Error if the alert live region element is not found
*/
export declare const getAlertLiveRegionOrThrow: (componentName: string) => HTMLElement;
/**
* Gets the status live region element if present on the page and returns it.
* Throws an error with the component name if the element is not found.
*
* @param componentName - The component name for error messages if live region is missing
* @returns The status live region HTMLElement
* @throws Error if the status live region element is not found
*/
export declare const getStatusLiveRegionOrThrow: (componentName: string) => HTMLElement;
/**
* Sets a message to the alert live region (role="alert", aria-live="assertive").
* Use for urgent announcements that should interrupt the user (e.g., warnings, errors).
*
* @param message - The message to announce
* @param componentName - The component name for error messages if live region is missing
*/
export declare const setAriaAlertMessage: (message: string, componentName: string) => void;
/**
* Sets a message to the status live region (role="status", aria-live="polite").
* Use for non-urgent announcements that should wait for user pause (e.g., status updates, success messages).
* The message is automatically cleared after a short delay.
*
* @param message - The message to announce
* @param componentName - The component name for error messages if live region is missing
*/
export declare const setAriaStatusMessage: (message: string, componentName: string) => void;