@alikhalilll/a-smart-skeleton 0.0.0-stage → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +356 -2
- package/dist/_chunks/core.js +2314 -0
- package/dist/_chunks/core.js.map +1 -0
- package/dist/_chunks/index.d.ts +431 -0
- package/dist/core/index.cjs +67 -0
- package/dist/core/index.d.cts +2 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/element/index.cjs +47 -0
- package/dist/element/index.cjs.map +1 -0
- package/dist/element/index.d.cts +24 -0
- package/dist/element/index.d.ts +24 -0
- package/dist/element/index.js +46 -0
- package/dist/element/index.js.map +1 -0
- package/dist/index.cjs +512 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +204 -0
- package/dist/index.d.ts +204 -0
- package/dist/index.js +441 -0
- package/dist/index.js.map +1 -0
- package/dist/nuxt/index.cjs +36 -0
- package/dist/nuxt/index.cjs.map +1 -0
- package/dist/nuxt/index.d.cts +30 -0
- package/dist/nuxt/index.d.ts +30 -0
- package/dist/nuxt/index.js +36 -0
- package/dist/nuxt/index.js.map +1 -0
- package/dist/resolver/index.cjs +26 -0
- package/dist/resolver/index.cjs.map +1 -0
- package/dist/resolver/index.d.cts +19 -0
- package/dist/resolver/index.d.ts +19 -0
- package/dist/resolver/index.js +26 -0
- package/dist/resolver/index.js.map +1 -0
- package/dist/styles.css +136 -0
- package/package.json +144 -3
- package/src/components/ASmartSkeleton.vue +228 -0
- package/src/components/ASmartSkeletonBox.vue +23 -0
- package/src/components/ASmartSkeletonProvider.vue +34 -0
- package/src/components/ASmartSkeletonSuspense.vue +142 -0
- package/src/composables/useSmartSkeleton.ts +20 -0
- package/src/composables/useStyles.ts +8 -0
- package/src/core/boot.ts +63 -0
- package/src/core/canvas.ts +7 -0
- package/src/core/constants.ts +76 -0
- package/src/core/debug.ts +154 -0
- package/src/core/dev.ts +53 -0
- package/src/core/fade.ts +490 -0
- package/src/core/ghost.ts +236 -0
- package/src/core/images.ts +28 -0
- package/src/core/index.ts +71 -0
- package/src/core/inline.generated.ts +9 -0
- package/src/core/measure.ts +571 -0
- package/src/core/memory.ts +146 -0
- package/src/core/motion.ts +12 -0
- package/src/core/plan.ts +368 -0
- package/src/core/reveal.ts +89 -0
- package/src/core/reveals.ts +39 -0
- package/src/core/shapes.ts +84 -0
- package/src/core/shimmer.ts +229 -0
- package/src/core/skeleton.ts +43 -0
- package/src/core/snapshot.ts +159 -0
- package/src/core/styles.ts +187 -0
- package/src/core/sweep.ts +203 -0
- package/src/core/theme.ts +162 -0
- package/src/element/index.ts +59 -0
- package/src/index.ts +27 -0
- package/src/nuxt/index.ts +59 -0
- package/src/resolver/index.ts +35 -0
- package/src/types.ts +100 -0
- package/src/vue/Skeleton.ts +275 -0
- package/src/vue/Suspense.ts +157 -0
- package/src/vue/config.ts +78 -0
- package/src/vue/index.ts +7 -0
- package/web-types.json +256 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 alikhalilll
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,357 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@alikhalilll/a-smart-skeleton`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Measured skeleton loaders for Vue 3 and Nuxt 3+. Captures your real component's layout — background, border, radius, box-shadow, text lines, and media boxes — and replays it as a pixel-faithful placeholder. One template to maintain. Zero cumulative layout shift.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@alikhalilll/a-smart-skeleton)
|
|
6
|
+
[](https://github.com/alikhalilll/ali-nuxt-toolkit/blob/main/LICENSE)
|
|
7
|
+
[](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton)
|
|
8
|
+
|
|
9
|
+
**[Live demo and full documentation →](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton)**
|
|
10
|
+
|
|
11
|
+
Hand-authored skeletons are the second template you have to maintain. Padding drifts, font sizes bump, images start loading later — the skeleton guesses wrong and the reveal jumps. `ASmartSkeleton` lays your real content out invisibly, measures the live geometry, and reconstructs a skeleton from what it saw. One template — the real one — stays the source of truth.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of contents
|
|
16
|
+
|
|
17
|
+
- [Install](#install)
|
|
18
|
+
- [Minimal example](#minimal-example)
|
|
19
|
+
- [Full example — social feed card](#full-example--social-feed-card)
|
|
20
|
+
- [States: `loading`, `content`, `empty`, `error`](#states-loading-content-empty-error)
|
|
21
|
+
- [Hints — `ask-block`, `ask-ignore`, `ask-own`](#hints--ask-block-ask-ignore-ask-own)
|
|
22
|
+
- [Chrome capture](#chrome-capture)
|
|
23
|
+
- [Reveal transitions](#reveal-transitions)
|
|
24
|
+
- [Theming](#theming)
|
|
25
|
+
- [API reference](#api-reference)
|
|
26
|
+
- [Changelog highlights](#changelog-highlights)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pnpm add @alikhalilll/a-smart-skeleton
|
|
34
|
+
# or
|
|
35
|
+
npm install @alikhalilll/a-smart-skeleton
|
|
36
|
+
# or
|
|
37
|
+
yarn add @alikhalilll/a-smart-skeleton
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Nuxt 3 / 4** — add the module, four components auto-import:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// nuxt.config.ts
|
|
44
|
+
export default defineNuxtConfig({
|
|
45
|
+
modules: ['@alikhalilll/a-smart-skeleton/nuxt'],
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Vue 3 + Vite** — import the stylesheet and optionally register the auto-import resolver:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
// main.ts
|
|
53
|
+
import '@alikhalilll/a-smart-skeleton/styles.css';
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// vite.config.ts (optional, for unplugin-vue-components)
|
|
58
|
+
import Components from 'unplugin-vue-components/vite';
|
|
59
|
+
import ASmartSkeletonResolver from '@alikhalilll/a-smart-skeleton/resolver';
|
|
60
|
+
|
|
61
|
+
export default {
|
|
62
|
+
plugins: [Components({ resolvers: [ASmartSkeletonResolver()] })],
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Full install guide with troubleshooting → [docs/install](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#install).
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Minimal example
|
|
71
|
+
|
|
72
|
+
The simplest useful pattern — bind `data`, pass a `sample`, give it an `id`, done:
|
|
73
|
+
|
|
74
|
+
```vue
|
|
75
|
+
<script setup lang="ts">
|
|
76
|
+
import { ref, onMounted } from 'vue';
|
|
77
|
+
|
|
78
|
+
const user = ref<User | null>(null);
|
|
79
|
+
|
|
80
|
+
const SAMPLE_USER = {
|
|
81
|
+
name: 'Sarah Connor',
|
|
82
|
+
role: 'Chief Mitigation Officer',
|
|
83
|
+
avatar: 'https://example.com/avatar.jpg',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
onMounted(async () => {
|
|
87
|
+
user.value = await $fetch('/api/me');
|
|
88
|
+
});
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
<ASmartSkeleton :data="user" :sample="SAMPLE_USER" id="user-card">
|
|
93
|
+
<template #default="{ data }">
|
|
94
|
+
<UserCard :user="data" />
|
|
95
|
+
</template>
|
|
96
|
+
</ASmartSkeleton>
|
|
97
|
+
</template>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Three things to know:**
|
|
101
|
+
|
|
102
|
+
1. **Always pass a `sample`.** It's what the skeleton measures before real data arrives.
|
|
103
|
+
2. **Give the skeleton a stable `id`.** Shape memory is keyed by `id` across visits.
|
|
104
|
+
3. **Give images explicit `width` / `height`** (or `aspect-ratio`) so the skeleton can reserve their space.
|
|
105
|
+
|
|
106
|
+
Live on the site → [Quick start demo](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#quick-start).
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Full example — social feed card
|
|
111
|
+
|
|
112
|
+
A real social feed: `<article>` wrapper with `border` + `bg-white` + `rounded-[12px]`, split sub-components for `Header` (avatar + author + actions menu), body text, `MediaGrid`, and a `footer` divider holding the reactions and comment buttons.
|
|
113
|
+
|
|
114
|
+
Interactive version with reveal-mode / animation / theme controls → [Customize demo](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#customize).
|
|
115
|
+
|
|
116
|
+
```vue
|
|
117
|
+
<!-- PostList.vue (parent) -->
|
|
118
|
+
<script setup lang="ts">
|
|
119
|
+
import { ref, computed } from 'vue';
|
|
120
|
+
import PostCard from './PostCard.vue';
|
|
121
|
+
|
|
122
|
+
interface Post {
|
|
123
|
+
id: string;
|
|
124
|
+
author: { id: string; name: string; avatar?: string | null; verified?: boolean };
|
|
125
|
+
created_at_relative: string;
|
|
126
|
+
body: string;
|
|
127
|
+
media?: { type: 'image' | 'video'; url: string }[];
|
|
128
|
+
reactions_count: number;
|
|
129
|
+
comments_count: number;
|
|
130
|
+
is_reacted?: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Sample bodies match the real-feed replacements 1:1 in length so the
|
|
134
|
+
// reveal doesn't trigger the plugin's dev-only "layout jumped" warning.
|
|
135
|
+
const SAMPLE_POSTS: Post[] = [
|
|
136
|
+
/* ... three sample shapes ... */
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
const feed = ref<Post[] | null>(null);
|
|
140
|
+
async function loadFeed() {
|
|
141
|
+
feed.value = null;
|
|
142
|
+
feed.value = await $fetch<Post[]>('/api/community/posts');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const theme = computed(() => ({
|
|
146
|
+
light: { base: '#d1d4dc', highlight: '#ffffff' },
|
|
147
|
+
dark: { base: '#31363f', highlight: 'rgba(255,255,255,0.08)' },
|
|
148
|
+
animation: 'shimmer' as const,
|
|
149
|
+
speed: 'slow' as const,
|
|
150
|
+
}));
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<template>
|
|
154
|
+
<ASmartSkeletonProvider :theme="theme" mode="system" reveal="smart">
|
|
155
|
+
<ASmartSkeleton id="community-feed" :data="feed" :sample="SAMPLE_POSTS">
|
|
156
|
+
<template #default="{ data }">
|
|
157
|
+
<div class="flex flex-col gap-4">
|
|
158
|
+
<PostCard v-for="post in data as Post[]" :key="post.id" :post="post" />
|
|
159
|
+
</div>
|
|
160
|
+
</template>
|
|
161
|
+
</ASmartSkeleton>
|
|
162
|
+
</ASmartSkeletonProvider>
|
|
163
|
+
</template>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```vue
|
|
167
|
+
<!-- PostCard.vue (child) -->
|
|
168
|
+
<script setup lang="ts">
|
|
169
|
+
import Header from './Header.vue';
|
|
170
|
+
import MediaGrid from './MediaGrid.vue';
|
|
171
|
+
import Reactions from './Reactions.vue';
|
|
172
|
+
|
|
173
|
+
defineProps<{ post: Post }>();
|
|
174
|
+
</script>
|
|
175
|
+
|
|
176
|
+
<template>
|
|
177
|
+
<article
|
|
178
|
+
:id="`post-${post.id}`"
|
|
179
|
+
class="border border-[#EDF0F3] rounded-[12px] bg-white flex flex-col w-full"
|
|
180
|
+
>
|
|
181
|
+
<Header :post="post" class="px-5 pb-4 pt-4" />
|
|
182
|
+
|
|
183
|
+
<div class="px-5 pb-4 flex flex-col gap-3">
|
|
184
|
+
<p class="text-sm leading-relaxed whitespace-pre-wrap break-words">{{ post.body }}</p>
|
|
185
|
+
<MediaGrid :media="post.media" />
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<footer class="px-5 py-4 border-t border-[#EDF0F3] flex gap-4 items-center">
|
|
189
|
+
<Reactions :count="post.reactions_count" :is-reacted="post.is_reacted" />
|
|
190
|
+
<!--
|
|
191
|
+
`data-ask-block` collapses the icon + count into a single pill bone
|
|
192
|
+
during loading — otherwise the walker recurses and emits a tiny
|
|
193
|
+
media bone for the icon and a separate text bar for the number.
|
|
194
|
+
-->
|
|
195
|
+
<button
|
|
196
|
+
type="button"
|
|
197
|
+
data-ask-block
|
|
198
|
+
class="text-sm text-gray-600 rounded-lg flex gap-1.5 items-center hover:text-primary"
|
|
199
|
+
>
|
|
200
|
+
<svg class="size-4" aria-hidden="true"><!-- comment icon --></svg>
|
|
201
|
+
<span>{{ post.comments_count }}</span>
|
|
202
|
+
</button>
|
|
203
|
+
</footer>
|
|
204
|
+
</article>
|
|
205
|
+
</template>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**What this example exercises** (and what the demo on the docs site shows live):
|
|
209
|
+
|
|
210
|
+
- **Chrome capture** on the painted `<article>` — the skeleton reproduces the background, border, and radius verbatim from the real element's computed style. See [Chrome capture section](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#chrome-capture).
|
|
211
|
+
- **Divider capture** on the `<footer>`'s `border-t` — one-sided borders survive as hairlines in the skeleton.
|
|
212
|
+
- **`data-ask-block`** on action buttons for one-bone pills instead of SVG + text fragments. See [Hints](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#hints--ask-block-ask-ignore-ask-own).
|
|
213
|
+
- **Split sub-component walker** — the plugin follows DOM nodes, not Vue VNodes, so SFC boundaries are transparent.
|
|
214
|
+
- **Provider-driven theming** — switch `mode`, `theme.light`, `theme.dark`, `animation`, `speed`, `reveal` in one place. See [Theming](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#theming).
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## States: `loading`, `content`, `empty`, `error`
|
|
219
|
+
|
|
220
|
+
One component handles all four states. Resolution order is `loading` → `error` → `empty` → `content`:
|
|
221
|
+
|
|
222
|
+
```vue
|
|
223
|
+
<ASmartSkeleton
|
|
224
|
+
:data="posts"
|
|
225
|
+
:sample="SAMPLE_POSTS"
|
|
226
|
+
:error="fetchError"
|
|
227
|
+
:empty="posts?.length === 0"
|
|
228
|
+
@reveal="onContentRevealed"
|
|
229
|
+
>
|
|
230
|
+
<template #default="{ data }">
|
|
231
|
+
<PostList :posts="data" />
|
|
232
|
+
</template>
|
|
233
|
+
<template #empty>
|
|
234
|
+
<EmptyIllustration message="No posts yet" />
|
|
235
|
+
</template>
|
|
236
|
+
<template #error="{ error }">
|
|
237
|
+
<ErrorBanner :error="error" @retry="reload" />
|
|
238
|
+
</template>
|
|
239
|
+
</ASmartSkeleton>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`@reveal` fires once per `skeleton → content` transition. Full behaviour → [States demo](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#states-loading-content-empty-error).
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Hints — `ask-block`, `ask-ignore`, `ask-own`
|
|
247
|
+
|
|
248
|
+
Three attributes let you override the walker where it matters:
|
|
249
|
+
|
|
250
|
+
| Hint | Alias | What it does |
|
|
251
|
+
| ------------ | ---------------------- | -------------------------------------------------------------------------------------------- |
|
|
252
|
+
| `ask-block` | `data-skeleton-block` | Draws the element as **one** bone sized to its box + border-radius. Walker does not recurse. |
|
|
253
|
+
| `ask-ignore` | `data-skeleton-ignore` | Element keeps its layout footprint but emits **zero** bones. |
|
|
254
|
+
| `ask-own` | `data-skeleton-own` | Subtree opts out — you're drawing its skeleton yourself. |
|
|
255
|
+
|
|
256
|
+
See it in action → [Hints demo](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#hints--ask-block-ask-ignore-ask-own).
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Chrome capture
|
|
261
|
+
|
|
262
|
+
When the walker crosses a **painted container** (a background, a border on any side, or a box-shadow) it emits an extra `type: 'chrome'` bone that carries the captured `background`, per-side `border-*`, `border-radius`, and `box-shadow` inline. The bone skips the shimmer animation so it reads as an outline, not a placeholder fill.
|
|
263
|
+
|
|
264
|
+
- `backgroundColor` and gradient `backgroundImage` layers are kept. `url(…)` layers are stripped — hero bitmaps don't re-download in the skeleton.
|
|
265
|
+
- Each border side is captured separately, so a `border-t` footer divider survives as a hairline.
|
|
266
|
+
- `border-radius` is captured per-corner where the parent's overflow clips it.
|
|
267
|
+
- Chrome bones ignore `--ask-base` by design — they're reproductions, not placeholders.
|
|
268
|
+
|
|
269
|
+
Full details and before/after shots → [Chrome capture section](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#chrome-capture).
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Reveal transitions
|
|
274
|
+
|
|
275
|
+
| Mode | Behaviour |
|
|
276
|
+
| ---------- | ----------------------------------------------------------------------------- |
|
|
277
|
+
| `smart` | Instant if the content lands under ~150 ms, otherwise crossfade. The default. |
|
|
278
|
+
| `together` | Simple crossfade. |
|
|
279
|
+
| `sequence` | Staggered top-to-bottom cascade. |
|
|
280
|
+
| `sweep` | A highlight beam sweeps across the transition edge. |
|
|
281
|
+
| `instant` | Snap, no transition. |
|
|
282
|
+
|
|
283
|
+
`prefers-reduced-motion: reduce` forces `instant` automatically. Compare them side-by-side → [Reveal transitions](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#reveal-transitions).
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Theming
|
|
288
|
+
|
|
289
|
+
Every surface is driven by CSS custom properties. Set them on any ancestor:
|
|
290
|
+
|
|
291
|
+
```css
|
|
292
|
+
.my-feed {
|
|
293
|
+
--ask-base: #d1d4dc;
|
|
294
|
+
--ask-highlight: #ffffff;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
For app-wide defaults, wrap your app in `<ASmartSkeletonProvider>`:
|
|
299
|
+
|
|
300
|
+
```vue
|
|
301
|
+
<ASmartSkeletonProvider
|
|
302
|
+
mode="system"
|
|
303
|
+
:theme="{
|
|
304
|
+
light: { base: '#d1d4dc', highlight: '#ffffff' },
|
|
305
|
+
dark: { base: '#31363f', highlight: 'rgba(255,255,255,0.08)' },
|
|
306
|
+
animation: 'shimmer',
|
|
307
|
+
speed: 'slow',
|
|
308
|
+
}"
|
|
309
|
+
reveal="smart"
|
|
310
|
+
>
|
|
311
|
+
<App />
|
|
312
|
+
</ASmartSkeletonProvider>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Bones ship as `<div [ask-rect="text|media|box|chrome"]>` in the **light DOM** (no shadow root), so your stylesheet reaches them without `:deep()`. A 4-px minimum radius is applied to every non-chrome bone so nothing reads as a sharp glyph.
|
|
316
|
+
|
|
317
|
+
```css
|
|
318
|
+
/* Handcrafted ASkeletonBlock-style look on placeholder bones. */
|
|
319
|
+
[ask-rect='text'],
|
|
320
|
+
[ask-rect='box'] {
|
|
321
|
+
box-shadow: inset 0 0 0 1px rgb(169 175 188 / 0.35);
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Full CSS variable table + per-type targeting → [Theming](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#theming).
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## API reference
|
|
330
|
+
|
|
331
|
+
**`<ASmartSkeleton>`** — `data`, `sample`, `loading`, `id`, `label`, `empty`, `error`, `reveal` · emits `@reveal` · slots `default` / `empty` / `error` / `skeleton`.
|
|
332
|
+
|
|
333
|
+
**`<ASmartSkeletonProvider>`** — `theme`, `mode`, `reveal`, `debug`, `nonce`.
|
|
334
|
+
|
|
335
|
+
**`<ASmartSkeletonBox>`** — standalone shimmer bar · `width`, `height`, `radius`.
|
|
336
|
+
|
|
337
|
+
**`<ASmartSkeletonSuspense>`** — Suspense-boundary wrapper that measures a fallback skeleton.
|
|
338
|
+
|
|
339
|
+
**Author hints (HTML attrs)** — `ask-block`, `ask-ignore`, `ask-own` (each with a `data-skeleton-*` alias).
|
|
340
|
+
|
|
341
|
+
Full props / slots / events reference → [API reference](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/smart-skeleton#api-reference).
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Changelog highlights
|
|
346
|
+
|
|
347
|
+
- **Chrome capture** (`type: 'chrome'`) — reproduces card outlines pixel-for-pixel from the consumer's own paint. Partial borders (`border-t` dividers) are preserved. `url(…)` background layers are filtered out so hero bitmaps don't re-download.
|
|
348
|
+
- **Light-DOM bones** — placeholders now live in the light DOM with a `[ask-rect="text|media|box|chrome"]` attribute, so consumer stylesheets can style them per type without `:deep()` on a shadow root.
|
|
349
|
+
- **4-px minimum radius** — every non-chrome bone is floored at 4 px so sharp-cornered placeholders don't read as glyphs of the real content.
|
|
350
|
+
- **Fixed `reveal="instant"`** — previously threw `Cannot read properties of undefined (reading 'hold')`. Unknown reveal strings now fall through to `smart` instead of crashing.
|
|
351
|
+
- **Cleaned stylesheet** — the published `dist/styles.css` is now a hand-authored ~4 KB file. No Tailwind utilities, `@property` globals, or theme tokens leak into the consumer's `:root`.
|
|
352
|
+
|
|
353
|
+
See the full version history → [CHANGELOG](https://github.com/alikhalilll/ali-nuxt-toolkit/blob/main/packages/ui-components/ASmartSkeleton/CHANGELOG.md) · browse source → [GitHub](https://github.com/alikhalilll/ali-nuxt-toolkit/tree/main/packages/ui-components/ASmartSkeleton) · open an issue → [Issues](https://github.com/alikhalilll/ali-nuxt-toolkit/issues).
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
MIT © [alikhalilll](https://github.com/alikhalilll)
|