nano-skeleton 0.1.1 → 0.3.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/CHANGELOG.md +14 -0
- package/README.md +84 -10
- package/dist/Skeleton.d.ts +8 -0
- package/dist/Skeleton.js +1 -1
- package/dist/Skeleton.js.map +1 -1
- package/dist/SkeletonProvider.js +1 -1
- package/dist/SkeletonProvider.js.map +1 -1
- package/dist/constants.js +19 -5
- package/dist/constants.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- feat: add `text` attribute to force text mode for text wrapped in elements
|
|
8
|
+
|
|
9
|
+
## 0.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- feat: add `interactive` attribute to keep loading content focusable
|
|
14
|
+
|
|
15
|
+
- feat: expose `aria-busy` on loading providers
|
|
16
|
+
|
|
3
17
|
## 0.1.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -34,7 +34,8 @@ for a shimmering placeholder of the same shape.
|
|
|
34
34
|
- **Media-aware.** `<img>` and `<video>` keep their box while their content is
|
|
35
35
|
hidden.
|
|
36
36
|
- **Accessible by default.** Loading content is made `inert`, so it can't be
|
|
37
|
-
focused or clicked,
|
|
37
|
+
focused or clicked, the provider is marked `aria-busy`, and the shimmer
|
|
38
|
+
respects `prefers-reduced-motion`. Opt out per skeleton with `interactive`.
|
|
38
39
|
- **Light and dark mode** out of the box via `light-dark()`.
|
|
39
40
|
- **Works everywhere:** plain HTML, any framework, inside Shadow DOM.
|
|
40
41
|
- **No dependencies**, styles included. No CSS file to import.
|
|
@@ -100,7 +101,7 @@ all of its skeletons at once.
|
|
|
100
101
|
A `<nano-skeleton>` without a provider simply renders its content as usual.
|
|
101
102
|
|
|
102
103
|
`<nano-skeleton>` works in one of two modes, picked automatically from its
|
|
103
|
-
content.
|
|
104
|
+
content (text mode can also be forced with the `text` attribute).
|
|
104
105
|
|
|
105
106
|
### Text mode
|
|
106
107
|
|
|
@@ -129,6 +130,34 @@ placeholder text first and replace it with real data later:
|
|
|
129
130
|
<h2><nano-skeleton>Loading title…</nano-skeleton></h2>
|
|
130
131
|
```
|
|
131
132
|
|
|
133
|
+
#### Text wrapped in elements
|
|
134
|
+
|
|
135
|
+
Automatic detection only looks at text placed directly inside the skeleton.
|
|
136
|
+
When the text is wrapped in an inline element, like a link or `<strong>`, add
|
|
137
|
+
`text` to force text mode:
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<p>
|
|
141
|
+
<nano-skeleton text>
|
|
142
|
+
<a href="/posts/42">Read the full post</a>
|
|
143
|
+
</nano-skeleton>
|
|
144
|
+
</p>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The placeholder follows the text lines exactly like plain text does. The
|
|
148
|
+
wrapping elements should be inline: a block or `inline-block` child gets a
|
|
149
|
+
single bar for its whole box.
|
|
150
|
+
|
|
151
|
+
Text decorations, such as a link's underline, stay visible while loading, so the
|
|
152
|
+
placeholder still reads as a link. To hide them too, wrap the link's content in
|
|
153
|
+
a `<span>`:
|
|
154
|
+
|
|
155
|
+
```html
|
|
156
|
+
<nano-skeleton text>
|
|
157
|
+
<a href="/posts/42"><span>Read the full post</span></a>
|
|
158
|
+
</nano-skeleton>
|
|
159
|
+
```
|
|
160
|
+
|
|
132
161
|
### Element mode
|
|
133
162
|
|
|
134
163
|
If the skeleton contains elements, it becomes `display: contents` (it doesn't
|
|
@@ -156,6 +185,38 @@ Because the placeholder is just the element's own box, make sure it has a size
|
|
|
156
185
|
while loading, e.g. `width`/`height` attributes or `aspect-ratio` on images, or
|
|
157
186
|
a fixed size on containers.
|
|
158
187
|
|
|
188
|
+
The content is hidden with `opacity: 0`, so if an element inside has a
|
|
189
|
+
`transition` on `opacity` (or `transition: all`), it fades in and out when
|
|
190
|
+
`loading` changes instead of switching instantly.
|
|
191
|
+
|
|
192
|
+
### Interactive skeletons
|
|
193
|
+
|
|
194
|
+
By default, a loading skeleton is `inert`: its content can't be focused, clicked
|
|
195
|
+
or reached by assistive technology. That's usually what you want, but sometimes
|
|
196
|
+
the content is usable before the data arrives, e.g. a link whose `href` is
|
|
197
|
+
already known, or a button that doesn't depend on the data.
|
|
198
|
+
|
|
199
|
+
It matters most when content goes back into the loading state (a background
|
|
200
|
+
refetch, pagination): an `inert` element that had focus loses it, and keyboard
|
|
201
|
+
users are sent back to the top of the page.
|
|
202
|
+
|
|
203
|
+
Add `interactive` to keep the content focusable and clickable while loading:
|
|
204
|
+
|
|
205
|
+
```html
|
|
206
|
+
<nano-skeleton-provider loading>
|
|
207
|
+
<nano-skeleton interactive>
|
|
208
|
+
<a href="/posts/42" class="card__link">
|
|
209
|
+
<span>Placeholder post title</span>
|
|
210
|
+
</a>
|
|
211
|
+
</nano-skeleton>
|
|
212
|
+
</nano-skeleton-provider>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The placeholder looks the same, but the content stays in the tab order and
|
|
216
|
+
keeps its accessible name. Make sure that name makes sense while loading, as screen reader users
|
|
217
|
+
will hear it. The provider sets `aria-busy="true"` while loading to tell them
|
|
218
|
+
the region is still updating.
|
|
219
|
+
|
|
159
220
|
## Examples
|
|
160
221
|
|
|
161
222
|
### A card
|
|
@@ -291,7 +352,13 @@ import type { DetailedHTMLProps, HTMLAttributes } from "react";
|
|
|
291
352
|
declare module "react" {
|
|
292
353
|
namespace JSX {
|
|
293
354
|
interface IntrinsicElements {
|
|
294
|
-
[COMPONENT_NAME]: DetailedHTMLProps<
|
|
355
|
+
[COMPONENT_NAME]: DetailedHTMLProps<
|
|
356
|
+
HTMLAttributes<Skeleton>,
|
|
357
|
+
Skeleton
|
|
358
|
+
> & {
|
|
359
|
+
interactive?: boolean;
|
|
360
|
+
text?: boolean;
|
|
361
|
+
};
|
|
295
362
|
[PROVIDER_NAME]: DetailedHTMLProps<
|
|
296
363
|
HTMLAttributes<SkeletonProvider>,
|
|
297
364
|
SkeletonProvider
|
|
@@ -316,16 +383,23 @@ Registers `<nano-skeleton-provider>` and `<nano-skeleton>` with
|
|
|
316
383
|
| -------------------- | --------- | --------------------------------------------------------------------- |
|
|
317
384
|
| `loading` | `boolean` | When present/`true`, all descendant skeletons render as placeholders. |
|
|
318
385
|
|
|
386
|
+
While loading, the provider is exposed to assistive technology as
|
|
387
|
+
`aria-busy="true"`. An `aria-busy` attribute you set yourself takes precedence.
|
|
388
|
+
|
|
319
389
|
### `<nano-skeleton>`
|
|
320
390
|
|
|
321
|
-
|
|
322
|
-
|
|
391
|
+
| Attribute / property | Type | Description |
|
|
392
|
+
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------------- |
|
|
393
|
+
| `interactive` | `boolean` | Keeps the content focusable and clickable while loading. See [Interactive skeletons](#interactive-skeletons). |
|
|
394
|
+
| `text` | `boolean` | Forces text mode, e.g. for text inside a link. See [Text wrapped in elements](#text-wrapped-in-elements). |
|
|
395
|
+
|
|
396
|
+
It also exposes these state attributes, which you can use as styling hooks:
|
|
323
397
|
|
|
324
|
-
| Attribute | Set when
|
|
325
|
-
| -------------- |
|
|
326
|
-
| `data-loading` | The nearest provider is loading.
|
|
327
|
-
| `data-text` | The skeleton directly contains text (text mode).
|
|
328
|
-
| `inert` | The nearest provider is loading. Blocks focus and clicks. |
|
|
398
|
+
| Attribute | Set when |
|
|
399
|
+
| -------------- | ------------------------------------------------------------------------------------- |
|
|
400
|
+
| `data-loading` | The nearest provider is loading. |
|
|
401
|
+
| `data-text` | The skeleton directly contains text, or `text` is set (text mode). |
|
|
402
|
+
| `inert` | The nearest provider is loading and `interactive` isn't set. Blocks focus and clicks. |
|
|
329
403
|
|
|
330
404
|
### Exports
|
|
331
405
|
|
package/dist/Skeleton.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
//#region src/Skeleton.d.ts
|
|
2
2
|
export declare class Skeleton extends HTMLElement {
|
|
3
3
|
#private;
|
|
4
|
+
static observedAttributes: string[];
|
|
5
|
+
/** Keeps the content focusable and clickable while loading */
|
|
6
|
+
get interactive(): boolean;
|
|
7
|
+
set interactive(value: boolean);
|
|
8
|
+
/** Forces text mode, even when the text is wrapped in elements */
|
|
9
|
+
get text(): boolean;
|
|
10
|
+
set text(value: boolean);
|
|
11
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
4
12
|
connectedCallback(): void;
|
|
5
13
|
disconnectedCallback(): void;
|
|
6
14
|
}
|
package/dist/Skeleton.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{SKELETON_CONTEXT as e}from"./constants.js";import{ContextRequestEvent as t}from"./ContextRequestEvent.js";import{adoptStyles as n}from"./styles.js";function r(e){return`adoptedStyleSheets`in e}var i=class extends HTMLElement{
|
|
1
|
+
import{SKELETON_CONTEXT as e}from"./constants.js";import{ContextRequestEvent as t}from"./ContextRequestEvent.js";import{adoptStyles as n}from"./styles.js";function r(e){return`adoptedStyleSheets`in e}var i=class extends HTMLElement{static observedAttributes=[`interactive`,`text`];#e=!1;#t;#n=new MutationObserver(()=>this.#r());get interactive(){return this.hasAttribute(`interactive`)}set interactive(e){this.toggleAttribute(`interactive`,!!e)}get text(){return this.hasAttribute(`text`)}set text(e){this.toggleAttribute(`text`,!!e)}attributeChangedCallback(e,t,n){t!==n&&(e===`text`?this.#r():this.#i(this.#e))}connectedCallback(){let i=this.getRootNode();r(i)&&n(i),this.#r(),this.#i(!1),this.dispatchEvent(new t(e,(e,t)=>{t&&(this.#t=t),this.#i(e)},!0)),this.#n.observe(this,{childList:!0,characterData:!0,subtree:!0})}disconnectedCallback(){this.#t?.(),this.#t=void 0,this.#n.disconnect()}#r(){let e=this.text;for(let t of this.childNodes){if(e)break;t instanceof Text&&t.data.trim()!==``&&(e=!0)}this.toggleAttribute(`data-text`,e)}#i(e){this.#e=e,this.toggleAttribute(`inert`,e&&!this.interactive),this.toggleAttribute(`data-loading`,e)}};export{i as Skeleton};
|
|
2
2
|
//# sourceMappingURL=Skeleton.js.map
|
package/dist/Skeleton.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Skeleton.js","names":["isDocumentOrShadowRoot","node","Skeleton","#update","
|
|
1
|
+
{"version":3,"file":"Skeleton.js","names":["isDocumentOrShadowRoot","node","Skeleton","#update","value","oldValue","newValue","name","#render","#loading","root","adoptStyles","ContextRequestEvent","SKELETON_CONTEXT","unsubscribe","#unsubscribe","loading","#observer","isText"],"sources":["../src/Skeleton.ts"],"sourcesContent":["import { SKELETON_CONTEXT } from \"#root/constants.js\";\nimport { ContextRequestEvent } from \"#root/ContextRequestEvent.js\";\nimport { adoptStyles } from \"#root/styles.js\";\nimport { type Unsubscribe } from \"#root/types.js\";\n\nfunction isDocumentOrShadowRoot(\n node: Node | Document | ShadowRoot,\n): node is Document | ShadowRoot {\n return \"adoptedStyleSheets\" in node;\n}\n\nexport class Skeleton extends HTMLElement {\n static observedAttributes = [\"interactive\", \"text\"];\n\n #loading = false;\n #unsubscribe?: Unsubscribe | undefined;\n #observer = new MutationObserver(() => this.#update());\n\n /** Keeps the content focusable and clickable while loading */\n get interactive() {\n return this.hasAttribute(\"interactive\");\n }\n\n set interactive(value: boolean) {\n this.toggleAttribute(\"interactive\", Boolean(value));\n }\n\n /** Forces text mode, even when the text is wrapped in elements */\n get text() {\n return this.hasAttribute(\"text\");\n }\n\n set text(value: boolean) {\n this.toggleAttribute(\"text\", Boolean(value));\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue) return;\n if (name === \"text\") this.#update();\n else this.#render(this.#loading);\n }\n\n connectedCallback() {\n const root = this.getRootNode();\n\n if (isDocumentOrShadowRoot(root)) {\n adoptStyles(root);\n }\n\n this.#update();\n this.#render(false);\n\n this.dispatchEvent(\n new ContextRequestEvent(\n SKELETON_CONTEXT,\n (loading, unsubscribe) => {\n if (unsubscribe) this.#unsubscribe = unsubscribe;\n this.#render(loading);\n },\n true,\n ),\n );\n\n this.#observer.observe(this, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n }\n\n disconnectedCallback() {\n this.#unsubscribe?.();\n this.#unsubscribe = undefined;\n this.#observer.disconnect();\n }\n\n #update() {\n let isText = this.text;\n for (const node of this.childNodes) {\n if (isText) break;\n if (node instanceof Text && node.data.trim() !== \"\") {\n isText = true;\n }\n }\n this.toggleAttribute(\"data-text\", isText);\n }\n\n #render(loading: boolean) {\n this.#loading = loading;\n this.toggleAttribute(\"inert\", loading && !this.interactive);\n this.toggleAttribute(\"data-loading\", loading);\n }\n}\n"],"mappings":"A,2JAKA,SAASA,EACP,EAC+B,CAC/B,MAAO,uBAAwBC,CACjC,CAEA,IAAaC,EAAb,cAA8B,WAAY,CACxC,OAAO,mBAAqB,CAAC,cAAe,MAAM,EAElD,GAAW,GACX,GACA,GAAY,IAAI,qBAAuB,KAAKC,GAAQ,CAAC,EAGrD,IAAI,aAAc,CAChB,OAAO,KAAK,aAAa,aAAa,CACxC,CAEA,IAAI,YAAY,EAAgB,CAC9B,KAAK,gBAAgB,cAAe,EAAQC,CAAM,CACpD,CAGA,IAAI,MAAO,CACT,OAAO,KAAK,aAAa,MAAM,CACjC,CAEA,IAAI,KAAK,EAAgB,CACvB,KAAK,gBAAgB,OAAQ,EAAQA,CAAM,CAC7C,CAEA,yBACE,EACA,EACA,EACA,CACIC,IAAaC,IACbC,IAAS,OAAQ,KAAKJ,GAAQ,EAC7B,KAAKK,GAAQ,KAAKC,EAAQ,EACjC,CAEA,mBAAoB,CAClB,IAAMC,EAAO,KAAK,YAAY,EAE1BV,EAAuBU,CAAI,GAC7BC,EAAYD,CAAI,EAGlB,KAAKP,GAAQ,EACb,KAAKK,GAAQ,EAAK,EAElB,KAAK,cACH,IAAII,EACFC,GACC,EAAS,IAAgB,CACpBC,IAAa,KAAKC,GAAeD,GACrC,KAAKN,GAAQQ,CAAO,CACtB,EACA,EACF,CACF,EAEA,KAAKC,GAAU,QAAQ,KAAM,CAC3B,UAAW,GACX,cAAe,GACf,QAAS,EACX,CAAC,CACH,CAEA,sBAAuB,CACrB,KAAKF,KAAe,EACpB,KAAKA,GAAe,IAAA,GACpB,KAAKE,GAAU,WAAW,CAC5B,CAEA,IAAU,CACR,IAAIC,EAAS,KAAK,KAClB,IAAK,IAAMjB,KAAQ,KAAK,WAAY,CAClC,GAAIiB,EAAQ,MACRjB,aAAgB,MAAQA,EAAK,KAAK,KAAK,IAAM,KAC/C,EAAS,GAEb,CACA,KAAK,gBAAgB,YAAaiB,CAAM,CAC1C,CAEA,GAAQ,EAAkB,CACxB,KAAKT,GAAWO,EAChB,KAAK,gBAAgB,QAASA,GAAW,CAAC,KAAK,WAAW,EAC1D,KAAK,gBAAgB,eAAgBA,CAAO,CAC9C,CACF,E"}
|
package/dist/SkeletonProvider.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{SKELETON_CONTEXT as e}from"./constants.js";var t=class extends HTMLElement{static observedAttributes=[`loading`];#e=/* @__PURE__ */ new Set;constructor(){super(),this.addEventListener(`context-request`,t=>{if(t.context!==e||typeof t.callback!=`function`)return;t.stopPropagation();let{callback:n}=t;t.subscribe?(this.#e.add(n),n(this.loading,()=>this.#e.delete(n))):n(this.loading)})}get loading(){return this.hasAttribute(`loading`)}set loading(e){this.toggleAttribute(`loading`,!!e)}attributeChangedCallback(e,t,n){if(t!==n)for(let e of this.#e)e(this.loading)}};export{t as SkeletonProvider};
|
|
1
|
+
import{SKELETON_CONTEXT as e}from"./constants.js";var t=class extends HTMLElement{static observedAttributes=[`loading`];#e=/* @__PURE__ */ new Set;#t=this.attachInternals();constructor(){super(),this.addEventListener(`context-request`,t=>{if(t.context!==e||typeof t.callback!=`function`)return;t.stopPropagation();let{callback:n}=t;t.subscribe?(this.#e.add(n),n(this.loading,()=>this.#e.delete(n))):n(this.loading)})}get loading(){return this.hasAttribute(`loading`)}set loading(e){this.toggleAttribute(`loading`,!!e)}attributeChangedCallback(e,t,n){if(t!==n){this.#t.ariaBusy=this.loading?`true`:null;for(let e of this.#e)e(this.loading)}}};export{t as SkeletonProvider};
|
|
2
2
|
//# sourceMappingURL=SkeletonProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SkeletonProvider.js","names":["SkeletonProvider","event","SKELETON_CONTEXT","#subscribers","callback","value","oldValue","newValue"],"sources":["../src/SkeletonProvider.ts"],"sourcesContent":["import type { ContextRequestEvent } from \"#root/ContextRequestEvent.js\";\nimport type { LoadingCallback } from \"#root/types.js\";\n\nimport { SKELETON_CONTEXT } from \"#root/constants.js\";\n\nexport class SkeletonProvider extends HTMLElement {\n static observedAttributes = [\"loading\"];\n\n #subscribers = new Set<LoadingCallback>();\n\n constructor() {\n super();\n /**\n * Adding listener from the constructor so the provider is ready before any\n * descendant connects and asks for context.\n */\n this.addEventListener(\"context-request\", (event: ContextRequestEvent) => {\n if (\n event.context !== SKELETON_CONTEXT ||\n typeof event.callback !== \"function\"\n )\n return;\n\n /** The nearest provider wins */\n event.stopPropagation();\n\n const { callback } = event;\n\n if (event.subscribe) {\n this.#subscribers.add(callback);\n callback(this.loading, () => this.#subscribers.delete(callback));\n } else {\n callback(this.loading);\n }\n });\n }\n\n get loading() {\n return this.hasAttribute(\"loading\");\n }\n\n set loading(value: boolean) {\n this.toggleAttribute(\"loading\", Boolean(value));\n }\n\n attributeChangedCallback(\n _name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue) return;\n for (const callback of this.#subscribers) callback(this.loading);\n }\n}\n"],"mappings":"A,kDAKA,IAAaA,EAAb,cAAsC,WAAY,CAChD,OAAO,mBAAqB,CAAC,SAAS,EAEtC,mBAAe,IAAI,
|
|
1
|
+
{"version":3,"file":"SkeletonProvider.js","names":["SkeletonProvider","event","SKELETON_CONTEXT","#subscribers","callback","value","oldValue","newValue","#internals"],"sources":["../src/SkeletonProvider.ts"],"sourcesContent":["import type { ContextRequestEvent } from \"#root/ContextRequestEvent.js\";\nimport type { LoadingCallback } from \"#root/types.js\";\n\nimport { SKELETON_CONTEXT } from \"#root/constants.js\";\n\nexport class SkeletonProvider extends HTMLElement {\n static observedAttributes = [\"loading\"];\n\n #subscribers = new Set<LoadingCallback>();\n #internals = this.attachInternals();\n\n constructor() {\n super();\n /**\n * Adding listener from the constructor so the provider is ready before any\n * descendant connects and asks for context.\n */\n this.addEventListener(\"context-request\", (event: ContextRequestEvent) => {\n if (\n event.context !== SKELETON_CONTEXT ||\n typeof event.callback !== \"function\"\n )\n return;\n\n /** The nearest provider wins */\n event.stopPropagation();\n\n const { callback } = event;\n\n if (event.subscribe) {\n this.#subscribers.add(callback);\n callback(this.loading, () => this.#subscribers.delete(callback));\n } else {\n callback(this.loading);\n }\n });\n }\n\n get loading() {\n return this.hasAttribute(\"loading\");\n }\n\n set loading(value: boolean) {\n this.toggleAttribute(\"loading\", Boolean(value));\n }\n\n attributeChangedCallback(\n _name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue) return;\n /**\n * Via internals, so an aria-busy attribute set by the user still takes\n * precedence.\n */\n this.#internals.ariaBusy = this.loading ? \"true\" : null;\n for (const callback of this.#subscribers) callback(this.loading);\n }\n}\n"],"mappings":"A,kDAKA,IAAaA,EAAb,cAAsC,WAAY,CAChD,OAAO,mBAAqB,CAAC,SAAS,EAEtC,mBAAe,IAAI,IACnB,GAAa,KAAK,gBAAgB,EAElC,aAAc,CACZ,MAAM,EAKN,KAAK,iBAAiB,kBAAoB,GAA+B,CACvE,GACEC,EAAM,UAAYC,GAClB,OAAOD,EAAM,UAAa,WAE1B,OAGFA,EAAM,gBAAgB,EAEtB,GAAM,CAAE,YAAaA,EAEjBA,EAAM,WACR,KAAKE,GAAa,IAAIC,CAAQ,EAC9BA,EAAS,KAAK,YAAe,KAAKD,GAAa,OAAOC,CAAQ,CAAC,GAE/DA,EAAS,KAAK,OAAO,CAEzB,CAAC,CACH,CAEA,IAAI,SAAU,CACZ,OAAO,KAAK,aAAa,SAAS,CACpC,CAEA,IAAI,QAAQ,EAAgB,CAC1B,KAAK,gBAAgB,UAAW,EAAQC,CAAM,CAChD,CAEA,yBACE,EACA,EACA,EACA,CACA,GAAIC,IAAaC,EAKjB,MAAKC,GAAW,SAAW,KAAK,QAAU,OAAS,KACnD,IAAK,IAAMJ,KAAY,KAAKD,GAAcC,EAAS,KAAK,OAAO,CADZ,CAErD,CACF,E"}
|
package/dist/constants.js
CHANGED
|
@@ -21,10 +21,11 @@ const e=`nano-skeleton`,t=`${e}-provider`,{default:n}={default:`:scope {
|
|
|
21
21
|
|
|
22
22
|
/*
|
|
23
23
|
* Everything inside the root should disappear, but nothing jumps,
|
|
24
|
-
* as layout is intact.
|
|
24
|
+
* as layout is intact. Hidden visually only: focus and accessibility are
|
|
25
|
+
* handled by inert, so interactive skeletons keep both.
|
|
25
26
|
*/
|
|
26
|
-
:scope[data-loading] > *
|
|
27
|
-
|
|
27
|
+
:scope[data-loading] > * > * {
|
|
28
|
+
opacity: 0 !important;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -37,13 +38,26 @@ const e=`nano-skeleton`,t=`${e}-provider`,{default:n}={default:`:scope {
|
|
|
37
38
|
var(--nano-skeleton-text-border-radius-default)
|
|
38
39
|
);
|
|
39
40
|
box-decoration-break: clone;
|
|
40
|
-
text-box
|
|
41
|
-
text-box-trim: trim-both;
|
|
41
|
+
text-box: cap alphabetic;
|
|
42
42
|
/**
|
|
43
43
|
* This ensures the ellipsis on truncated text is also hidden.
|
|
44
44
|
* -webkit-text-fill-color overrides any "color" property that is also set.
|
|
45
45
|
*/
|
|
46
46
|
-webkit-text-fill-color: transparent;
|
|
47
|
+
/**
|
|
48
|
+
* Text might get visible when selected
|
|
49
|
+
*/
|
|
50
|
+
user-select: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Children and grandchildren need to inherit these styles
|
|
55
|
+
* for consistent outlines
|
|
56
|
+
*/
|
|
57
|
+
:scope[data-loading][data-text] > *,
|
|
58
|
+
:scope[data-loading][data-text] > * > * {
|
|
59
|
+
border-radius: inherit;
|
|
60
|
+
text-box: inherit;
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
:scope[data-loading] > :is(video, img):only-child {
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":["COMPONENT_NAME","PROVIDER_NAME","CSS_CONTENT","SKELETON_CONTEXT"],"sources":["../src/constants.ts"],"sourcesContent":["const isDev = import.meta.env.MODE !== \"production\";\n\nexport const COMPONENT_NAME = \"nano-skeleton\" as const;\n\nexport const PROVIDER_NAME = `${COMPONENT_NAME}-provider` as const;\n\nconst { default: CSS_CONTENT } = isDev\n ? await import(\"./styles.css?inline\")\n : { default: `__CSS_CONTENT__` };\n\nexport { CSS_CONTENT };\n\nexport const SKELETON_CONTEXT = Symbol.for(`${COMPONENT_NAME}-context`);\n"],"mappings":"AAEA,MAAaA,EAAiB,gBAEjBC,EAAgB,GAAGD,EAAe,WAEzC,CAAE,QAASE,GAEb,CAAE,QAAS
|
|
1
|
+
{"version":3,"file":"constants.js","names":["COMPONENT_NAME","PROVIDER_NAME","CSS_CONTENT","SKELETON_CONTEXT"],"sources":["../src/constants.ts"],"sourcesContent":["const isDev = import.meta.env.MODE !== \"production\";\n\nexport const COMPONENT_NAME = \"nano-skeleton\" as const;\n\nexport const PROVIDER_NAME = `${COMPONENT_NAME}-provider` as const;\n\nconst { default: CSS_CONTENT } = isDev\n ? await import(\"./styles.css?inline\")\n : { default: `__CSS_CONTENT__` };\n\nexport { CSS_CONTENT };\n\nexport const SKELETON_CONTEXT = Symbol.for(`${COMPONENT_NAME}-context`);\n"],"mappings":"AAEA,MAAaA,EAAiB,gBAEjBC,EAAgB,GAAGD,EAAe,WAEzC,CAAE,QAASE,GAEb,CAAE,QAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgGb,EAIWC,EAAmB,OAAO,IAAI,GAAGH,EAAe,SAAS,E"}
|