GrLoading

  • core
  • feedback
  • API · 11 props

Indicates that a section or action is currently loading.

import { GrLoading } from '@feugene/granularity/components/GrLoading'

Overview

When to take it

  • the content is already there and is being updated — a table is being re-requested, a form is being submitted: the old data is visible but unavailable;
  • the wait is short — delay does not show the spinner at all if the answer arrived quickly;
  • the whole screen has to be blocked — fullscreen for the duration of an operation that must not be interrupted;
  • the overlay is needed imperatively — the v-loading directive instead of a component in the markup.

When to take something else

NeedTake
There is no content at all yetGrSkeleton
The share of what is done is knownGrProgressBar / GrProgressCircle
The wait is inside a buttonGrButton with loading
Uploading a fileGrFileUpload

Playground controls come from the API, not from hand

Stage

This component lives above the screen, not inside a canvas: a screen opens it, and the playground has nothing to open it from. The examples below show it at work.

0 changed· state is in the URL

Examples 3 each with a live demo and its code

  • A table recalculated in place

    The layer lies on the nearest positioned ancestor, so relative on the block decides how far it reaches: the table waits, the period switch does not, and the old figures stay in view under the scrim. delay keeps a fast answer — the week comes back in 120 ms — from flashing a spinner.

    Revenue by line
    Subscriptions€76,900+6%
    Add-ons€9,450+3%
    Services€14,200+11%
  • A card blocked while it saves

    v-loading covers the card it sits on and also blocks it: aria-busy on the card and inert on its children, so Tab does not walk into fields nobody can see. Options come as an object — text, delay — and focus returns where it was when the layer closes.

    Payout accountWhere card payments are paid out every Monday.
    13 / 18
  • Switching a workspace

    Switching reloads everything the app holds, so the whole screen waits: fullscreen covers it. The default slot replaces the spinner with what the wait is about — which workspace is opening.

    Workspaces

API generated from the package · cannot drift

Props

PropTypedefaultDescription
textstring | undefinedundefinedThe caption under the spinner. An empty string removes it entirely. By default — from the locale.
zIndexVarstring | undefinedundefinedThe name of the CSS variable of the layer — an escape hatch past `--gr-z-loading`.
spinnerComponent | undefinedundefinedA spinner component of your own instead of the default icon.
spinnerClassstring | undefinedundefinedExtra classes for the wrapper of the spinner.
spinnerSizenumber | "xs" | "sm" | "md" | "lg" | undefined28The size of the spinner: the scale of the package or an arbitrary one in pixels.
spinnerToneGrIconTone | undefined"neutral"The tone of the spinner from the palette.
animatedboolean | undefinedtrueThe rotation of the spinner. On by default.
backgroundstring | undefinedundefinedA `background-color` of your own. If it is set, the default `--gr-overlay-bg` scrim is removed.
fullscreenboolean | undefinedfalseCover the whole screen (`position: fixed`) instead of the nearest positioned ancestor.
delaynumber | undefined0The delay before showing, in milliseconds: a short load does not flash an overlay.
customClassstring | undefinedundefinedExtra classes for the root of the overlay.

Slots

SlotTypeDescription
defaultanyThe content of the panel as a whole instead of the spinner with a caption.

Events

EventSignatureDescription
show(event: "show"): void—

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrLoadingProps, GrLoadingEmits } from '@feugene/granularity/components/GrLoading'

Filedist/types/components/GrLoading/config.d.ts

grLoadingConfigconst
const grLoadingConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrLoading">;

Filedist/types/components/GrLoading/safelist.d.ts

grLoadingSafelistconst
const grLoadingSafelist: string[];

Filedist/types/components/GrLoading/GrLoading.vue.d.ts

GrLoadingPropsinterface
interface GrLoadingProps {
    text?: string;
    spinner?: Component;
    spinnerClass?: string;
    spinnerSize?: GrIconSize | number;
    spinnerTone?: GrIconTone;
    animated?: boolean;
    background?: string;
    fullscreen?: boolean;
    zIndexVar?: string;
    delay?: number;
    customClass?: string;
}
GrLoadingEmitsinterface
interface GrLoadingEmits {
    (e: 'show'): void;
}

Usage from the library prose

Two modes

By default the overlay lies on the nearest positioned ancestor — that is the loading of a piece of the page. fullscreen covers the whole screen: that is loading at the level of the application.

The directive chooses the mode itself: target = document.body → fullscreen, otherwise inline. To a container with position: static it temporarily gives relative, and to a container with rounding overflow: hidden, so that the blurred backdrop does not come out of the rounded shape.

The delay

delay (ms) postpones the showing. A request that answered faster than the delay does not show the overlay at all — flickering is more irritating than the absence of an indicator.

vue
<GrLoading v-if="pending" :delay="200" />

The countdown starts from the mounting, that is, from the moment the loading started. While it is running, the content is not blocked: otherwise a fast request would silently freeze the form.

Accessibility

The overlay is a role="status" with aria-live="polite": the caption is read by a screen reader at the moment it appears, without interrupting the user.

Covering it visually is not enough: without blocking, the tab goes into a form that is no longer visible, and a screen reader reads it as an ordinary one. At the moment of showing, the directive therefore declares the container aria-busy="true" and gives inert to its other children — the subtree as a whole drops out of the tab order, out of pointer events and out of the accessibility tree. On closing, inert is removed only from what it set itself, and the focus returns to where it was if the user has not moved it themselves.

The declarative <GrLoading> does not dispose of its neighbours — it does not know them. If blocking is needed on the same container, take the directive.

There is deliberately no focus trap inside the overlay: there is nothing to trap in it, and the inert on the neighbours covers the task as a whole.

The layer

The full-screen mode sits on the --gr-z-loading token (1150) — above the modals, because it blocks the whole application, and below the toasts, so as not to hide a notification about a background error. The inline mode has nothing to do with the scale: z-10 is the order inside its own container.

zIndexVar replaces the layer variable with one of your own — an escape hatch of the same kind as in useFloating. The component does not accept a raw number: see docs/z-index.md.

The scrim

The dimming under the panel is the --gr-overlay-bg role of the theme, the same one GrModal and GrDrawer use: a loading overlay has to look like the other modal layers and to change density together with the theme.

The background prop sets a background-color of your own and cancels the scrim entirely — it is needed when the overlay lies on an already dimmed surface.

The spinner and the content

spinnerSize and spinnerTone go through GrIcon — the scale of the icons and the text tokens rather than pixels in the markup. spinner replaces the icon itself, and animated switches the rotation off.

The slot replaces the content of the panel as a whole — progress with percentages, a button to cancel a long operation:

vue
<GrLoading>
  <GrProgressBar :value="percent" class="w-48" />
  <GrButton size="xs" variant="outline" @click="abort">Cancel</GrButton>
</GrLoading>

The caption comes from the locale by default (gr.loading.defaultText); text sets one of your own, and an empty string removes it entirely.

The directive
ts
const controller = createLoading({ target: '#report', text: 'Computing the report', delay: 200 })
controller.setText('Almost done')
controller.close()

v-loading accepts either a boolean or an object of options (the same as the component’s, plus target and fullscreen). Changing target or the mode recreates the overlay, everything else is updated in place.