GrSkeleton

  • core
  • feedback
  • API · 5 props

A temporary interface skeleton while the content is still loading.

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

Overview

When to take it

  • there is no content yet — the placeholder repeats its shape, and the layout does not jump when the data appears;
  • the first render is loading — a card, a list, a table: the user sees the structure before the data;
  • there are several placeholders — count repeats a row the required number of times;
  • the shape is non-standard — variant gives a line, a block or a circle for an avatar.

When to take something else

NeedTake
The content is already there but is being updatedGrLoading
The share of what is done is knownGrProgressBar
The loading has finished and there is no dataGrEmptyState
The wait is inside a buttonGrButton with loading

A skeleton is honest only when it repeats the future layout. Three identical lines instead of a table of six columns deceive twice: first they promise the wrong thing, then they give a jump of the layout.

Variants every tone and view, rendered by the real component

variant

  • circle
  • text
  • rect

Playground controls come from the API, not from hand

Stage

Loading…

0 changed· state is in the URL

Examples 3 each with a live demo and its code

  • A customer list that does not jump

    The placeholder repeats the real row — a circle for the avatar, a short and a long line for the name and the address, a pill for the plan — and every row keeps one height in both states, so the data replaces it without a jump. The placeholders are aria-hidden: the list says aria-busy, and a live line says what has loaded.

    Customers
    Loading customers
  • Help centre cards

    A cover is a block: variant="rect" with the radius of the card through rounded, at the height of the picture. The lead is a paragraph — :count="2" cuts the last line short, so the block reads as text rather than as a stack of bars. Under prefers-reduced-motion the pulse stops altogether.

    Help centre · Billing
    Loading articles
  • Figures being recalculated

    Shape comes from variant, size from the figure the placeholder stands for: a short line for the label, a block of the number’s height and a pill for the change. Each slot keeps the line height of its text, so the tiles do not move when the numbers arrive.

    October so far
    Recalculating

API generated from the package · cannot drift

Props

PropTypedefaultDescription
variant"circle" | "text" | "rect" | undefined"text"The shape rather than the dimensions: `width`/`height` stay with the consumer, because the height of a placeholder is dictated by the neighbouring content rather than by the scale of the component.
countnumber | undefined1How many placeholders in a row. More than one and they go into a common wrapper.
widthstring | undefinedundefinedThe width, a CSS length. `100%` by default, `2.5rem` for a circle; without it the last of several text lines is shorter than the rest.
heightstring | undefinedundefinedThe height, a CSS length. `12px` by default; for a circle it equals the width.
roundedstring | undefinedundefinedThe radius pointwise. Unset — it is taken from `variant`.

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 { GrSkeletonProps, GrSkeletonVariant } from '@feugene/granularity/components/GrSkeleton'

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

GrSkeletonPropsinterface
interface GrSkeletonProps {
    variant?: GrSkeletonVariant;
    height?: string;
    width?: string;
    rounded?: string;
    count?: number;
}

Filedist/types/components/GrSkeleton/grSkeletonStyles.d.ts

GrSkeletonVarianttype
type GrSkeletonVariant = typeof GR_SKELETON_VARIANTS[number];

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

grSkeletonConfigconst
const grSkeletonConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrSkeleton">;

Usage from the library prose

The shape by variant, the sizes by the consumer
variantThe radiusWhat for
text (the default)--gr-radius-fulla line of text
rect--gr-radius-mda block: a card, a button, a picture
circle--gr-radius-fullan avatar, an icon

The variant sets the shape only. width and height stay with the consumer: the height of a placeholder is dictated by the content it stands in for rather than by the scale of the component. The default is 100% × 12px, and 2.5rem for a circle.

The circle is the only shape where the sizes are linked: height repeats width until it is set explicitly. Otherwise one side that was set would turn the circle into an oval.

rounded overrides the radius of the variant pointwise — for instance when the card of an application has a large radius of its own.

A block of several lines
vue
<GrSkeleton :count="3" />

count draws N placeholders in a shared grid. In text the last line is shorter (60%): the block reads as a paragraph rather than as a list of identical bars. In rect and circle all of the elements are identical — they have no “unfinished line”. A width that has been set is stronger than that rule.

A single placeholder (the default count) renders without a wrapper — with exactly the same node as before.

Accessibility

The placeholder as a whole is marked aria-hidden="true" — both a single one and the wrapper of a group. There is no point announcing an “empty” node; the fact that loading is going on is reported by the container: aria-busy="true" and a live region with text. That is how it is done inside the package — see the loading state of GrStatistic and GrTable.

The movement

The pulsing is a @keyframes of the component’s own, and under prefers-reduced-motion: reduce it is switched off entirely (animation: none) rather than sped up: an endless animation is movement the user has asked not to be shown. The whole contract of movement — ../motion.md.

The <style> of the component is deliberately not scoped: the rule hangs on the [data-gr-skeleton] attribute, so it reaches placeholders rendered inside other components of the package as well.