On this page
Import
import {
Avatar,
Button,
Card,
Column,
Grid,
List,
NavigationDrawer,
NavigationRail,
Page,
PageHeader,
placeholders,
Progress,
Row,
Scaffold,
SegmentedControl,
Spinner,
Text,
TextField,
TopBar,
} from '@envsemble/envui';
Usage
Render the page's own components with skeleton while the data loads, over placeholders<Item>(count).
items ?? placeholders<Item>(3)gives them stand-ins to map over.- A text prop given as
undefinedrenders as a bar of its text style's length. - Wait 200 ms before showing the skeleton, through a timer of the application's own, and keep its space meanwhile, as the SkeletonList example does.
import { useEffect, useState } from 'react';
import { Card, Grid, placeholders } from '@envsemble/envui';
interface Project {
id: string;
name: string;
updated: string;
summary: string;
}
const projects: Project[] = [
{ id: 'brief', name: 'Launch brief', updated: 'Today', summary: 'Goals and dates.' },
{ id: 'budget', name: 'Budget', updated: 'Monday', summary: 'Spend by quarter.' },
{ id: 'survey', name: 'Survey', updated: 'May 4', summary: 'Answers so far.' },
];
export default function CardsLoading() {
const [items, setItems] = useState<Project[]>();
useEffect(() => {
// The projects load from a server as the page opens; a timer stands in for it here.
const timer = window.setTimeout(() => setItems(projects), 1600);
return () => window.clearTimeout(timer);
}, []);
return (
// The same cards, over placeholder items while the projects load, so they land in place.
<Grid columns={{ min: '12rem' }} gap={4} style={{ inlineSize: '100%' }}>
{(items ?? placeholders<Project>(3)).map((project) => (
<Card
key={project.id}
skeleton={!items}
title={project.name}
meta={project.updated}
description={project.summary}
/>
))}
</Grid>
);
}A skeleton List, as the SkeletonList example shows it:
<List.Root aria-label="Design projects" skeleton={!items}>
{(items ?? placeholders<Project>(3)).map((project) => (
<List.Item key={project.id} label={project.name} description={project.owner} />
))}
</List.Root>
Indicators
| What is loading | Code |
|---|---|
| Content of a known shape | Its components with skeleton, over placeholders() |
| Content of no known shape | <Spinner label="Searching documents" showLabel /> |
| An update to what is shown | <Spinner size="sm" label="…" /> beside it, aria-busy on the region |
| The result of an action | pending on the Button that started it |
| Work whose end can be measured | <Progress label={file.name} value={percent} /> |
| A whole page | loading on the TopBar, named by loadingLabel |
A TopBar with loading shows a whole page on its way; the page on screen stays current until the next arrives, as the PageProgress example does.
<TopBar title="Northwind" loading={next !== null} loadingLabel="Loading Projects" />
Media
Give media an aspect ratio, with width and height or the CSS aspect-ratio, so it holds its place. Dev mode's envui.layout.skeletonShift() reports what moves as content replaces its skeleton.
Composition
A skeleton's root takes aria-busy and its content is inert, in env-skeleton-content, which has no box of its own. Bars have small rounded corners, over missing text and text that is there alike, and shapes keep their component's corners, such as a chip's, an avatar's round one or a card's. A skeleton List holds no wrapper, and beside it a visually hidden status says what is loading.
Accessibility
You provide
- A label that names what is loading, on every indicator.
- A status message where the wait is long, since the skeleton's content isn't read.
- The delay before a skeleton or spinner appears.
envUI provides
- One busy region per skeleton,
aria-busyon its root, whose content is inert: Tab passes over it and assistive technology skips it until it loads. - A visually hidden status beside a skeleton list, which holds no wrapper, that says what is loading.
- A skeleton popup, sheet or supporting pane that keeps focus, so Esc still closes it.
- A Spinner with
showLabel, a busy status with its label; a pending Button that keeps its size and ignores presses; Progress with its value.
Changelog
- 1.0.0
Skeletons are the components themselves, with
skeletonandplaceholders(), in place of hand-built Skeleton placeholders. The page is on the figure template.- 1.0.0-prerelease.6
Only the bars and shapes pulse: the skeleton's own surfaces, such as cards and rows, keep a faint tint that barely moves, so they keep their edges against the page and the layout stands still.
A skeleton's breath lasts seven and a half times
--env-motion-duration-slow, as long as before, with--env-motion-ease, so durations of0sstop it.