| Version | Tag | Status | API |
|---|---|---|---|
| v1 | <oc-skeleton-v1> |
Stable, allowed for generation | SkeletonV1 |
Overview (v1)
Source: ./src/components/skeleton/v1/Overview.mdx
Skeleton
The skeleton component provides a placeholder for content while it's loading. It has attributes for setting the shape, width, height, border radius, and visibility state. The skeleton can be a rectangle or a circle, and its dimensions and border radius can be customized. The visibility state can be toggled, with changes applied using a fade-in/out transition.
Default variation
Story Default:
<oc-skeleton-v1></oc-skeleton-v1>
Configuration
The skeleton component offers a variety of styling variants such as circle and rectangle.
This component is configurable, allowing you to tailor its features and appearance to your specific needs.
To explore all the available options and adjust the component, use the component configurator and see the changes affect the component in real-time.
Usage guidelines
Before integrating the skeleton component into your project, make sure you have correctly installed the OTTO components package. Look through the variations page for examples of possible component variations. Here, you can discover both common and specific variations that address different use cases.
Accessibility
The skeleton component comes with a set of built-in accessibility features to ensure a seamless experience for all users.
Configuration (v1)
Source: ./src/components/skeleton/v1/Configuration.mdx
Skeleton configuration
Configure the skeleton component with the controls below and see the changes live in the preview canvas. Click the Show code button within the preview canvas to see the source code for the current component configuration.
Story Default:
<oc-skeleton-v1></oc-skeleton-v1>
Interactive configurator (Storybook controls); every option is listed in the API section of this file.
API v1
Source: ./src/components/skeleton/v1/SkeletonV1.API.g.mdx
Skeleton v1 API
API: <oc-skeleton-v1> (SkeletonV1)
The {Latest.default.name.toLowerCase()} component provides a placeholder for content while it's loading. It has attributes for setting the shape, width, height, border radius, and visibility state. The skeleton can be a rectangle or a circle, and its dimensions and border radius can be customized. The visibility state can be toggled, with changes applied using a fade-in/out transition.
Attributes / properties
| Attribute | Type | Default | Required | Description |
|---|---|---|---|---|
variant |
"rectangle" | "circle" |
"rectangle" |
no | Sets the main shape of the skeleton. |
hidden |
boolean |
false |
no | Toggles the state of the skeleton between visible and hidden. Set to true to hide the skeleton. State changes will be applied with a fade-in/out transition. |
width |
string |
undefined |
no | Sets the width of the skeleton. |
height |
string |
undefined |
no | Sets the height of the skeleton. This attribute only applies to variant="rectangle". |
border-radius |
string |
"8px" |
no | Sets the border radius of the rectangular skeleton. This attribute only applies to variant="rectangle". |
Events
| Event | Detail type | Description |
|---|---|---|
oc-property-change |
OcSkeletonV1Events["oc-property-change"] |
Whenever a property value changes, this event triggers. Use this event to track all property changes within the component. Refer to the Events documentation for more information. |
oc-mount |
{ component: string; } |
Fired when the component is mounted to the DOM. The event is fired when the onMount hook of the component is called by the runtime. |
oc-unmount |
{ component: string; } |
Fired when the component is unmounted from the DOM. The event is fired when the function returned by the onMount hook of the component is called by the runtime. |
Variations (v1)
Source: ./src/components/skeleton/v1/Variations.mdx
Variations
Listed below are the most common variations of the skeleton component as well as specific component variations for different use cases.
You can explore all available options using the component configurator, adjust the component to your needs, and see the changes live in a preview canvas.
Default
Story: components-skeleton-variations--default · tags: components
The default configuration uses variant=rectangle and an 8px border radius.
<oc-skeleton-v1></oc-skeleton-v1>
Rectangular skeleton without border radius
Story: components-skeleton-variations--rectangle-without-border-radius · tags: components
The rectangle variant without a border radius.
Args: variant=rectangle, border-radius=0px, width=400px, height=100px
<oc-skeleton-v1 variant="rectangle" border-radius="0px" width="400px" height="100px"></oc-skeleton-v1>
A circular skeleton
Story: components-skeleton-variations--circle · tags: components
The circle variant with a width of 200px.
Args: variant=circle, width=200px
<oc-skeleton-v1 variant="circle" width="200px"></oc-skeleton-v1>
Demo: collection of skeletons
Story: components-skeleton-variations--demo-skeleton-collection · tags: components
A collection of skeletons with a custom layout and a button to toggle their visibility using the hidden attribute.
<div class="collection">
<oc-skeleton-v1
style="display: block; margin-bottom: 12px;"
width="100%"
height="103px"
border-radius="0px"
></oc-skeleton-v1>
<div style="display: flex; justify-content: space-between; margin-bottom: 11px;">
<oc-skeleton-v1 border-radius="8px" width="114px" height="13px"></oc-skeleton-v1>
<oc-skeleton-v1 border-radius="8px" width="93px" height="13px"></oc-skeleton-v1>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 24px">
<oc-skeleton-v1 border-radius="8px" width="64px" height="13px"></oc-skeleton-v1>
<oc-skeleton-v1 border-radius="8px" width="143px" height="13px"></oc-skeleton-v1>
</div>
<div style="display: flex; gap: 4px; margin-bottom: 17px">
<oc-skeleton-v1
border-radius="16px"
width="81px"
height="48px"
style="flex-shrink: 0;"
></oc-skeleton-v1>
<oc-skeleton-v1
border-radius="16px"
width="81px"
height="48px"
style="flex-shrink: 0;"
></oc-skeleton-v1>
<oc-skeleton-v1
border-radius="16px"
width="81px"
height="48px"
style="flex-shrink: 0;"
></oc-skeleton-v1>
</div>
<div style="align-items: center; display: flex; gap: 17px">
<oc-skeleton-v1
style="display: inline-block; flex-shrink: 0;"
variant="circle"
width="64px"
></oc-skeleton-v1>
<div style="display: flex; flex-direction: column; gap: 8px; width: 100%;">
<oc-skeleton-v1 width="100%" height="8px"></oc-skeleton-v1>
<oc-skeleton-v1 width="100%" height="8px"></oc-skeleton-v1>
</div>
</div>
</div>
<p>
<oc-button-v1 variant="secondary" size="50" type="button" id="toggle-button"
>Toggle Skeleton
</oc-button-v1>
</p>
<script>
(() => {
const skeletons = document.querySelectorAll(".collection oc-skeleton-v1");
const button = document.getElementById("toggle-button");
let hidden = false;
button.addEventListener("click", () => {
hidden = !hidden;
for (let skeleton of skeletons) {
skeleton.hidden = hidden;
}
});
})();
</script>
<style>
.collection {
width: 220px;
height: 310px;
overflow-x: hidden;
display: flex;
flex-direction: column;
margin: 46px;
}
</style>
Story source (TypeScript, verbatim from Storybook)
{
name: "Demo: collection of skeletons",
parameters: {
controls: {
disabled: true
},
chromatic: {
hideInChromatic: true
}
},
argTypes: hideControlsBadge(Metadata),
render() {
return html`
<div class="collection">
<oc-skeleton-v1
style="display: block; margin-bottom: 12px;"
width="100%"
height="103px"
border-radius="0px"
></oc-skeleton-v1>
<div style="display: flex; justify-content: space-between; margin-bottom: 11px;">
<oc-skeleton-v1 border-radius="8px" width="114px" height="13px"></oc-skeleton-v1>
<oc-skeleton-v1 border-radius="8px" width="93px" height="13px"></oc-skeleton-v1>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 24px">
<oc-skeleton-v1 border-radius="8px" width="64px" height="13px"></oc-skeleton-v1>
<oc-skeleton-v1 border-radius="8px" width="143px" height="13px"></oc-skeleton-v1>
</div>
<div style="display: flex; gap: 4px; margin-bottom: 17px">
<oc-skeleton-v1
border-radius="16px"
width="81px"
height="48px"
style="flex-shrink: 0;"
></oc-skeleton-v1>
<oc-skeleton-v1
border-radius="16px"
width="81px"
height="48px"
style="flex-shrink: 0;"
></oc-skeleton-v1>
<oc-skeleton-v1
border-radius="16px"
width="81px"
height="48px"
style="flex-shrink: 0;"
></oc-skeleton-v1>
</div>
<div style="align-items: center; display: flex; gap: 17px">
<oc-skeleton-v1
style="display: inline-block; flex-shrink: 0;"
variant="circle"
width="64px"
></oc-skeleton-v1>
<div style="display: flex; flex-direction: column; gap: 8px; width: 100%;">
<oc-skeleton-v1 width="100%" height="8px"></oc-skeleton-v1>
<oc-skeleton-v1 width="100%" height="8px"></oc-skeleton-v1>
</div>
</div>
</div>
<p>
<oc-button-v1 variant="secondary" size="50" type="button" id="toggle-button"
>Toggle Skeleton
</oc-button-v1>
</p>
<script>
(() => {
const skeletons = document.querySelectorAll(".collection oc-skeleton-v1");
const button = document.getElementById("toggle-button");
let hidden = false;
button.addEventListener("click", () => {
hidden = !hidden;
for (let skeleton of skeletons) {
skeleton.hidden = hidden;
}
});
})();
</script>
<style>
.collection {
width: 220px;
height: 310px;
overflow-x: hidden;
display: flex;
flex-direction: column;
margin: 46px;
}
</style>
`;
}
}
Demo: loading image
Story: components-skeleton-variations--demo-loading-image · tags: components
This demo showcases skeletons that are replaced by an image after it loads.
<div class="layout">
<div class="container">
<oc-skeleton-v1
width="100%"
height="100%"
border-radius="0px"
class="skeleton"
hidden
></oc-skeleton-v1>
</div>
<div class="container">
<oc-skeleton-v1 width="100%" height="100%" class="skeleton" hidden></oc-skeleton-v1>
</div>
<div class="container" style="width: 150px; height: 150px;">
<oc-skeleton-v1
width="150px"
height="150px"
variant="circle"
class="skeleton"
hidden
></oc-skeleton-v1>
</div>
</div>
<br />
<p>
<oc-button-v1 variant="secondary" size="50" type="button" id="loading-button"
>Load image
</oc-button-v1>
</p>
<script>
(() => {
const button = document.getElementById("loading-button");
const container = document.querySelectorAll(".container");
function load() {
for (let element of container) {
const prevImage = element.querySelector("img");
const skeleton = element.querySelector("oc-skeleton-v1");
skeleton.hidden = false;
if (prevImage) {
prevImage.parentNode.removeChild(prevImage);
}
const image = document.createElement("img");
image.onload = () => {
skeleton.hidden = true;
};
element.append(image);
// simulate slower image loading
setTimeout(() => {
image.setAttribute("src", "${placeholderImage}");
}, 2000);
}
}
button.addEventListener("click", () => {
load();
});
})();
</script>
<style>
.layout {
display: flex;
flex-flow: row wrap;
gap: 10px;
}
.container {
position: relative;
width: 200px;
height: 150px;
overflow: hidden;
}
.container img {
object-fit: contain;
}
.skeleton {
position: absolute;
}
</style>
Story source (TypeScript, verbatim from Storybook)
{
name: "Demo: loading image",
parameters: {
controls: {
disabled: true
},
chromatic: {
hideInChromatic: true
}
},
argTypes: hideControlsBadge(Metadata),
render() {
return html`
<div class="layout">
<div class="container">
<oc-skeleton-v1
width="100%"
height="100%"
border-radius="0px"
class="skeleton"
hidden
></oc-skeleton-v1>
</div>
<div class="container">
<oc-skeleton-v1 width="100%" height="100%" class="skeleton" hidden></oc-skeleton-v1>
</div>
<div class="container" style="width: 150px; height: 150px;">
<oc-skeleton-v1
width="150px"
height="150px"
variant="circle"
class="skeleton"
hidden
></oc-skeleton-v1>
</div>
</div>
<br />
<p>
<oc-button-v1 variant="secondary" size="50" type="button" id="loading-button"
>Load image
</oc-button-v1>
</p>
<script>
(() => {
const button = document.getElementById("loading-button");
const container = document.querySelectorAll(".container");
function load() {
for (let element of container) {
const prevImage = element.querySelector("img");
const skeleton = element.querySelector("oc-skeleton-v1");
skeleton.hidden = false;
if (prevImage) {
prevImage.parentNode.removeChild(prevImage);
}
const image = document.createElement("img");
image.onload = () => {
skeleton.hidden = true;
};
element.append(image);
// simulate slower image loading
setTimeout(() => {
image.setAttribute("src", "${placeholderImage}");
}, 2000);
}
}
button.addEventListener("click", () => {
load();
});
})();
</script>
<style>
.layout {
display: flex;
flex-flow: row wrap;
gap: 10px;
}
.container {
position: relative;
width: 200px;
height: 150px;
overflow: hidden;
}
.container img {
object-fit: contain;
}
.skeleton {
position: absolute;
}
</style>
`;
}
}