@camp.dev/bones 0.3.0 → 0.4.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/README.md +21 -15
- package/dist/css/auto.css +30 -27
- package/dist/css/bones.css +68 -27
- package/dist/element/overlay.mjs +5 -5
- package/dist/react/create-bones.mjs +2 -6
- package/dist/react/index.d.mts +1 -2
- package/dist/react/index.mjs +1 -2
- package/package.json +1 -1
- package/src/css/auto.css +30 -27
- package/src/css/bones.css +68 -27
- package/dist/react/bones.d.mts +0 -15
- package/dist/react/bones.mjs +0 -27
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Bones skips the duplication. You write your markup once and it handles both stat
|
|
|
14
14
|
|
|
15
15
|
- Works in Server Components. No hooks, no context, no `'use client'`.
|
|
16
16
|
- One component handles both loading and loaded states.
|
|
17
|
-
- Pass a promise
|
|
17
|
+
- Pass data or a promise. A pending promise suspends to your `<Suspense>` boundary; `forceBones` renders the skeleton.
|
|
18
18
|
- Skeletons are pure CSS, themed with custom properties.
|
|
19
19
|
- Loading elements get `aria-busy="true"` automatically.
|
|
20
20
|
|
|
@@ -34,7 +34,7 @@ import "@camp.dev/bones/css";
|
|
|
34
34
|
|
|
35
35
|
| Import | Contents |
|
|
36
36
|
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
37
|
-
| `@camp.dev/bones/react` | `createBones`, `readPromise`, `forceBones`, `minMax`,
|
|
37
|
+
| `@camp.dev/bones/react` | `createBones`, `readPromise`, `forceBones`, `minMax`, `isMinMax`, `BonesBoundary` |
|
|
38
38
|
| `@camp.dev/bones/css` | The skeleton stylesheet. Import once in your root layout. |
|
|
39
39
|
| `@camp.dev/bones/auto.css` | Skeletonizes unmarked leaves under `aria-busy="true"`. Imports the base stylesheet itself, so a separate `/css` import is optional. |
|
|
40
40
|
| `@camp.dev/bones/element` | `<bones-boundary>`, a custom element that sets `aria-busy` and `inert` on its subtree with `delay`, `min-duration`, and a crossfade. `precision="measured"` draws pixel-accurate per-line bones measured from the content. |
|
|
@@ -65,21 +65,23 @@ function ProfileCard({ user }: { user: Promise<User> | User }) {
|
|
|
65
65
|
}
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
Pass the promise to the component, and reuse the same component with `forceBones` as the fallback:
|
|
69
69
|
|
|
70
70
|
```tsx
|
|
71
|
-
import {
|
|
71
|
+
import { forceBones } from "@camp.dev/bones/react";
|
|
72
|
+
import { Suspense } from "react";
|
|
72
73
|
|
|
73
74
|
export default function Page() {
|
|
75
|
+
const user = fetchUser();
|
|
74
76
|
return (
|
|
75
|
-
<
|
|
76
|
-
<ProfileCard user={
|
|
77
|
-
</
|
|
77
|
+
<Suspense fallback={<ProfileCard user={forceBones} />}>
|
|
78
|
+
<ProfileCard user={user} />
|
|
79
|
+
</Suspense>
|
|
78
80
|
);
|
|
79
81
|
}
|
|
80
82
|
```
|
|
81
83
|
|
|
82
|
-
While the promise is pending,
|
|
84
|
+
While the promise is pending, the fallback renders `<ProfileCard>` with skeletons visible. Once it resolves, the real content swaps in. There is no separate skeleton component to keep in sync — the fallback is the component.
|
|
83
85
|
|
|
84
86
|
## Bone types
|
|
85
87
|
|
|
@@ -99,17 +101,21 @@ import { createBones, forceBones } from "@camp.dev/bones/react";
|
|
|
99
101
|
<ProfileCard user={forceBones} />;
|
|
100
102
|
```
|
|
101
103
|
|
|
102
|
-
To force
|
|
104
|
+
To force a subtree into skeleton mode at once, pass `forceBones` to each component in it:
|
|
103
105
|
|
|
104
106
|
```tsx
|
|
105
|
-
|
|
107
|
+
<ProfileCard user={forceBones} />
|
|
108
|
+
<PostList posts={forceBones} />
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`forceBones` only forces the component it's passed to. A component that derives child props from its own data must forward it itself, such as `PostList` mapping items into cards. `repeat` yields `undefined` for items that don't exist yet:
|
|
106
112
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<PostList />
|
|
110
|
-
</BonesForce>;
|
|
113
|
+
```tsx
|
|
114
|
+
<PostCard key={item?.id ?? i} post={item ?? forceBones} />
|
|
111
115
|
```
|
|
112
116
|
|
|
117
|
+
For content that has no bone markup at all, wrap it in [`<bones-boundary force>`](https://github.com/campdotdev/bones/blob/main/apps/docs/content/docs/api/bones-boundary.mdx) and let `auto.css` draw leaf bones.
|
|
118
|
+
|
|
113
119
|
## Automatic skeletons
|
|
114
120
|
|
|
115
121
|
For markup you haven't wired up with `bone()` — third-party components, server-rendered HTML, anything without explicit attributes — import the auto stylesheet. It imports `/css` itself, so this one file is a complete setup:
|
|
@@ -129,7 +135,7 @@ Set `aria-busy="true"` on the loading region and every unmarked leaf inside it b
|
|
|
129
135
|
|
|
130
136
|
`[data-bones-auto="off"]` opts a subtree out — useful for a status message you want to stay readable while its container skeletonizes. Explicit `data-bone` markup is left alone; `auto.css` only styles elements neither `bone()` nor a manual `data-bone` attribute has already claimed.
|
|
131
137
|
|
|
132
|
-
Auto rules live in `@layer bones-auto`, so any page CSS that sets `color` on an element outranks the bone's transparent text, and that text stays visible over its skeleton bar. `data-bone-animate`
|
|
138
|
+
Auto rules live in `@layer bones-auto`, so any page CSS that sets `color` on an element outranks the bone's transparent text, and that text stays visible over its skeleton bar. `data-bone-animate` works on the `aria-busy` element itself or on any ancestor. The `data-bone-animate` overrides rely on `@scope`. In a browser without `@scope`, every bone shimmers, and `data-bone-animate="pulse"` and `"none"` cannot change that. The `prefers-reduced-motion` fallback to pulse still applies.
|
|
133
139
|
|
|
134
140
|
## Without React
|
|
135
141
|
|
package/dist/css/auto.css
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
select *,
|
|
45
45
|
object *
|
|
46
46
|
) {
|
|
47
|
-
color:
|
|
47
|
+
color: rgb(from currentcolor r g b / 0);
|
|
48
48
|
position: relative;
|
|
49
49
|
min-width: 4ch;
|
|
50
50
|
min-height: 1lh;
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
progress,
|
|
145
145
|
meter
|
|
146
146
|
):not([data-bone], [data-bone] *, [data-bones-auto="off"], [data-bones-auto="off"] *) {
|
|
147
|
-
color:
|
|
147
|
+
color: rgb(from currentcolor r g b / 0);
|
|
148
148
|
background-color: var(--bone-base);
|
|
149
149
|
border-radius: var(--bone-radius);
|
|
150
150
|
border-color: transparent;
|
|
@@ -283,10 +283,13 @@
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
@layer bones-auto {
|
|
286
|
-
/* Auto bones default to shimmer. bones.css
|
|
287
|
-
[data-bone
|
|
288
|
-
the default
|
|
289
|
-
that
|
|
286
|
+
/* Auto bones default to shimmer. bones.css has its own default, but its
|
|
287
|
+
rules match [data-bone] elements only and never the unmarked leaves
|
|
288
|
+
targeted here, so the default is restated for them, and the scopes below
|
|
289
|
+
mirror bones.css for pages that set the attribute (BON-16). In those
|
|
290
|
+
scopes, :is(:scope, :scope *) lets the attribute
|
|
291
|
+
sit on the aria-busy element itself: a scoped selector's implicit :scope
|
|
292
|
+
prefix matches strict descendants only (BON-17). */
|
|
290
293
|
[aria-busy="true"]
|
|
291
294
|
:not(:has(*)):not(
|
|
292
295
|
[data-bone],
|
|
@@ -374,7 +377,7 @@
|
|
|
374
377
|
}
|
|
375
378
|
|
|
376
379
|
@scope ([data-bone-animate="shimmer"]) to ([data-bone-animate]:not([data-bone-animate="shimmer"])) {
|
|
377
|
-
[aria-busy="true"]
|
|
380
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
378
381
|
:not(:has(*)):not(
|
|
379
382
|
[data-bone],
|
|
380
383
|
[data-bone] *,
|
|
@@ -403,7 +406,7 @@
|
|
|
403
406
|
select *,
|
|
404
407
|
object *
|
|
405
408
|
)::before,
|
|
406
|
-
[aria-busy="true"]
|
|
409
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
407
410
|
:not(:has(*)):not(
|
|
408
411
|
[data-bone],
|
|
409
412
|
[data-bone] *,
|
|
@@ -432,7 +435,7 @@
|
|
|
432
435
|
select *,
|
|
433
436
|
object *
|
|
434
437
|
)::after,
|
|
435
|
-
[aria-busy="true"]
|
|
438
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
436
439
|
:is(
|
|
437
440
|
img,
|
|
438
441
|
svg,
|
|
@@ -461,7 +464,7 @@
|
|
|
461
464
|
}
|
|
462
465
|
|
|
463
466
|
@media (prefers-reduced-motion: reduce) {
|
|
464
|
-
[aria-busy="true"]
|
|
467
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
465
468
|
:not(:has(*)):not(
|
|
466
469
|
[data-bone],
|
|
467
470
|
[data-bone] *,
|
|
@@ -490,7 +493,7 @@
|
|
|
490
493
|
select *,
|
|
491
494
|
object *
|
|
492
495
|
)::before,
|
|
493
|
-
[aria-busy="true"]
|
|
496
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
494
497
|
:not(:has(*)):not(
|
|
495
498
|
[data-bone],
|
|
496
499
|
[data-bone] *,
|
|
@@ -519,7 +522,7 @@
|
|
|
519
522
|
select *,
|
|
520
523
|
object *
|
|
521
524
|
)::after,
|
|
522
|
-
[aria-busy="true"]
|
|
525
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
523
526
|
:is(
|
|
524
527
|
img,
|
|
525
528
|
svg,
|
|
@@ -544,7 +547,7 @@
|
|
|
544
547
|
}
|
|
545
548
|
|
|
546
549
|
@media (forced-colors: active) {
|
|
547
|
-
[aria-busy="true"]
|
|
550
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
548
551
|
:not(:has(*)):not(
|
|
549
552
|
[data-bone],
|
|
550
553
|
[data-bone] *,
|
|
@@ -573,7 +576,7 @@
|
|
|
573
576
|
select *,
|
|
574
577
|
object *
|
|
575
578
|
)::after,
|
|
576
|
-
[aria-busy="true"]
|
|
579
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
577
580
|
:is(
|
|
578
581
|
img,
|
|
579
582
|
svg,
|
|
@@ -598,7 +601,7 @@
|
|
|
598
601
|
}
|
|
599
602
|
|
|
600
603
|
@scope ([data-bone-animate="pulse"]) to ([data-bone-animate]:not([data-bone-animate="pulse"])) {
|
|
601
|
-
[aria-busy="true"]
|
|
604
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
602
605
|
:not(:has(*)):not(
|
|
603
606
|
[data-bone],
|
|
604
607
|
[data-bone] *,
|
|
@@ -627,7 +630,7 @@
|
|
|
627
630
|
select *,
|
|
628
631
|
object *
|
|
629
632
|
)::before,
|
|
630
|
-
[aria-busy="true"]
|
|
633
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
631
634
|
:not(:has(*)):not(
|
|
632
635
|
[data-bone],
|
|
633
636
|
[data-bone] *,
|
|
@@ -656,7 +659,7 @@
|
|
|
656
659
|
select *,
|
|
657
660
|
object *
|
|
658
661
|
)::after,
|
|
659
|
-
[aria-busy="true"]
|
|
662
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
660
663
|
:is(
|
|
661
664
|
img,
|
|
662
665
|
svg,
|
|
@@ -680,7 +683,7 @@
|
|
|
680
683
|
}
|
|
681
684
|
|
|
682
685
|
@media (prefers-reduced-motion: reduce) {
|
|
683
|
-
[aria-busy="true"]
|
|
686
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
684
687
|
:not(:has(*)):not(
|
|
685
688
|
[data-bone],
|
|
686
689
|
[data-bone] *,
|
|
@@ -709,7 +712,7 @@
|
|
|
709
712
|
select *,
|
|
710
713
|
object *
|
|
711
714
|
)::before,
|
|
712
|
-
[aria-busy="true"]
|
|
715
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
713
716
|
:not(:has(*)):not(
|
|
714
717
|
[data-bone],
|
|
715
718
|
[data-bone] *,
|
|
@@ -738,7 +741,7 @@
|
|
|
738
741
|
select *,
|
|
739
742
|
object *
|
|
740
743
|
)::after,
|
|
741
|
-
[aria-busy="true"]
|
|
744
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
742
745
|
:is(
|
|
743
746
|
img,
|
|
744
747
|
svg,
|
|
@@ -763,7 +766,7 @@
|
|
|
763
766
|
}
|
|
764
767
|
|
|
765
768
|
@media (forced-colors: active) {
|
|
766
|
-
[aria-busy="true"]
|
|
769
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
767
770
|
:not(:has(*)):not(
|
|
768
771
|
[data-bone],
|
|
769
772
|
[data-bone] *,
|
|
@@ -792,7 +795,7 @@
|
|
|
792
795
|
select *,
|
|
793
796
|
object *
|
|
794
797
|
)::after,
|
|
795
|
-
[aria-busy="true"]
|
|
798
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
796
799
|
:is(
|
|
797
800
|
img,
|
|
798
801
|
svg,
|
|
@@ -817,7 +820,7 @@
|
|
|
817
820
|
}
|
|
818
821
|
|
|
819
822
|
@scope ([data-bone-animate="none"]) to ([data-bone-animate]:not([data-bone-animate="none"])) {
|
|
820
|
-
[aria-busy="true"]
|
|
823
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
821
824
|
:not(:has(*)):not(
|
|
822
825
|
[data-bone],
|
|
823
826
|
[data-bone] *,
|
|
@@ -846,7 +849,7 @@
|
|
|
846
849
|
select *,
|
|
847
850
|
object *
|
|
848
851
|
)::before,
|
|
849
|
-
[aria-busy="true"]
|
|
852
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
850
853
|
:not(:has(*)):not(
|
|
851
854
|
[data-bone],
|
|
852
855
|
[data-bone] *,
|
|
@@ -875,7 +878,7 @@
|
|
|
875
878
|
select *,
|
|
876
879
|
object *
|
|
877
880
|
)::after,
|
|
878
|
-
[aria-busy="true"]
|
|
881
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
879
882
|
:is(
|
|
880
883
|
img,
|
|
881
884
|
svg,
|
|
@@ -899,7 +902,7 @@
|
|
|
899
902
|
}
|
|
900
903
|
|
|
901
904
|
@media (forced-colors: active) {
|
|
902
|
-
[aria-busy="true"]
|
|
905
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
903
906
|
:not(:has(*)):not(
|
|
904
907
|
[data-bone],
|
|
905
908
|
[data-bone] *,
|
|
@@ -928,7 +931,7 @@
|
|
|
928
931
|
select *,
|
|
929
932
|
object *
|
|
930
933
|
)::after,
|
|
931
|
-
[aria-busy="true"]
|
|
934
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
932
935
|
:is(
|
|
933
936
|
img,
|
|
934
937
|
svg,
|
package/dist/css/bones.css
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/* Bones derive their color from the text color in effect at the bone, so
|
|
3
|
+
they inherit the page's own contrast guarantee on any background in any
|
|
4
|
+
scheme (BON-13). currentColor resolves where var() is used, not here.
|
|
5
|
+
The alpha reset matters: bones hide their content by zeroing the alpha
|
|
6
|
+
of the color property (never with the transparent keyword, which would
|
|
7
|
+
discard the channels), and the mix restores it. */
|
|
8
|
+
--bone-base: color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent);
|
|
9
|
+
--bone-highlight: color-mix(in srgb, rgb(from currentcolor r g b / 1) 6%, transparent);
|
|
4
10
|
--bone-radius: 4px;
|
|
5
11
|
--bone-duration: 1.5s;
|
|
6
12
|
}
|
|
7
13
|
|
|
8
|
-
@media (prefers-color-scheme: dark) {
|
|
9
|
-
:root {
|
|
10
|
-
--bone-base: rgba(255, 255, 255, 0.12);
|
|
11
|
-
--bone-highlight: rgba(255, 255, 255, 0.06);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
14
|
[data-bone="text"] {
|
|
16
|
-
color:
|
|
15
|
+
color: rgb(from currentcolor r g b / 0);
|
|
17
16
|
position: relative;
|
|
18
17
|
min-width: 4ch;
|
|
19
18
|
min-height: 1lh;
|
|
@@ -72,7 +71,7 @@
|
|
|
72
71
|
|
|
73
72
|
img[data-bone="block"],
|
|
74
73
|
video[data-bone="block"] {
|
|
75
|
-
color:
|
|
74
|
+
color: rgb(from currentcolor r g b / 0);
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
[data-bone="container"] {
|
|
@@ -111,11 +110,43 @@ video[data-bone="block"] {
|
|
|
111
110
|
}
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
|
|
113
|
+
/* Marked bones shimmer by default, the same as auto bones and the measured
|
|
114
|
+
overlay (BON-16). data-bone-animate="none" is the opt-out; the @scope
|
|
115
|
+
blocks below override this at higher specificity, so their order relative
|
|
116
|
+
to this rule does not matter. */
|
|
117
|
+
[data-bone="text"]::after,
|
|
118
|
+
[data-bone="block"],
|
|
119
|
+
[data-bone="container"]::before {
|
|
120
|
+
animation: bone-shimmer var(--bone-duration) ease-in-out infinite;
|
|
121
|
+
background: linear-gradient(
|
|
122
|
+
90deg,
|
|
123
|
+
var(--bone-base) 25%,
|
|
124
|
+
var(--bone-highlight) 50%,
|
|
125
|
+
var(--bone-base) 75%
|
|
126
|
+
);
|
|
127
|
+
background-size: 200% 100%;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@media (prefers-reduced-motion: reduce) {
|
|
115
131
|
[data-bone="text"]::after,
|
|
116
|
-
[data-bone="text"]::before,
|
|
117
132
|
[data-bone="block"],
|
|
118
133
|
[data-bone="container"]::before {
|
|
134
|
+
animation: bone-pulse 2s ease-in-out infinite;
|
|
135
|
+
background: var(--bone-base);
|
|
136
|
+
background-size: auto;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* :is(:scope, :scope *) lets the attribute work from the bone itself, not
|
|
141
|
+
only from a wrapper: a scoped selector's implicit :scope prefix matches
|
|
142
|
+
strict descendants only, so a plain compound never matches the scope root
|
|
143
|
+
(BON-17). The reduced-motion override sits inside each animated scope so it
|
|
144
|
+
matches at the scope's own specificity; "none" has no override because none
|
|
145
|
+
still means none. */
|
|
146
|
+
@scope ([data-bone-animate="shimmer"]) to ([data-bone-animate]:not([data-bone-animate="shimmer"])) {
|
|
147
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
148
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
149
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
119
150
|
animation: bone-shimmer var(--bone-duration) ease-in-out infinite;
|
|
120
151
|
background: linear-gradient(
|
|
121
152
|
90deg,
|
|
@@ -125,32 +156,42 @@ video[data-bone="block"] {
|
|
|
125
156
|
);
|
|
126
157
|
background-size: 200% 100%;
|
|
127
158
|
}
|
|
159
|
+
|
|
160
|
+
@media (prefers-reduced-motion: reduce) {
|
|
161
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
162
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
163
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
164
|
+
animation: bone-pulse 2s ease-in-out infinite;
|
|
165
|
+
background: var(--bone-base);
|
|
166
|
+
background-size: auto;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
128
169
|
}
|
|
129
170
|
|
|
130
171
|
@scope ([data-bone-animate="pulse"]) to ([data-bone-animate]:not([data-bone-animate="pulse"])) {
|
|
131
|
-
[data-bone="text"]::after,
|
|
132
|
-
[data-bone="
|
|
133
|
-
[data-bone="
|
|
134
|
-
[data-bone="container"]::before {
|
|
172
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
173
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
174
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
135
175
|
animation: bone-pulse var(--bone-duration) ease-in-out infinite;
|
|
136
176
|
background: var(--bone-base);
|
|
137
177
|
background-size: auto;
|
|
138
178
|
}
|
|
179
|
+
|
|
180
|
+
@media (prefers-reduced-motion: reduce) {
|
|
181
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
182
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
183
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
184
|
+
animation: bone-pulse 2s ease-in-out infinite;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
139
187
|
}
|
|
140
188
|
|
|
141
189
|
@scope ([data-bone-animate="none"]) to ([data-bone-animate]:not([data-bone-animate="none"])) {
|
|
142
|
-
[data-bone="text"]::after,
|
|
143
|
-
[data-bone="
|
|
144
|
-
[data-bone="
|
|
145
|
-
[data-bone="container"]::before {
|
|
190
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
191
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
192
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
146
193
|
animation: none;
|
|
147
194
|
background: var(--bone-base);
|
|
148
195
|
background-size: auto;
|
|
149
196
|
}
|
|
150
197
|
}
|
|
151
|
-
|
|
152
|
-
@media (prefers-reduced-motion: reduce) {
|
|
153
|
-
[aria-busy="true"] {
|
|
154
|
-
animation: bone-pulse 2s ease-in-out infinite;
|
|
155
|
-
}
|
|
156
|
-
}
|
package/dist/element/overlay.mjs
CHANGED
|
@@ -19,7 +19,7 @@ const OVERLAY_CSS = `
|
|
|
19
19
|
}
|
|
20
20
|
[part~="bone"] {
|
|
21
21
|
position: absolute;
|
|
22
|
-
background: var(--bone-base,
|
|
22
|
+
background: var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent));
|
|
23
23
|
border-radius: var(--bone-radius, 4px);
|
|
24
24
|
}
|
|
25
25
|
@keyframes bone-shimmer {
|
|
@@ -35,9 +35,9 @@ const OVERLAY_CSS = `
|
|
|
35
35
|
animation: bone-shimmer var(--bone-duration, 1.5s) ease-in-out infinite;
|
|
36
36
|
background: linear-gradient(
|
|
37
37
|
90deg,
|
|
38
|
-
var(--bone-base,
|
|
39
|
-
var(--bone-highlight,
|
|
40
|
-
var(--bone-base,
|
|
38
|
+
var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent)) 25%,
|
|
39
|
+
var(--bone-highlight, color-mix(in srgb, rgb(from currentcolor r g b / 1) 6%, transparent)) 50%,
|
|
40
|
+
var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent)) 75%
|
|
41
41
|
);
|
|
42
42
|
background-size: 200% 100%;
|
|
43
43
|
}
|
|
@@ -55,7 +55,7 @@ const OVERLAY_CSS = `
|
|
|
55
55
|
[part~="overlay"][data-bone-animate="shimmer"] [part~="bone"],
|
|
56
56
|
[part~="overlay"][data-bone-animate="pulse"] [part~="bone"] {
|
|
57
57
|
animation: bone-pulse 2s ease-in-out infinite;
|
|
58
|
-
background: var(--bone-base,
|
|
58
|
+
background: var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent));
|
|
59
59
|
background-size: auto;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { boneAttributes } from "../core/attributes.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { cloneElement, createElement, isValidElement } from "react";
|
|
3
3
|
//#region src/react/create-bones.ts
|
|
4
|
-
const getBonesContext = cache(() => ({ loading: false }));
|
|
5
4
|
function withKey(node, key) {
|
|
6
5
|
return isValidElement(node) ? cloneElement(node, { key }) : node;
|
|
7
6
|
}
|
|
@@ -40,9 +39,6 @@ function createBones(dataOrOptions, maybeOptions) {
|
|
|
40
39
|
} else if (data != null && data === forceBones) {
|
|
41
40
|
isLoading = true;
|
|
42
41
|
resolved = void 0;
|
|
43
|
-
} else if (getBonesContext().loading) {
|
|
44
|
-
isLoading = true;
|
|
45
|
-
resolved = void 0;
|
|
46
42
|
} else if (data != null && data instanceof Promise) resolved = readPromise(data);
|
|
47
43
|
else resolved = data;
|
|
48
44
|
let boneCallIndex = 0;
|
|
@@ -70,4 +66,4 @@ function createBones(dataOrOptions, maybeOptions) {
|
|
|
70
66
|
};
|
|
71
67
|
}
|
|
72
68
|
//#endregion
|
|
73
|
-
export { createBones, forceBones,
|
|
69
|
+
export { createBones, forceBones, readPromise };
|
package/dist/react/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BoneOptions, BoneType, MinMax, isMinMax, minMax } from "../core/attributes.mjs";
|
|
2
2
|
import { CreateBonesOptions, CreateBonesReturn, createBones, forceBones, readPromise } from "./create-bones.mjs";
|
|
3
|
-
import { Bones, BonesForce } from "./bones.mjs";
|
|
4
3
|
import { BonesBoundary, BonesBoundaryProps } from "./boundary.mjs";
|
|
5
|
-
export { type BoneOptions, type BoneType,
|
|
4
|
+
export { type BoneOptions, type BoneType, BonesBoundary, type BonesBoundaryProps, type CreateBonesOptions, type CreateBonesReturn, type MinMax, createBones, forceBones, isMinMax, minMax, readPromise };
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { isMinMax, minMax } from "../core/attributes.mjs";
|
|
2
2
|
import { createBones, forceBones, readPromise } from "./create-bones.mjs";
|
|
3
|
-
import { Bones, BonesForce } from "./bones.mjs";
|
|
4
3
|
import { BonesBoundary } from "./boundary.mjs";
|
|
5
|
-
export {
|
|
4
|
+
export { BonesBoundary, createBones, forceBones, isMinMax, minMax, readPromise };
|
package/package.json
CHANGED
package/src/css/auto.css
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
select *,
|
|
45
45
|
object *
|
|
46
46
|
) {
|
|
47
|
-
color:
|
|
47
|
+
color: rgb(from currentcolor r g b / 0);
|
|
48
48
|
position: relative;
|
|
49
49
|
min-width: 4ch;
|
|
50
50
|
min-height: 1lh;
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
progress,
|
|
145
145
|
meter
|
|
146
146
|
):not([data-bone], [data-bone] *, [data-bones-auto="off"], [data-bones-auto="off"] *) {
|
|
147
|
-
color:
|
|
147
|
+
color: rgb(from currentcolor r g b / 0);
|
|
148
148
|
background-color: var(--bone-base);
|
|
149
149
|
border-radius: var(--bone-radius);
|
|
150
150
|
border-color: transparent;
|
|
@@ -283,10 +283,13 @@
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
@layer bones-auto {
|
|
286
|
-
/* Auto bones default to shimmer. bones.css
|
|
287
|
-
[data-bone
|
|
288
|
-
the default
|
|
289
|
-
that
|
|
286
|
+
/* Auto bones default to shimmer. bones.css has its own default, but its
|
|
287
|
+
rules match [data-bone] elements only and never the unmarked leaves
|
|
288
|
+
targeted here, so the default is restated for them, and the scopes below
|
|
289
|
+
mirror bones.css for pages that set the attribute (BON-16). In those
|
|
290
|
+
scopes, :is(:scope, :scope *) lets the attribute
|
|
291
|
+
sit on the aria-busy element itself: a scoped selector's implicit :scope
|
|
292
|
+
prefix matches strict descendants only (BON-17). */
|
|
290
293
|
[aria-busy="true"]
|
|
291
294
|
:not(:has(*)):not(
|
|
292
295
|
[data-bone],
|
|
@@ -374,7 +377,7 @@
|
|
|
374
377
|
}
|
|
375
378
|
|
|
376
379
|
@scope ([data-bone-animate="shimmer"]) to ([data-bone-animate]:not([data-bone-animate="shimmer"])) {
|
|
377
|
-
[aria-busy="true"]
|
|
380
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
378
381
|
:not(:has(*)):not(
|
|
379
382
|
[data-bone],
|
|
380
383
|
[data-bone] *,
|
|
@@ -403,7 +406,7 @@
|
|
|
403
406
|
select *,
|
|
404
407
|
object *
|
|
405
408
|
)::before,
|
|
406
|
-
[aria-busy="true"]
|
|
409
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
407
410
|
:not(:has(*)):not(
|
|
408
411
|
[data-bone],
|
|
409
412
|
[data-bone] *,
|
|
@@ -432,7 +435,7 @@
|
|
|
432
435
|
select *,
|
|
433
436
|
object *
|
|
434
437
|
)::after,
|
|
435
|
-
[aria-busy="true"]
|
|
438
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
436
439
|
:is(
|
|
437
440
|
img,
|
|
438
441
|
svg,
|
|
@@ -461,7 +464,7 @@
|
|
|
461
464
|
}
|
|
462
465
|
|
|
463
466
|
@media (prefers-reduced-motion: reduce) {
|
|
464
|
-
[aria-busy="true"]
|
|
467
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
465
468
|
:not(:has(*)):not(
|
|
466
469
|
[data-bone],
|
|
467
470
|
[data-bone] *,
|
|
@@ -490,7 +493,7 @@
|
|
|
490
493
|
select *,
|
|
491
494
|
object *
|
|
492
495
|
)::before,
|
|
493
|
-
[aria-busy="true"]
|
|
496
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
494
497
|
:not(:has(*)):not(
|
|
495
498
|
[data-bone],
|
|
496
499
|
[data-bone] *,
|
|
@@ -519,7 +522,7 @@
|
|
|
519
522
|
select *,
|
|
520
523
|
object *
|
|
521
524
|
)::after,
|
|
522
|
-
[aria-busy="true"]
|
|
525
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
523
526
|
:is(
|
|
524
527
|
img,
|
|
525
528
|
svg,
|
|
@@ -544,7 +547,7 @@
|
|
|
544
547
|
}
|
|
545
548
|
|
|
546
549
|
@media (forced-colors: active) {
|
|
547
|
-
[aria-busy="true"]
|
|
550
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
548
551
|
:not(:has(*)):not(
|
|
549
552
|
[data-bone],
|
|
550
553
|
[data-bone] *,
|
|
@@ -573,7 +576,7 @@
|
|
|
573
576
|
select *,
|
|
574
577
|
object *
|
|
575
578
|
)::after,
|
|
576
|
-
[aria-busy="true"]
|
|
579
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
577
580
|
:is(
|
|
578
581
|
img,
|
|
579
582
|
svg,
|
|
@@ -598,7 +601,7 @@
|
|
|
598
601
|
}
|
|
599
602
|
|
|
600
603
|
@scope ([data-bone-animate="pulse"]) to ([data-bone-animate]:not([data-bone-animate="pulse"])) {
|
|
601
|
-
[aria-busy="true"]
|
|
604
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
602
605
|
:not(:has(*)):not(
|
|
603
606
|
[data-bone],
|
|
604
607
|
[data-bone] *,
|
|
@@ -627,7 +630,7 @@
|
|
|
627
630
|
select *,
|
|
628
631
|
object *
|
|
629
632
|
)::before,
|
|
630
|
-
[aria-busy="true"]
|
|
633
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
631
634
|
:not(:has(*)):not(
|
|
632
635
|
[data-bone],
|
|
633
636
|
[data-bone] *,
|
|
@@ -656,7 +659,7 @@
|
|
|
656
659
|
select *,
|
|
657
660
|
object *
|
|
658
661
|
)::after,
|
|
659
|
-
[aria-busy="true"]
|
|
662
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
660
663
|
:is(
|
|
661
664
|
img,
|
|
662
665
|
svg,
|
|
@@ -680,7 +683,7 @@
|
|
|
680
683
|
}
|
|
681
684
|
|
|
682
685
|
@media (prefers-reduced-motion: reduce) {
|
|
683
|
-
[aria-busy="true"]
|
|
686
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
684
687
|
:not(:has(*)):not(
|
|
685
688
|
[data-bone],
|
|
686
689
|
[data-bone] *,
|
|
@@ -709,7 +712,7 @@
|
|
|
709
712
|
select *,
|
|
710
713
|
object *
|
|
711
714
|
)::before,
|
|
712
|
-
[aria-busy="true"]
|
|
715
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
713
716
|
:not(:has(*)):not(
|
|
714
717
|
[data-bone],
|
|
715
718
|
[data-bone] *,
|
|
@@ -738,7 +741,7 @@
|
|
|
738
741
|
select *,
|
|
739
742
|
object *
|
|
740
743
|
)::after,
|
|
741
|
-
[aria-busy="true"]
|
|
744
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
742
745
|
:is(
|
|
743
746
|
img,
|
|
744
747
|
svg,
|
|
@@ -763,7 +766,7 @@
|
|
|
763
766
|
}
|
|
764
767
|
|
|
765
768
|
@media (forced-colors: active) {
|
|
766
|
-
[aria-busy="true"]
|
|
769
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
767
770
|
:not(:has(*)):not(
|
|
768
771
|
[data-bone],
|
|
769
772
|
[data-bone] *,
|
|
@@ -792,7 +795,7 @@
|
|
|
792
795
|
select *,
|
|
793
796
|
object *
|
|
794
797
|
)::after,
|
|
795
|
-
[aria-busy="true"]
|
|
798
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
796
799
|
:is(
|
|
797
800
|
img,
|
|
798
801
|
svg,
|
|
@@ -817,7 +820,7 @@
|
|
|
817
820
|
}
|
|
818
821
|
|
|
819
822
|
@scope ([data-bone-animate="none"]) to ([data-bone-animate]:not([data-bone-animate="none"])) {
|
|
820
|
-
[aria-busy="true"]
|
|
823
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
821
824
|
:not(:has(*)):not(
|
|
822
825
|
[data-bone],
|
|
823
826
|
[data-bone] *,
|
|
@@ -846,7 +849,7 @@
|
|
|
846
849
|
select *,
|
|
847
850
|
object *
|
|
848
851
|
)::before,
|
|
849
|
-
[aria-busy="true"]
|
|
852
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
850
853
|
:not(:has(*)):not(
|
|
851
854
|
[data-bone],
|
|
852
855
|
[data-bone] *,
|
|
@@ -875,7 +878,7 @@
|
|
|
875
878
|
select *,
|
|
876
879
|
object *
|
|
877
880
|
)::after,
|
|
878
|
-
[aria-busy="true"]
|
|
881
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
879
882
|
:is(
|
|
880
883
|
img,
|
|
881
884
|
svg,
|
|
@@ -899,7 +902,7 @@
|
|
|
899
902
|
}
|
|
900
903
|
|
|
901
904
|
@media (forced-colors: active) {
|
|
902
|
-
[aria-busy="true"]
|
|
905
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
903
906
|
:not(:has(*)):not(
|
|
904
907
|
[data-bone],
|
|
905
908
|
[data-bone] *,
|
|
@@ -928,7 +931,7 @@
|
|
|
928
931
|
select *,
|
|
929
932
|
object *
|
|
930
933
|
)::after,
|
|
931
|
-
[aria-busy="true"]
|
|
934
|
+
[aria-busy="true"]:is(:scope, :scope *)
|
|
932
935
|
:is(
|
|
933
936
|
img,
|
|
934
937
|
svg,
|
package/src/css/bones.css
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/* Bones derive their color from the text color in effect at the bone, so
|
|
3
|
+
they inherit the page's own contrast guarantee on any background in any
|
|
4
|
+
scheme (BON-13). currentColor resolves where var() is used, not here.
|
|
5
|
+
The alpha reset matters: bones hide their content by zeroing the alpha
|
|
6
|
+
of the color property (never with the transparent keyword, which would
|
|
7
|
+
discard the channels), and the mix restores it. */
|
|
8
|
+
--bone-base: color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent);
|
|
9
|
+
--bone-highlight: color-mix(in srgb, rgb(from currentcolor r g b / 1) 6%, transparent);
|
|
4
10
|
--bone-radius: 4px;
|
|
5
11
|
--bone-duration: 1.5s;
|
|
6
12
|
}
|
|
7
13
|
|
|
8
|
-
@media (prefers-color-scheme: dark) {
|
|
9
|
-
:root {
|
|
10
|
-
--bone-base: rgba(255, 255, 255, 0.12);
|
|
11
|
-
--bone-highlight: rgba(255, 255, 255, 0.06);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
14
|
[data-bone="text"] {
|
|
16
|
-
color:
|
|
15
|
+
color: rgb(from currentcolor r g b / 0);
|
|
17
16
|
position: relative;
|
|
18
17
|
min-width: 4ch;
|
|
19
18
|
min-height: 1lh;
|
|
@@ -72,7 +71,7 @@
|
|
|
72
71
|
|
|
73
72
|
img[data-bone="block"],
|
|
74
73
|
video[data-bone="block"] {
|
|
75
|
-
color:
|
|
74
|
+
color: rgb(from currentcolor r g b / 0);
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
[data-bone="container"] {
|
|
@@ -111,11 +110,43 @@ video[data-bone="block"] {
|
|
|
111
110
|
}
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
|
|
113
|
+
/* Marked bones shimmer by default, the same as auto bones and the measured
|
|
114
|
+
overlay (BON-16). data-bone-animate="none" is the opt-out; the @scope
|
|
115
|
+
blocks below override this at higher specificity, so their order relative
|
|
116
|
+
to this rule does not matter. */
|
|
117
|
+
[data-bone="text"]::after,
|
|
118
|
+
[data-bone="block"],
|
|
119
|
+
[data-bone="container"]::before {
|
|
120
|
+
animation: bone-shimmer var(--bone-duration) ease-in-out infinite;
|
|
121
|
+
background: linear-gradient(
|
|
122
|
+
90deg,
|
|
123
|
+
var(--bone-base) 25%,
|
|
124
|
+
var(--bone-highlight) 50%,
|
|
125
|
+
var(--bone-base) 75%
|
|
126
|
+
);
|
|
127
|
+
background-size: 200% 100%;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@media (prefers-reduced-motion: reduce) {
|
|
115
131
|
[data-bone="text"]::after,
|
|
116
|
-
[data-bone="text"]::before,
|
|
117
132
|
[data-bone="block"],
|
|
118
133
|
[data-bone="container"]::before {
|
|
134
|
+
animation: bone-pulse 2s ease-in-out infinite;
|
|
135
|
+
background: var(--bone-base);
|
|
136
|
+
background-size: auto;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* :is(:scope, :scope *) lets the attribute work from the bone itself, not
|
|
141
|
+
only from a wrapper: a scoped selector's implicit :scope prefix matches
|
|
142
|
+
strict descendants only, so a plain compound never matches the scope root
|
|
143
|
+
(BON-17). The reduced-motion override sits inside each animated scope so it
|
|
144
|
+
matches at the scope's own specificity; "none" has no override because none
|
|
145
|
+
still means none. */
|
|
146
|
+
@scope ([data-bone-animate="shimmer"]) to ([data-bone-animate]:not([data-bone-animate="shimmer"])) {
|
|
147
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
148
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
149
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
119
150
|
animation: bone-shimmer var(--bone-duration) ease-in-out infinite;
|
|
120
151
|
background: linear-gradient(
|
|
121
152
|
90deg,
|
|
@@ -125,32 +156,42 @@ video[data-bone="block"] {
|
|
|
125
156
|
);
|
|
126
157
|
background-size: 200% 100%;
|
|
127
158
|
}
|
|
159
|
+
|
|
160
|
+
@media (prefers-reduced-motion: reduce) {
|
|
161
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
162
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
163
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
164
|
+
animation: bone-pulse 2s ease-in-out infinite;
|
|
165
|
+
background: var(--bone-base);
|
|
166
|
+
background-size: auto;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
128
169
|
}
|
|
129
170
|
|
|
130
171
|
@scope ([data-bone-animate="pulse"]) to ([data-bone-animate]:not([data-bone-animate="pulse"])) {
|
|
131
|
-
[data-bone="text"]::after,
|
|
132
|
-
[data-bone="
|
|
133
|
-
[data-bone="
|
|
134
|
-
[data-bone="container"]::before {
|
|
172
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
173
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
174
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
135
175
|
animation: bone-pulse var(--bone-duration) ease-in-out infinite;
|
|
136
176
|
background: var(--bone-base);
|
|
137
177
|
background-size: auto;
|
|
138
178
|
}
|
|
179
|
+
|
|
180
|
+
@media (prefers-reduced-motion: reduce) {
|
|
181
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
182
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
183
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
184
|
+
animation: bone-pulse 2s ease-in-out infinite;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
139
187
|
}
|
|
140
188
|
|
|
141
189
|
@scope ([data-bone-animate="none"]) to ([data-bone-animate]:not([data-bone-animate="none"])) {
|
|
142
|
-
[data-bone="text"]::after,
|
|
143
|
-
[data-bone="
|
|
144
|
-
[data-bone="
|
|
145
|
-
[data-bone="container"]::before {
|
|
190
|
+
[data-bone="text"]:is(:scope, :scope *)::after,
|
|
191
|
+
[data-bone="block"]:is(:scope, :scope *),
|
|
192
|
+
[data-bone="container"]:is(:scope, :scope *)::before {
|
|
146
193
|
animation: none;
|
|
147
194
|
background: var(--bone-base);
|
|
148
195
|
background-size: auto;
|
|
149
196
|
}
|
|
150
197
|
}
|
|
151
|
-
|
|
152
|
-
@media (prefers-reduced-motion: reduce) {
|
|
153
|
-
[aria-busy="true"] {
|
|
154
|
-
animation: bone-pulse 2s ease-in-out infinite;
|
|
155
|
-
}
|
|
156
|
-
}
|
package/dist/react/bones.d.mts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
//#region src/react/bones.d.ts
|
|
4
|
-
declare function BonesForce({
|
|
5
|
-
children
|
|
6
|
-
}: {
|
|
7
|
-
children: ReactNode;
|
|
8
|
-
}): ReactNode;
|
|
9
|
-
declare function Bones({
|
|
10
|
-
children
|
|
11
|
-
}: {
|
|
12
|
-
children: ReactNode;
|
|
13
|
-
}): ReactNode;
|
|
14
|
-
//#endregion
|
|
15
|
-
export { Bones, BonesForce };
|
package/dist/react/bones.mjs
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { forceBones, getBonesContext } from "./create-bones.mjs";
|
|
2
|
-
import { Children, Fragment, Suspense, cloneElement, createElement, isValidElement } from "react";
|
|
3
|
-
//#region src/react/bones.ts
|
|
4
|
-
function BonesStart() {
|
|
5
|
-
getBonesContext().loading = true;
|
|
6
|
-
return null;
|
|
7
|
-
}
|
|
8
|
-
function BonesEnd() {
|
|
9
|
-
getBonesContext().loading = false;
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
function swapPromises(children) {
|
|
13
|
-
return Children.map(children, (child) => {
|
|
14
|
-
if (!isValidElement(child)) return child;
|
|
15
|
-
const props = {};
|
|
16
|
-
for (const [key, value] of Object.entries(child.props)) props[key] = value instanceof Promise ? forceBones : value;
|
|
17
|
-
return cloneElement(child, props);
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
function BonesForce({ children }) {
|
|
21
|
-
return createElement(Fragment, null, createElement(BonesStart), children, createElement(BonesEnd));
|
|
22
|
-
}
|
|
23
|
-
function Bones({ children }) {
|
|
24
|
-
return createElement(Suspense, { fallback: createElement(Fragment, null, createElement(BonesStart), swapPromises(children), createElement(BonesEnd)) }, children);
|
|
25
|
-
}
|
|
26
|
-
//#endregion
|
|
27
|
-
export { Bones, BonesForce };
|