diff --git a/packages/web/src/scss/components/Skeleton/README.md b/packages/web/src/scss/components/Skeleton/README.md
new file mode 100644
index 0000000000..efe9d910f4
--- /dev/null
+++ b/packages/web/src/scss/components/Skeleton/README.md
@@ -0,0 +1,70 @@
+# Skeleton
+
+On the parent element, you must use `aria-busy` and `aria-live` attributes to indicate that the content inside is loading.
+The `aria-busy` is set to `true` when the content is loading, and `aria-live` is set to `polite` to announce the loading
+state to screen readers.
+
+## Text
+
+The `Skeleton--text` class is used to create a text skeleton.
+
+- Number of lines is defined by the number of `Skeleton__item` elements
+- Minimum number of lines is 1
+
+```html
+
+```
+
+## Heading
+
+The `Skeleton--heading` class is used to create a heading skeleton.
+
+```html
+
+```
+
+### Text, Heading Sizes
+
+The Skeleton component supports the following sizes for text and heading skeletons:
+
+- `Skeleton--xsmall`
+- `Skeleton--small`
+- `Skeleton--medium` (default)
+- `Skeleton--large`
+- `Skeleton--xlarge`
+
+```html
+
+```
+
+## Shapes
+
+Use CSS custom properties to define the width, height, and radius of the shape.
+
+- The default radius is `--spirit-radius-300`
+
+- `--spirit-skeleton-shape-width: number{px};`
+- `--spirit-skeleton-shape-height: number{px};`
+- `--spirit-skeleton-shape-radius: var(--spirit-radius-200);`
+- `--spirit-skeleton-shape-radius-tablet: var(--spirit-radius-300);`
+- `--spirit-skeleton-shape-radius-desktop: var(--spirit-radius-400);`
+
+```html
+
+```
+
+```html
+
+```
diff --git a/packages/web/src/scss/components/Skeleton/_Skeleton.scss b/packages/web/src/scss/components/Skeleton/_Skeleton.scss
new file mode 100644
index 0000000000..3f9909f589
--- /dev/null
+++ b/packages/web/src/scss/components/Skeleton/_Skeleton.scss
@@ -0,0 +1,78 @@
+@use '@tokens' as tokens;
+@use '../../tools/breakpoint';
+@use '../../tools/responsive-properties';
+@use 'theme';
+@use 'tools';
+
+.Skeleton {
+ width: 100%;
+}
+
+.Skeleton--text {
+ @include tools.generate-item-sizes(
+ $class-name: 'Skeleton',
+ $sizes: tools.generate-text-sizes(theme.$sizes-body, 'mobile')
+ );
+}
+
+.Skeleton--heading {
+ @each $breakpoint-name, $breakpoint-value in theme.$breakpoints {
+ @include breakpoint.up($breakpoint-value) {
+ @include tools.generate-item-sizes(
+ $class-name: 'Skeleton',
+ $sizes: tools.generate-text-sizes(theme.$sizes-heading, $breakpoint-name)
+ );
+ }
+ }
+}
+
+.Skeleton--shape {
+ display: inline-flex;
+ flex-shrink: 0;
+ width: var(--#{tokens.$css-variable-prefix}skeleton-shape-width);
+ height: var(--#{tokens.$css-variable-prefix}skeleton-shape-height);
+ border: theme.$shape-border-width solid theme.$shape-border-color;
+
+ @include responsive-properties.create-cascade(
+ $property: 'border-radius',
+ $input-custom-property-base-name: '#{tokens.$css-variable-prefix}skeleton-shape-radius',
+ $breakpoints: theme.$breakpoints,
+ $fallback-value: theme.$shape-default-border-radius
+ );
+}
+
+.Skeleton__item,
+.Skeleton--shape {
+ background: theme.$item-gradient;
+ background-size: 600% 600%;
+
+ @media (prefers-reduced-motion: no-preference) {
+ animation: skeleton-loading 2.5s infinite;
+ }
+}
+
+.Skeleton--text > .Skeleton__item,
+.Skeleton--heading > .Skeleton__item {
+ display: block;
+ width: 100%;
+ height: var(--#{tokens.$css-variable-prefix}skeleton-height, #{theme.$typography-default-height});
+ border-radius: theme.$typography-border-radius;
+
+ &:not(:last-child) {
+ margin-bottom: theme.$margin-bottom;
+ }
+
+ &:last-child:not(:only-child) {
+ width: 80%;
+ }
+}
+
+@keyframes skeleton-loading {
+ 0% {
+ background-position: 100% 50%;
+ }
+
+ 100% {
+ background-position: 0 50%;
+ }
+}
diff --git a/packages/web/src/scss/components/Skeleton/_theme.scss b/packages/web/src/scss/components/Skeleton/_theme.scss
new file mode 100644
index 0000000000..7010140293
--- /dev/null
+++ b/packages/web/src/scss/components/Skeleton/_theme.scss
@@ -0,0 +1,29 @@
+@use '@tokens' as tokens;
+@use 'tools';
+
+$margin-bottom: tokens.$space-400;
+$item-gradient: tokens.$gradient-skeleton;
+$breakpoints: tokens.$breakpoints;
+
+$typography-border-radius: tokens.$radius-300;
+$typography-default-height: tools.get-height(tokens.$body-medium-bold, mobile);
+
+$shape-border-color: tokens.$disabled-border;
+$shape-border-width: tokens.$border-width-100;
+$shape-default-border-radius: tokens.$radius-300;
+
+$sizes-body: (
+ xsmall: tokens.$body-xsmall-bold,
+ small: tokens.$body-small-bold,
+ medium: tokens.$body-medium-bold,
+ large: tokens.$body-large-bold,
+ xlarge: tokens.$body-xlarge-bold,
+);
+
+$sizes-heading: (
+ xsmall: tokens.$heading-xsmall-bold,
+ small: tokens.$heading-small-bold,
+ medium: tokens.$heading-medium-bold,
+ large: tokens.$heading-large-bold,
+ xlarge: tokens.$heading-xlarge-bold,
+);
diff --git a/packages/web/src/scss/components/Skeleton/_tools.scss b/packages/web/src/scss/components/Skeleton/_tools.scss
new file mode 100644
index 0000000000..359ef73779
--- /dev/null
+++ b/packages/web/src/scss/components/Skeleton/_tools.scss
@@ -0,0 +1,36 @@
+@use 'sass:map';
+@use '@tokens' as tokens;
+@use '../../tools/string' as spirit-string;
+
+@function get-height($token, $device) {
+ @return map.get($token, $device, font-size) * map.get($token, $device, line-height);
+}
+
+@function generate-text-sizes($sizes, $device) {
+ $result: ();
+
+ @each $size, $token in $sizes {
+ $height: get-height($token, $device);
+ $size-map: (
+ #{$size}:
+ (
+ height: $height,
+ )
+ );
+ $result: map.merge($result, $size-map);
+ }
+
+ @return $result;
+}
+
+@mixin generate-item-sizes($class-name, $sizes) {
+ @each $size, $variables in $sizes {
+ &.#{$class-name}--#{$size} {
+ $component-infix: spirit-string.convert-pascal-case-to-kebab-case($class-name);
+
+ @each $variable-key, $variable-value in $variables {
+ --#{tokens.$css-variable-prefix}#{$component-infix}-#{$variable-key}: #{$variable-value};
+ }
+ }
+ }
+}
diff --git a/packages/web/src/scss/components/Skeleton/index.html b/packages/web/src/scss/components/Skeleton/index.html
new file mode 100644
index 0000000000..aab71f8309
--- /dev/null
+++ b/packages/web/src/scss/components/Skeleton/index.html
@@ -0,0 +1,169 @@
+{{#> web/layout/default title="Skeleton" parentPageName="Components" }}
+
+{{setVar "sizes" "xsmall" "small" "medium" "large" "xlarge" }}
+
+
+
+
SkeletonText
+
+
+
+ {{#each @root.sizes as |size|}}
+
+ {{/each}}
+
+
+
+
+
+
+
+
+
SkeletonHeading
+
+
+
+ {{#each @root.sizes as |size|}}
+
+ {{/each}}
+
+
+
+
+
+
+
+
+
SkeletonShape
+
+
+
+
+
+
+
+
+
+
+ Shape Rectangular responsive radius
+
+
+
+
+
+
+
+
+
+
+
+{{/web/layout/default }}
diff --git a/packages/web/src/scss/components/Skeleton/index.scss b/packages/web/src/scss/components/Skeleton/index.scss
new file mode 100644
index 0000000000..f44b6cf98b
--- /dev/null
+++ b/packages/web/src/scss/components/Skeleton/index.scss
@@ -0,0 +1 @@
+@forward 'Skeleton';
diff --git a/packages/web/src/scss/components/index.scss b/packages/web/src/scss/components/index.scss
index 13a2888365..417e9c0c67 100644
--- a/packages/web/src/scss/components/index.scss
+++ b/packages/web/src/scss/components/index.scss
@@ -23,6 +23,7 @@
@forward 'Radio';
@forward 'ScrollView';
@forward 'Select';
+@forward 'Skeleton';
@forward 'Stack';
@forward 'Tabs';
@forward 'Tag';
diff --git a/tests/e2e/demo-components-compare.spec.ts-snapshots/skeleton-chromium-linux.png b/tests/e2e/demo-components-compare.spec.ts-snapshots/skeleton-chromium-linux.png
new file mode 100644
index 0000000000..8b9dac0f74
Binary files /dev/null and b/tests/e2e/demo-components-compare.spec.ts-snapshots/skeleton-chromium-linux.png differ
diff --git a/tests/e2e/demo-homepages.spec.ts-snapshots/web-chromium-linux.png b/tests/e2e/demo-homepages.spec.ts-snapshots/web-chromium-linux.png
index e6d8da3af5..845ef7640a 100644
Binary files a/tests/e2e/demo-homepages.spec.ts-snapshots/web-chromium-linux.png and b/tests/e2e/demo-homepages.spec.ts-snapshots/web-chromium-linux.png differ