kopia lustrzana https://github.com/shoelace-style/shoelace
First version of skeleton
rodzic
6ad0d676ef
commit
919d75c022
|
@ -4,26 +4,188 @@
|
||||||
|
|
||||||
Skeletons are used to show where content will eventually be drawn.
|
Skeletons are used to show where content will eventually be drawn.
|
||||||
|
|
||||||
|
These are simple containers for scaffolding layouts that mimic what users will see when content has finished loading. This prevents large areas of empty space during asynchronous operations.
|
||||||
|
|
||||||
|
Skeletons try not to be opinionated, as there are endless possibilities for designing layouts. Therefore, you'll likely use more than one skeleton to create the effect you want. If you find yourself using them frequently, consider creating a template that renders them with the desired arrangement and styles.
|
||||||
|
|
||||||
```html preview
|
```html preview
|
||||||
<div class="skeleton-overview">
|
<div class="skeleton-overview">
|
||||||
<sl-skeleton loading></sl-skeleton>
|
<header>
|
||||||
<sl-skeleton loading></sl-skeleton>
|
<sl-skeleton></sl-skeleton>
|
||||||
<sl-skeleton loading></sl-skeleton>
|
<sl-skeleton></sl-skeleton>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.skeleton-overview header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-overview header sl-skeleton:last-child {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
.skeleton-overview sl-skeleton {
|
.skeleton-overview sl-skeleton {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton-overview sl-skeleton:nth-child(2) {
|
.skeleton-overview sl-skeleton:nth-child(1) {
|
||||||
|
float: left;
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-overview sl-skeleton:nth-child(3) {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton-overview sl-skeleton:last-child {
|
.skeleton-overview sl-skeleton:nth-child(4) {
|
||||||
width: 60%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Effects
|
||||||
|
|
||||||
|
The default effect is `sheen`, which animates a sheen horizontally across the skeleton. There's also `pulse`, which fades the skeleton in and out. To disable animations, use `none`. Effects are intentionally subtle, as they can be distracting otherwise.
|
||||||
|
|
||||||
|
```html preview
|
||||||
|
<div class="skeleton-effects">
|
||||||
|
<sl-skeleton effect="sheen"></sl-skeleton>
|
||||||
|
Sheen
|
||||||
|
|
||||||
|
<sl-skeleton effect="pulse"></sl-skeleton>
|
||||||
|
Pulse
|
||||||
|
|
||||||
|
<sl-skeleton effect="none"></sl-skeleton>
|
||||||
|
None
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.skeleton-effects {
|
||||||
|
font-size: var(--sl-font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-effects sl-skeleton:not(:first-child) {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paragraphs
|
||||||
|
|
||||||
|
Use multiple skeletons and some clever styles to simulate paragraphs.
|
||||||
|
|
||||||
|
```html preview
|
||||||
|
<div class="skeleton-paragraphs">
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.skeleton-paragraphs sl-skeleton {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-paragraphs sl-skeleton:nth-child(2) {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-paragraphs sl-skeleton:nth-child(4) {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-paragraphs sl-skeleton:last-child {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Avatars
|
||||||
|
|
||||||
|
Set a matching width and height to make a circle, square, or rounded avatar skeleton.
|
||||||
|
|
||||||
|
```html preview
|
||||||
|
<div class="skeleton-avatars">
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
<sl-skeleton></sl-skeleton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.skeleton-avatars sl-skeleton {
|
||||||
|
display: inline-block;
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
margin-right: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-avatars sl-skeleton:nth-child(1) {
|
||||||
|
--border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-avatars sl-skeleton:nth-child(2) {
|
||||||
|
--border-radius: var(--sl-border-radius-medium);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Shapes
|
||||||
|
|
||||||
|
Use the `--border-radius` custom property to make circles, squares, and rectangles. For more complex shapes, you can apply `clip-path` to the `indicator` part. [Try Clippy](https://bennettfeely.com/clippy/) if you need help generating custom shapes.
|
||||||
|
|
||||||
|
```html preview
|
||||||
|
<div class="skeleton-sizes">
|
||||||
|
<sl-skeleton style="width: 100px; height: 100px; --border-radius: var(--sl-border-radius-medium);"></sl-skeleton>
|
||||||
|
<sl-skeleton style="width: 100px; height: 100px; --border-radius: var(--sl-border-radius-circle);"></sl-skeleton>
|
||||||
|
<sl-skeleton style="width: 100px; height: 100px; --border-radius: 0;"></sl-skeleton>
|
||||||
|
<sl-skeleton style="width: 100px; height: 100px; --border-radius: 0;"></sl-skeleton>
|
||||||
|
<sl-skeleton style="width: 100px; height: 100px; --border-radius: 0;"></sl-skeleton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.skeleton-sizes sl-skeleton {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-sizes sl-skeleton:nth-child(3)::part(indicator) {
|
||||||
|
clip-path: polygon(50% 0, 0 100%, 100% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-sizes sl-skeleton:nth-child(4)::part(indicator) {
|
||||||
|
clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-sizes sl-skeleton:nth-child(5)::part(indicator) {
|
||||||
|
clip-path: polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-sizes sl-skeleton:not(:last-child) {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Colors
|
||||||
|
|
||||||
|
Set the `--color` and `--sheen-color` custom properties to adjust the skeleton's color.
|
||||||
|
|
||||||
|
```html preview
|
||||||
|
<sl-skeleton style="--color: tomato; --sheen-color: #ffb094;"></sl-skeleton>
|
||||||
|
```
|
||||||
|
|
||||||
[component-metadata:sl-skeleton]
|
[component-metadata:sl-skeleton]
|
||||||
|
|
|
@ -42,7 +42,7 @@ export namespace Components {
|
||||||
*/
|
*/
|
||||||
"initials": string;
|
"initials": string;
|
||||||
/**
|
/**
|
||||||
* Initials to use as a fallback when no image is available (1-2 characters max recommended).
|
* The shape of the avatar.
|
||||||
*/
|
*/
|
||||||
"shape": 'circle' | 'square' | 'rounded';
|
"shape": 'circle' | 'square' | 'rounded';
|
||||||
}
|
}
|
||||||
|
@ -669,9 +669,9 @@ export namespace Components {
|
||||||
}
|
}
|
||||||
interface SlSkeleton {
|
interface SlSkeleton {
|
||||||
/**
|
/**
|
||||||
* When enabled, the skeleton will be animated to indicate that content is loading.
|
* Determines which effect the skeleton will use.
|
||||||
*/
|
*/
|
||||||
"loading": boolean;
|
"effect": 'pulse' | 'sheen' | 'none';
|
||||||
}
|
}
|
||||||
interface SlSpinner {
|
interface SlSpinner {
|
||||||
}
|
}
|
||||||
|
@ -1192,7 +1192,7 @@ declare namespace LocalJSX {
|
||||||
*/
|
*/
|
||||||
"initials"?: string;
|
"initials"?: string;
|
||||||
/**
|
/**
|
||||||
* Initials to use as a fallback when no image is available (1-2 characters max recommended).
|
* The shape of the avatar.
|
||||||
*/
|
*/
|
||||||
"shape"?: 'circle' | 'square' | 'rounded';
|
"shape"?: 'circle' | 'square' | 'rounded';
|
||||||
}
|
}
|
||||||
|
@ -1895,9 +1895,9 @@ declare namespace LocalJSX {
|
||||||
}
|
}
|
||||||
interface SlSkeleton {
|
interface SlSkeleton {
|
||||||
/**
|
/**
|
||||||
* When enabled, the skeleton will be animated to indicate that content is loading.
|
* Determines which effect the skeleton will use.
|
||||||
*/
|
*/
|
||||||
"loading"?: boolean;
|
"effect"?: 'pulse' | 'sheen' | 'none';
|
||||||
}
|
}
|
||||||
interface SlSpinner {
|
interface SlSpinner {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,60 @@
|
||||||
@import 'component';
|
@import 'component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @prop --border-radius: The skeleton's border radius.
|
||||||
* @prop --color: The color of the skeleton.
|
* @prop --color: The color of the skeleton.
|
||||||
* @prop --sheen-color: The sheen color when the skeleton is in its loading state.
|
* @prop --sheen-color: The sheen color when the skeleton is in its loading state.
|
||||||
*/
|
*/
|
||||||
:host {
|
:host {
|
||||||
|
--border-radius: var(--sl-border-radius-pill);
|
||||||
--color: var(--sl-color-gray-90);
|
--color: var(--sl-color-gray-90);
|
||||||
--sheen-color: var(--sl-color-gray-95);
|
--sheen-color: var(--sl-color-gray-95);
|
||||||
|
|
||||||
display: flex;
|
display: block;
|
||||||
height: 1em;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton {
|
.skeleton {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton__indicator {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
background: var(--color);
|
background: var(--color);
|
||||||
border-radius: var(--sl-border-radius-pill);
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton--loading {
|
.skeleton--sheen .skeleton__indicator {
|
||||||
background: linear-gradient(90deg, var(--sheen-color) 20%, var(--color) 40%, var(--sheen-color) 60%);
|
background: linear-gradient(270deg, var(--sheen-color), var(--color), var(--color), var(--sheen-color));
|
||||||
background-size: 400% 100%;
|
background-size: 400% 100%;
|
||||||
animation: loading 1.5s ease infinite;
|
background-size: 400% 100%;
|
||||||
|
animation: sheen 8s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes loading {
|
.skeleton--pulse .skeleton__indicator {
|
||||||
|
animation: pulse 2s ease-in-out 0.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sheen {
|
||||||
0% {
|
0% {
|
||||||
background-position: 100% 50%;
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-position: -200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-position: 0 50%;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Component, Prop, h } from '@stencil/core';
|
||||||
* @status stable
|
* @status stable
|
||||||
*
|
*
|
||||||
* @part base - The component's base wrapper.
|
* @part base - The component's base wrapper.
|
||||||
|
* @part indicator - The skeleton's indicator which is responsible for its color and animation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -13,8 +14,8 @@ import { Component, Prop, h } from '@stencil/core';
|
||||||
shadow: true
|
shadow: true
|
||||||
})
|
})
|
||||||
export class Skeleton {
|
export class Skeleton {
|
||||||
/** When enabled, the skeleton will be animated to indicate that content is loading. */
|
/** Determines which effect the skeleton will use. */
|
||||||
@Prop() loading = false;
|
@Prop() effect: 'pulse' | 'sheen' | 'none' = 'sheen';
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
@ -22,11 +23,14 @@ export class Skeleton {
|
||||||
part="base"
|
part="base"
|
||||||
class={{
|
class={{
|
||||||
skeleton: true,
|
skeleton: true,
|
||||||
'skeleton--loading': this.loading
|
'skeleton--pulse': this.effect === 'pulse',
|
||||||
|
'skeleton--sheen': this.effect === 'sheen'
|
||||||
}}
|
}}
|
||||||
aria-busy={this.loading}
|
aria-busy
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
/>
|
>
|
||||||
|
<div part="indicator" class="skeleton__indicator" />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue