Skip to content

Commit

Permalink
Feat(web): Introduce Skeleton component #DS-1625
Browse files Browse the repository at this point in the history
  • Loading branch information
Řehořková Kateřina committed Jan 22, 2025
1 parent fd239fd commit 080ae06
Show file tree
Hide file tree
Showing 7 changed files with 522 additions and 0 deletions.
108 changes: 108 additions & 0 deletions packages/web/src/scss/components/Skeleton/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Skeleton

## Text

The `Skeleton--text` class is used to create a text skeleton.

- Number of lines is defined by number of `Skeleton__item` elements
- Minimum number of lines is 1

```html

<div class="Skeleton Skeleton--text" aria-busy="true" aria-live="polite">
<div class="Skeleton__item Skeleton__item--text Skeleton__item--small" aria-hidden="true"></div>
<div class="Skeleton__item Skeleton__item--text Skeleton__item--small" aria-hidden="true"></div>
</div>
```

## Headline

The `Skeleton--headline` class is used to create a headline skeleton.

```html

<div class="Skeleton Skeleton--headline" aria-busy="true" aria-live="polite">
<div class="Skeleton__item Skeleton__item--text Skeleton__item--small" aria-hidden="true"></div>
<div class="Skeleton__item Skeleton__item--text Skeleton__item--small" aria-hidden="true"></div>
</div>
```

### Text, Headline sizes:

The Skeleton component supports the following sizes for text and headline skeletons:

- `Skeleton__item--xsmall`
- `Skeleton__item--small`
- `Skeleton__item--medium` (default)
- `Skeleton__item--large`
- `Skeleton__item--xlarge`

```html

<div class="Skeleton__item Skeleton__item--text Skeleton__item--xsmall" aria-hidden="true"></div>
```

## Shapes

Use CSS custom properties to define the width, height, and radius of the shape.

- The default radius is `--spirit-radius-300`

```html
--spirit-skeleton-shape-width: number{px};
--spirit-skeleton-shape-height: number{px};
--spirit-skeleton-shape-radius: var(--spirit-radius-200);
--spirit-skeleton-shape-tablet-radius: var(--spirit-radius-300);
--spirit-skeleton-shape-desktop-radius: var(--spirit-radius-400);
```

```html

<div class="Skeleton Skeleton--shape" aria-busy="true" aria-live="polite">
<div
class="Skeleton__item Skeleton__item--shape"
style="--spirit-skeleton-shape-width: 100px; --spirit-skeleton-shape-height: 100px; --spirit-skeleton-shape-radius: var(--spirit-radius-400)"
></div>
</div>
```

### Circle

- For circle there is class `Skeleton__item--circle`

```html
--spirit-skeleton-shape-width: number{px};
--spirit-skeleton-shape-height: number{px};
```

```html

<div class="Skeleton Skeleton--shape" aria-busy="true" aria-live="polite">
<div
class="Skeleton__item Skeleton__item--shape Skeleton__item--circle"
style="--spirit-skeleton-shape-width: 100px; --spirit-skeleton-shape-height: 100px"
></div>
</div>
```

Shape can be used on `img` element as well.

- `height` and `width` of element should be defined as well

```html

<div class="Skeleton Skeleton--shape" aria-busy="true" aria-live="polite">
<img
src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
width="100"
height="100"
alt="Loading"
class="Skeleton__item Skeleton__item--shape"
style="--spirit-skeleton-shape-width: 100px; --spirit-skeleton-shape-height: 100px; --spirit-skeleton-shape-radius: var(--spirit-radius-400)"
>
</div>
```




101 changes: 101 additions & 0 deletions packages/web/src/scss/components/Skeleton/_Skeleton.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
@use 'theme';
@use 'tools';
@use 'sass:map';
@use '@tokens' as tokens;
@use '../../tools/breakpoint';

.Skeleton {
width: 100%;
cursor: progress;
}

.Skeleton--text {
@include tools.generate-sizes($class-name: 'Skeleton__item', $sizes: theme.$sizes-body);
}

.Skeleton--heading {
@include tools.generate-sizes($class-name: 'Skeleton__item', $sizes: theme.$sizes-heading-mobile);

@include breakpoint.up(map.get(theme.$breakpoints, tablet)) {
@include tools.generate-sizes($class-name: 'Skeleton__item', $sizes: theme.$sizes-heading-tablet);
}

@include breakpoint.up(map.get(theme.$breakpoints, desktop)) {
@include tools.generate-sizes($class-name: 'Skeleton__item', $sizes: theme.$sizes-heading-desktop);
}
}

.Skeleton--shape {
display: inline-flex;
overflow: hidden;
}

.Skeleton__item {
display: block;
width: 100%;
background: theme.$background;
background-size: 400% 400%;
animation: skeleton-loading 2s ease infinite;

&:not(:last-child) {
margin-bottom: theme.$margin-bottom;
}
}

.Skeleton__item--text {
height: var(--#{tokens.$css-variable-prefix}skeleton-item-height, #{theme.$typography-default-height});
border-radius: theme.$typography-border-radius;

&:last-child:not(:first-child) {
width: 80%;
}
}

.Skeleton__item--shape {
width: var(--#{tokens.$css-variable-prefix}skeleton-shape-width);
height: var(--#{tokens.$css-variable-prefix}skeleton-shape-height);
object-fit: cover;
border: 1px solid theme.$shape-border-color;
border-radius: var(
--#{tokens.$css-variable-prefix}skeleton-shape-border-radius,
#{theme.$shape-default-border-radius}
);
}

.Skeleton__item--circle {
border-radius: theme.$border-radius-circle;
}

$previous-suffix: '';
$previous-previous-suffix: '';

@each $breakpoint-name, $breakpoint-value in theme.$breakpoints {
$suffix: breakpoint.get-modifier(suffix, $breakpoint-name, $breakpoint-value);
$prefix: #{tokens.$css-variable-prefix}skeleton-shape;

.Skeleton--shape {
--#{$prefix}#{$suffix}-radius: var(--#{tokens.$css-variable-prefix}skeleton-shape#{$suffix}-radius);
}

@include breakpoint.up($breakpoint-value) {
.Skeleton__item--shape {
--#{$prefix}-border-radius: var(
--#{$prefix}#{$suffix}-radius,
var(--#{$prefix}#{$previous-suffix}-radius, var(--#{$prefix}#{$previous-previous-suffix}-radius))
);
}
}

$previous-previous-suffix: $previous-suffix;
$previous-suffix: $suffix;
}

@keyframes skeleton-loading {
0% {
background-position: 100% 50%;
}

100% {
background-position: 0 50%;
}
}
89 changes: 89 additions & 0 deletions packages/web/src/scss/components/Skeleton/_theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@use '@tokens' as tokens;
@use 'sass:map';
@use 'tools';

$margin-bottom: tokens.$space-400;
$background: linear-gradient(94.81deg, #ececec 0%, #fff 48.5%, #ececec 100%); // TODO: update to tokens
$breakpoints: tokens.$breakpoints;

$typography-border-radius: tokens.$radius-300;
$typography-default-height: tools.get-height(tokens.$body-medium-bold, mobile);

$border-radius-default: tokens.$radius-300;
$border-radius-square: tokens.$radius-400;
$border-radius-circle: tokens.$radius-full;

$shape-border-color: tokens.$disabled-border;
$shape-default-border-radius: tokens.$radius-300;

$sizes-body: (
xsmall: (
height: tools.get-height(tokens.$body-xsmall-bold, mobile),
),
small: (
height: tools.get-height(tokens.$body-small-bold, mobile),
),
medium: (
height: tools.get-height(tokens.$body-medium-bold, mobile),
),
large: (
height: tools.get-height(tokens.$body-large-bold, mobile),
),
xlarge: (
height: tools.get-height(tokens.$body-xlarge-bold, mobile),
),
);

$sizes-heading-mobile: (
xsmall: (
height: tools.get-height(tokens.$heading-xsmall-bold, mobile),
),
small: (
height: tools.get-height(tokens.$heading-small-bold, mobile),
),
medium: (
height: tools.get-height(tokens.$heading-medium-bold, mobile),
),
large: (
height: tools.get-height(tokens.$heading-large-bold, mobile),
),
xlarge: (
height: tools.get-height(tokens.$heading-xlarge-bold, mobile),
),
);

$sizes-heading-tablet: (
xsmall: (
height: tools.get-height(tokens.$heading-xsmall-bold, tablet),
),
small: (
height: tools.get-height(tokens.$heading-small-bold, tablet),
),
medium: (
height: tools.get-height(tokens.$heading-medium-bold, tablet),
),
large: (
height: tools.get-height(tokens.$heading-large-bold, tablet),
),
xlarge: (
height: tools.get-height(tokens.$heading-xlarge-bold, tablet),
),
);

$sizes-heading-desktop: (
xsmall: (
height: tools.get-height(tokens.$heading-xsmall-bold, desktop),
),
small: (
height: tools.get-height(tokens.$heading-small-bold, desktop),
),
medium: (
height: tools.get-height(tokens.$heading-medium-bold, desktop),
),
large: (
height: tools.get-height(tokens.$heading-large-bold, desktop),
),
xlarge: (
height: tools.get-height(tokens.$heading-xlarge-bold, desktop),
),
);
20 changes: 20 additions & 0 deletions packages/web/src/scss/components/Skeleton/_tools.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@use 'sass:map';
@use '../../tools/breakpoint';
@use '../../tools/string' as spirit-string;
@use '@tokens' as tokens;

@mixin generate-sizes($class-name, $sizes) {
@each $size, $variables in $sizes {
.#{$class-name}--#{$size} {
$component-infix: 'skeleton-item';

@each $variable-key, $variable-value in $variables {
--#{tokens.$css-variable-prefix}#{$component-infix}-#{$variable-key}: #{$variable-value};
}
}
}
}

@function get-height($token, $device) {
@return map.get($token, $device, font-size) * map.get($token, $device, line-height);
}
Loading

0 comments on commit 080ae06

Please sign in to comment.