The Basics

What it is

The Skeleton component is a visual placeholder for content that isn't yet known, but will appear once communication with a server is complete. It should reflect the general structure of what the actual content will be, but in abstract form (think a designer's sketch).

Thanks to the following people for this community contribution! This component is now maintained by Forge

  • Designers: Laura Materna & Len Ning
  • Developer: Mike Wessler

How it works

  • The user takes some action that will cause data to load from a server
  • A set of skeleton components appears mimicking in low-fidelity what the eventual UI structure will look like when the content arrives
  • The skeleton component shimmers to indicate that something is happening
  • When the data arrives, the skeleton components are replaced with the actual data content

When to use

  • To indicate that something is loading, where the thing that is loading has a known structure, even if the exact content isn't known yet
  • To give the user a sense of what is about to appear

When not to use

  • For data whose display doesn’t have a specific structure, or may have multiple different structures
  • For large solid areas like a single large image
  • For entire applications or pages
  • For indicating that a load failed
  • When the content you are loading is miniscule, like a number that has yet to be calculated. For something that small, opt for a text treatment such as “calculating x…”
  • Do not combine a Skeleton and a Loader in the same space (Use one or the other)

What to use instead

Loader

Use Loader instead when there is no placeholder structure, or there could be more than one type of structure that appears. 

How to use

  • Use an appropriate Skeleton type to replace each piece of unknown content: type=“text” for lines of text, type=“box” or “circle” for smaller images as appropriate
  • Use the same container structure and layout that you would have used to render the final content
  • Select a width and minWidth for text components that mimic the expected range of lengths of data
  • Don’t attempt to mimic wrapped text if wrapping is accidental and occasional
  • If you don’t know the number of items that will load, use two
  • If you do know the total number of items, but don’t know their content, use a skeleton for each item
  • Do include any layout-specific decorations (like horizontal rules, borders, margins, etc) that are consistent no matter what the data is
  • Skeletons are NEVER interactive

Style

Design details

  • Available placeholder elements:
    • “text” for lines of text (will show up as gray bars)
    • “box” or “circle” for smaller images as appropriate 
  • Margin and padding:
    • Skeleton components don’t have any padding and rely on their containers for layout and padding
    • Each skeleton should only represent a line of text or an image
    • Do not attempt to style a skeleton component
    • Style the container that holds it instead, just as you would style the container that holds your text or image
  • Font size:
    •  N/A, technically. Will automatically approximate Forge’s body copy style, although slightly shorter than 16px tall to represent the visual weight of a line of text without all letters necessarily having ascenders and descenders
    • The height of a text Skeleton automatically scales with the current font size. To change the height of a "text" skeleton, change the font size in its container
  • Colors:
    • The gray bars/shapes will be Forge’s “color-background, dark”, or #DBDBDB, with a white oblique linear gradient shimmer 

Placement and hierarchy

Skeletons should be included instead of text and images in a container structure identical to what you would render if you actually had the final data.

Content

Skeletons will never themselves contain other content.  However, it is very often the case that you will use several individual skeletons together to build a visual structure.

Demos

Skeleton Text Share

Skeleton Circle Share

Coding

Developer tips

Using isFetching

Resist the temptation to set isFetching={true}{{color:#172b4d}} Make isFetching reflect the actual state of the loader. Having the skeleton only shimmer when data is actively loading gives you a good debugging tool to know when your application is actually fetching data, and when it has failed to properly fetch the data.

If the fetch fails, do not leave the skeletons on screen. Replace them with an error message.

Every text skeleton component should have a unique seed. This seed should be stable between renderings of the same skeleton component.

Repository

Implementation links

Skeleton directory in Bitbucket

Implementation details

It is strongly recommended to familiarize yourself with the Forge source code. While this documentation is a best effort to document the intent and usage of a component, sometimes some features only become clear when looking at the source code. Also, looking at Forge's source code may help identify and fix bugs in either your application or Forge itself.

Storybook files

Forge maintains at least one storybook file per component. While the primary audience for these files is typically the Forge team, these storybook files may cover usages of the component not covered by a demo. The storybook for the latest version of forge can be found at go/forge-storybook.

Testing library

Forge strongly encourages using testing-library to write tests for your application.

"The more your tests resemble the way your software is used, the more confidence they can give you."

If you're having trouble testing a Forge component using testing-library, it would be a good idea to see how Forge tests its own components. For the most part, Forge tries to use screen.getByRole as much as it can, as that API provides the best feedback on a11y compliance. Forge discourages the use of document.querySelector and screen.getByTestId as both APIs encourage using implementation details to test your component, and discourage adding roles to your component.

With that being said, many of Forge's components were not built with accessability in mind. These components do break the recommendations listed above.

Import statements

In Nimbus applications

athenaOne serves the Forge bundle independently from your application's bundle. Importing Forge components directly from '@athena/forge' takes advantage of this feature.

import { Skeleton } from '@athena/forge'

In standalone applications

Importing components using the exact path to the module takes advantage of webpack's tree shaking feature. Webpack will include only that module and its dependencies.

import Skeleton from '@athena/forge/Skeleton';

To use this import guidance, Typescript applications must use typescript >= 4.7.3, and should add this setting to their tsconfig.json file:

{
"compilerOptions": {
"moduleResolution": "Node16",
}
}

If this setting doesn't work for your application, use this import statement instead:

import Skeleton from '@athena/forge/dist/Skeleton';

Props