Skip to content

Commit ca756f7

Browse files
committed
feat: Add customizable highlight and animation to Skeleton
- Introduces new props to Skeleton and SkeletonTheme components: baseColor, highlightColor, animationDirection, and customHighlightBackground, allowing for more flexible skeleton loading animations. - Updates documentation to reflect these changes, revises styles to use CSS variables for color and animation, and bumps package version to 2.0.0 to indicate breaking or significant new features.
1 parent c9e7479 commit ca756f7

File tree

5 files changed

+98
-38
lines changed

5 files changed

+98
-38
lines changed

README.md

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ Customize individual skeletons with props, or render a SkeletonTheme to style al
138138
<template>
139139
<div class="Home">
140140
<!-- This applies Base Color and Highligh Color to all Skeletons -->
141-
<SkeletonTheme background-color="#303030">
141+
<SkeletonTheme base-color="#303030" highlight-color="#606060">
142142
<h1>
143143
<Skeleton>{{ data.title }}</Skeleton>
144144
</h1>
@@ -158,25 +158,42 @@ Down bellow you can take a look at each prop available.
158158

159159
### `<Skeleton>`
160160

161-
| Name | Type | Description | Default |
162-
| -------------- | --------- | -------------------------------------------------------------------- | ------- |
163-
| rows | `number` | Set component amount of rows | `1` |
164-
| circle | `boolean` | Set component `border-radius` to 50%, it replaces borderRadius props | `false` |
165-
| containerClass | `string` | Set component class to skeleton container | `null` |
166-
| childClass | `string` | Set component class to each skeleton child | `null` |
161+
| Name | Type | Description | Default |
162+
| -------------- | --------- | -------------------------------------------------------------------- | ----------- |
163+
| rows | `number` | Set component amount of rows | `1` |
164+
| circle | `boolean` | Set component `border-radius` to 50%, it replaces borderRadius props | `false` |
165+
| containerClass | `string` | Set component class to skeleton container | `null` |
166+
| childClass | `string` | Set component class to each skeleton child | `null` |
167167
| loading | `boolean` | Set component loading state | `undefined` |
168168

169169
### `<Skeleton>` `<SkeletonTheme>`
170170

171-
| Name | Type | Description | Default |
172-
| ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------ | --------- |
173-
| width | `string` `number` | Set component `width`, it can be either number `px` or string with its corresponding css value | `100%` |
174-
| height | `string` `number` | Set component `height`, it can be either number `px` or string with its corresponding css value | `inherit` |
175-
| borderRadius | `string` `number` | Set component `border-radius`, it can be either number `px` or string with its corresponding css value | `0.25rem` |
176-
| backgroundColor | `string` | Set component `background-color` | `#e1e1e1` |
177-
| animationDuration | `number` | Set component `animation-duration` in seconds | `2` |
178-
| enableAnimation | `boolean` | Set component animation status `running` or `paused` | `true` |
179-
| inline | `boolean` | Set component inline behavior | `false` |
171+
| Name | Type | Description | Default |
172+
| ------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------- | ----------- |
173+
| width | `string` `number` | Set component `width`, it can be either number `px` or string with its corresponding css value | `100%` |
174+
| height | `string` `number` | Set component `height`, it can be either number `px` or string with its corresponding css value | `inherit` |
175+
| borderRadius | `string` `number` | Set component `border-radius`, it can be either number `px` or string with its corresponding css value | `0.25rem` |
176+
| baseColor | `string` | Set component `background-color` | `#ebebeb` |
177+
| highlightColor | `string` | Set component `background-image` highlight color animation | `#ebebeb` |
178+
| animationDuration | `number` | Set component `animation-duration` in seconds | `#f5f5f5` |
179+
| animationDirection | `normal` `reverse` | Set component animation direction `normal` (left to right) or `reverse` (right to left) | `normal` |
180+
| enableAnimation | `boolean` | Set component animation status `running` or `paused` | `true` |
181+
| inline | `boolean` | Set component inline behavior | `false` |
182+
| customHighlightBackground | `string` | Override the `background-image` property of the highlight element, enabling you to fully customize the gradient. | `undefined` |
183+
184+
#### Custom Highlight Background
185+
186+
You may want to make the gradient used in the highlight element narrower or wider. To do this, you can set the
187+
customHighlightBackground prop. Here's an example of a narrow highlight:
188+
189+
```html
190+
<Skeleton custom-highlight-background="linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)">
191+
Loading...
192+
</Skeleton>
193+
```
194+
195+
If you use this prop, the baseColor and highlightColor props are ignored, but you can still reference their
196+
corresponding CSS variables as shown in the above example.
180197

181198
### Troubleshooting
182199

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@brayamvalero/vue3-skeleton",
33
"description": "An elegant skeleton library compatible with Vue 3",
4-
"version": "1.0.1",
4+
"version": "2.0.0",
55
"author": "Brayam Valero",
66
"license": "MIT",
77
"repository": {

src/App.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ onMounted(() => {
4545
<!-- User Profile example -->
4646
<section class="user">
4747
<picture class="user-avatar">
48-
<Skeleton v-if="!data.img" childClass="user-avatar" background-color="#666" />
48+
<Skeleton v-if="!data.img" childClass="user-avatar" />
4949
<img v-else :src="data.img" />
5050
</picture>
5151
<div class="user-info">
@@ -64,7 +64,7 @@ onMounted(() => {
6464
<h2 class="mb-2">Inline loading with static data</h2>
6565
<section class="user">
6666
<picture class="user-avatar">
67-
<Skeleton :loading="manualLoading" childClass="user-avatar" background-color="#666">
67+
<Skeleton :loading="manualLoading" childClass="user-avatar">
6868
<img :src="data.img" />
6969
</Skeleton>
7070
</picture>

src/components/Skeleton.vue

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const props = withDefaults(defineProps<SkeletonProps>(), {
77
rows: 1,
88
enableAnimation: undefined,
99
inline: undefined,
10-
loading: undefined
10+
loading: undefined,
1111
})
1212
1313
const theme = inject(ThemeInjection, {})
@@ -21,7 +21,7 @@ const slots: any = useSlots()
2121
2222
const getLoadingStatus = computed<boolean>(() => {
2323
if (props.loading !== undefined) return !props.loading
24-
24+
2525
if (slots.default) return slots.default()[0].children || false
2626
})
2727
@@ -31,9 +31,12 @@ const getStyles = (): CSSProperties => {
3131
const width = props.width ?? theme.width ?? null
3232
const height = props.height ?? theme.height ?? null
3333
const borderRadius = props.borderRadius ?? theme.borderRadius ?? null
34-
const backgroundColor = props.backgroundColor ?? theme.backgroundColor ?? null
34+
const baseColor = props.baseColor ?? theme.baseColor ?? null
35+
const highlightColor = props.highlightColor ?? theme.highlightColor ?? null
3536
const animationDuration = props.animationDuration ?? theme.animationDuration ?? null
37+
const animationDirection = props.animationDirection ?? theme.animationDirection ?? 'normal'
3638
const enableAnimation = props.enableAnimation ?? theme.enableAnimation ?? null
39+
const customHighlightBackground = props.customHighlightBackground ?? theme.customHighlightBackground ?? undefined
3740
const circle = props.circle
3841
3942
// Convert Width
@@ -48,14 +51,19 @@ const getStyles = (): CSSProperties => {
4851
// Rounded Circle
4952
if (circle) style.borderRadius = '50%'
5053
51-
// Background Color
52-
if (backgroundColor) style.backgroundColor = backgroundColor
53-
54-
// Animation Duration
55-
if (animationDuration) style.animationDuration = `${animationDuration}s`
56-
57-
// Animation Play State
58-
if (enableAnimation) style.animationPlayState = enableAnimation ? 'running' : 'paused'
54+
// Set Custom CSS Variables based on props or theme
55+
if (baseColor) style['--base-color'] = baseColor
56+
if (highlightColor) style['--highlight-color'] = highlightColor
57+
if (animationDuration) style['--animation-duration'] = `${animationDuration}s`
58+
if (animationDirection) style['--animation-direction'] = animationDirection
59+
if (enableAnimation !== undefined) {
60+
style['--pseudo-element-display'] = 'block'
61+
} else {
62+
style['--pseudo-element-display'] = enableAnimation ? 'block' : 'none'
63+
}
64+
if (customHighlightBackground) {
65+
style['--custom-highlight-background'] = customHighlightBackground
66+
}
5967
6068
return style
6169
}
@@ -81,22 +89,54 @@ function convertToPx(value: string | number | null, name: 'width' | 'height' | '
8189
</template>
8290

8391
<style>
84-
@keyframes placeholder-glow {
85-
50% {
86-
opacity: 0.5;
92+
@keyframes loading-skeleton {
93+
100% {
94+
transform: translateX(100%);
8795
}
8896
}
8997
9098
.skeleton-loading {
99+
--base-color: #ebebeb;
100+
--highlight-color: #f5f5f5;
101+
--animation-duration: 1.5s;
102+
--animation-direction: normal;
103+
--pseudo-element-display: block;
104+
105+
background-color: var(--base-color);
91106
width: 100%;
92-
height: inherit;
93107
border-radius: 0.25rem;
94-
background-color: #e1e1e1;
95108
display: inline-flex;
96109
line-height: 1;
97110
position: relative;
98111
user-select: none;
99112
overflow: hidden;
100-
animation: placeholder-glow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
113+
}
114+
115+
.skeleton-loading::after {
116+
content: ' ';
117+
display: var(--pseudo-element-display);
118+
position: absolute;
119+
top: 0;
120+
left: 0;
121+
right: 0;
122+
height: 100%;
123+
background-repeat: no-repeat;
124+
background-image: var(
125+
--custom-highlight-background,
126+
linear-gradient(90deg, var(--base-color) 0%, var(--highlight-color) 50%, var(--base-color) 100%)
127+
);
128+
transform: translateX(-100%);
129+
130+
animation-name: loading-skeleton;
131+
animation-direction: var(--animation-direction);
132+
animation-duration: var(--animation-duration);
133+
animation-timing-function: ease-in-out;
134+
animation-iteration-count: infinite;
135+
}
136+
137+
@media (prefers-reduced-motion) {
138+
.skeleton-loading {
139+
--pseudo-element-display: block;
140+
}
101141
}
102142
</style>

src/types/index.types.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ export interface SkeletonThemeProps {
33
width?: string | number
44
height?: string | number
55
borderRadius?: string | number
6-
backgroundColor?: string
7-
animationDuration?: number
86
enableAnimation?: boolean
97
inline?: boolean
8+
baseColor?: string
9+
highlightColor?: string
10+
animationDuration?: number
11+
animationDirection?: 'normal' | 'reverse'
12+
customHighlightBackground?: string
1013
}
1114

1215
// Exclusive Props from Skeleton

0 commit comments

Comments
 (0)