@at-flux/astroflare 1.0.11 → 1.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@at-flux/astroflare",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "type": "module",
5
5
  "description": "Reusable headless components, styles, and utilities for Astro + Tailwind v4 + Cloudflare projects",
6
6
  "author": "atflux <dev@atflux.uk>",
@@ -30,6 +30,12 @@ interface Props {
30
30
  duration?: number;
31
31
  /** Spinner/skeleton accent colour (defaults to `--color-brand`). */
32
32
  spinnerColor?: string;
33
+ /** Second accent, used by the counter-rotating inner ring (defaults to `spinnerColor`). */
34
+ spinnerColorSecondary?: string;
35
+ /** Outer ring diameter, any CSS length. */
36
+ spinnerSize?: string;
37
+ /** Wrapper background shown behind the image. Pass `"transparent"` for `object-contain` images. */
38
+ background?: string;
33
39
  /** Show a shimmer skeleton instead of a spinner. */
34
40
  skeleton?: boolean;
35
41
  }
@@ -40,12 +46,18 @@ const {
40
46
  aspectRatio,
41
47
  duration = 500,
42
48
  spinnerColor,
49
+ spinnerColorSecondary,
50
+ spinnerSize,
51
+ background,
43
52
  skeleton = false,
44
53
  } = Astro.props;
45
54
 
46
55
  const style = [
47
56
  aspectRatio ? `aspect-ratio:${aspectRatio}` : '',
48
57
  spinnerColor ? `--if-accent:${spinnerColor}` : '',
58
+ spinnerColorSecondary ? `--if-accent-2:${spinnerColorSecondary}` : '',
59
+ spinnerSize ? `--if-size:${spinnerSize}` : '',
60
+ background ? `--if-bg:${background}` : '',
49
61
  `--if-duration:${duration}ms`,
50
62
  ]
51
63
  .filter(Boolean)
@@ -54,7 +66,13 @@ const style = [
54
66
 
55
67
  <image-fade class:list={['image-fade', skeleton && 'is-skeleton', rounded, className]} style={style}>
56
68
  <span class='image-fade__placeholder' aria-hidden='true'>
57
- {!skeleton && <span class='image-fade__ring'></span>}
69
+ {
70
+ !skeleton && (
71
+ <span class='image-fade__ring'>
72
+ <span class='image-fade__ring-inner' />
73
+ </span>
74
+ )
75
+ }
58
76
  </span>
59
77
  <slot />
60
78
  </image-fade>
@@ -64,7 +82,10 @@ const style = [
64
82
  position: relative;
65
83
  display: block;
66
84
  overflow: hidden;
67
- background: color-mix(in oklab, var(--if-accent, var(--color-brand, #7c5cff)) 8%, transparent);
85
+ background: var(
86
+ --if-bg,
87
+ color-mix(in oklab, var(--if-accent, var(--color-brand, #7c5cff)) 8%, transparent)
88
+ );
68
89
  }
69
90
 
70
91
  /* Slotted image starts hidden, fades in once loaded.
@@ -96,8 +117,10 @@ const style = [
96
117
  }
97
118
 
98
119
  .image-fade__ring {
99
- width: 2.5rem;
100
- height: 2.5rem;
120
+ position: relative;
121
+ display: block;
122
+ width: var(--if-size, 2.5rem);
123
+ height: var(--if-size, 2.5rem);
101
124
  border: 3px solid transparent;
102
125
  border-top-color: var(--if-accent, var(--color-brand, #7c5cff));
103
126
  border-right-color: color-mix(
@@ -106,7 +129,28 @@ const style = [
106
129
  transparent
107
130
  );
108
131
  border-radius: 50%;
109
- animation: image-fade-spin 0.9s linear infinite;
132
+ /* `transform` is reset explicitly: a host page may set a base transform on
133
+ every element (e.g. a `* { transform: translateZ(0) }` GPU hint), which
134
+ would otherwise make the keyframes interpolate as matrices and cancel the
135
+ full turn out to no visible rotation. */
136
+ transform: rotate(0deg);
137
+ animation: image-fade-spin 1.2s linear infinite;
138
+ }
139
+
140
+ .image-fade__ring-inner {
141
+ position: absolute;
142
+ top: 50%;
143
+ left: 50%;
144
+ display: block;
145
+ width: 60%;
146
+ height: 60%;
147
+ margin: -30% 0 0 -30%;
148
+ border: 2px solid transparent;
149
+ border-top-color: var(--if-accent-2, var(--if-accent, var(--color-brand, #7c5cff)));
150
+ border-right-color: var(--if-accent-2, var(--if-accent, var(--color-brand, #7c5cff)));
151
+ border-radius: 50%;
152
+ transform: rotate(0deg);
153
+ animation: image-fade-spin 0.8s linear infinite reverse;
110
154
  }
111
155
 
112
156
  /* Skeleton variant: shimmer sweep instead of a spinner */
@@ -121,7 +165,13 @@ const style = [
121
165
  animation: image-fade-shimmer 1.4s ease-in-out infinite;
122
166
  }
123
167
 
168
+ /* Both frames are explicit so the interpolation stays angular. An implicit
169
+ `from` would be taken from the element's computed transform, which is not
170
+ guaranteed to be `none` on every host page. */
124
171
  @keyframes image-fade-spin {
172
+ from {
173
+ transform: rotate(0deg);
174
+ }
125
175
  to {
126
176
  transform: rotate(360deg);
127
177
  }
@@ -141,7 +191,8 @@ const style = [
141
191
  .image-fade__placeholder {
142
192
  transition: none;
143
193
  }
144
- .image-fade__ring {
194
+ .image-fade__ring,
195
+ .image-fade__ring-inner {
145
196
  animation: none;
146
197
  }
147
198
  image-fade.image-fade.is-skeleton .image-fade__placeholder {