Loading Overlay
Covers the page, or one panel, while it is busy. A backdrop blocks the pointer and the keyboard, and a panel shows a spinner and a label.
fandry-loading-overlay
Example
Link to section ExampleCustom colors and parts
Link to section Custom colors and partsThe same component with a few --fd-* tokens set around it and its parts restyled. The code is under Parts below.
Props
Link to section Props| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Whether the page or panel is busy. Bind it rather than wrapping the overlay in lwc:if: it stays in the page so its label is announced, and so it can fade out. |
contained | boolean | false | Covers the nearest positioned ancestor (position: relative) instead of the viewport, and blocks keys inside its parent element. Put the overlay directly inside the panel it covers. |
label | string | '' | The text under the spinner, e.g. "Saving record…". Wins over messages.label. To restyle it (bold, color), use ::part(label), not the slot. |
messages | { label } | {} | Replaces the default label ("Loading") for every instance, e.g. to translate it. |
default slot | slot | spinner and label | The whole content of the panel, spinner included: your own indicator, markup in the text, a progress bar, a Cancel button. Its text (minus anything aria-hidden) is what is announced, or the label when it has none. |
Parts
Link to section PartsStyle the elements inside the component from your own CSS with ::part(). Every fandry component renders in native shadow DOM, so parts work on LWR and Salesforce alike. For colors, spacing, radius and type across many components, set the --fd-* tokens instead. The code for the "Custom colors and parts" example above:
| Part | Description |
|---|---|
backdrop | The dimmed layer behind a modal panel, or a loading overlay's cover. |
panel | The surface that opens: a dropdown's list, a popover, a tooltip, a dialog, a loading overlay's box. |
spinner | A loading overlay's spinner (a fandry-spinner's base). |
label | The label. |
<!-- template -->
<div class="brand row">
<fandry-button onclick={handleSave}>Save record</fandry-button>
<fandry-loading-overlay open={saving} label="Saving record…"></fandry-loading-overlay>
</div>
/* css */
/* Scoped to this demo. Set the same --fd-* tokens on :root to theme
every fandry-* component on the site. */
.brand {
--fd-primary: 160 84% 26%;
--fd-backdrop: 160 40% 10%;
--fd-backdrop-opacity: 0.4;
--fd-loading-overlay-delay: 0ms;
}
/* The spinner's track is the border color: a tint of the primary here. */
fandry-loading-overlay {
--fd-border: 160 84% 26% / 0.2;
}
fandry-loading-overlay::part(backdrop) {
backdrop-filter: blur(4px);
}
fandry-loading-overlay::part(panel) {
flex-direction: row;
padding: 1rem 1.5rem;
border-radius: 999px;
}
/* The ring is 1em square: its font-size is its size. */
fandry-loading-overlay::part(spinner) {
font-size: 1.25rem;
}
fandry-loading-overlay::part(label) {
font-weight: 600;
}
Code
Link to section Code<fandry-button onclick={handleConvert}>Convert lead</fandry-button>
<fandry-loading-overlay open={converting} label="Converting lead…"></fandry-loading-overlay>
Not a dialog
It blocks the pointer and the keyboard the moment it opens, but takes no focus and has nothing to close: the page must always set open back to false, on errors and timeouts too. It only shows after --fd-loading-overlay-delay (150ms), so an operation that finishes sooner never flashes an overlay.
Focus stays where it was. Over the page, keys do nothing until it closes (typing, Tab, Enter, Space, arrow-key scrolling, paste, the page's own shortcuts); the browser's shortcuts and wheel or touch scrolling still work. A contained overlay blocks keys inside the element it is in and lets Tab leave it. Keys still reach content in the overlay once it has focus: focus a Cancel button you put there yourself.
It blocks hardware keys, not every input method: text from an IME or an on-screen keyboard, and a screen reader's own click, can still reach what is behind it. Guard the actions that must not run twice.
It keeps a polite status region in the page at all times and puts the content's text into it once the overlay shows, so a screen reader announces it. A label edited in place while it is open is not announced again. Mark a slotted spinner aria-hidden="true": the region already says what is happening.
Example: Your own content
Link to section Example: Your own contentCode
<!-- template: the default slot replaces the spinner and label, so bring your own indicator -->
<fandry-button onclick={handleImport}>Import contacts</fandry-button>
<fandry-loading-overlay open={importing}>
<fandry-progress value={percent} label="Import progress"></fandry-progress>
<fandry-text>Importing contacts…</fandry-text>
<fandry-button variant="secondary" onclick={handleCancel}>Cancel</fandry-button>
</fandry-loading-overlay>
// js: keys are blocked outside the overlay, so put focus on the action
async handleImport() {
this.importing = true;
await Promise.resolve();
this.template.querySelector('fandry-loading-overlay fandry-button').focus();
}
Example: Over one panel
Link to section Example: Over one panelCode
<!-- template: the overlay goes directly inside the panel it covers -->
<div class="panel">
<fandry-card>
...
<fandry-button variant="secondary" onclick={handleRefresh}>Refresh</fandry-button>
</fandry-card>
<fandry-loading-overlay open={refreshing} contained label="Refreshing…"></fandry-loading-overlay>
</div>
/* css */
.panel {
position: relative;
}
/* Follow the panel's corners */
.panel fandry-loading-overlay::part(backdrop) {
border-radius: var(--fd-radius-lg, 0.5rem);
}