@anubis609/astroanimate-core 0.1.2
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 +212 -0
- package/dist/components/AnimatedBorderButton/AnimatedBorderButton.astro +129 -0
- package/dist/components/AnimatedBorderButton/index.js +3 -0
- package/dist/components/AnimatedBorderButton/index.js.map +1 -0
- package/dist/components/AnimatedButton/AnimatedButton.astro +299 -0
- package/dist/components/AnimatedButton/index.js +3 -0
- package/dist/components/AnimatedButton/index.js.map +1 -0
- package/dist/components/AnimatedCard/AnimatedCard.astro +832 -0
- package/dist/components/AnimatedCard/index.js +3 -0
- package/dist/components/AnimatedCard/index.js.map +1 -0
- package/dist/components/AnimatedTabs/AnimatedTabs.astro +348 -0
- package/dist/components/AnimatedTabs/index.js +3 -0
- package/dist/components/AnimatedTabs/index.js.map +1 -0
- package/dist/components/ArrowCTAButton/ArrowCTAButton.astro +159 -0
- package/dist/components/ArticleCard/ArticleCard.astro +208 -0
- package/dist/components/CardStack/CardStack.astro +444 -0
- package/dist/components/CardStack/index.js +3 -0
- package/dist/components/CardStack/index.js.map +1 -0
- package/dist/components/CountUp/CountUp.astro +89 -0
- package/dist/components/CountUp/index.js +3 -0
- package/dist/components/CountUp/index.js.map +1 -0
- package/dist/components/Dock/Dock.astro +567 -0
- package/dist/components/Dock/DockItem.astro +135 -0
- package/dist/components/Dropdown/Dropdown.astro +264 -0
- package/dist/components/ExpandableCard/ExpandableCard.astro +402 -0
- package/dist/components/ExpandableCard/index.js +3 -0
- package/dist/components/ExpandableCard/index.js.map +1 -0
- package/dist/components/FadeInText/FadeInText.astro +314 -0
- package/dist/components/FadeInText/index.js +3 -0
- package/dist/components/FadeInText/index.js.map +1 -0
- package/dist/components/FillHoverButton/FillHoverButton.astro +125 -0
- package/dist/components/GitHubShineButton/GitHubShineButton.astro +208 -0
- package/dist/components/GlassCard/GlassCard.astro +245 -0
- package/dist/components/GlassCard/index.js +3 -0
- package/dist/components/GlassCard/index.js.map +1 -0
- package/dist/components/GridDotsBackground/GridDotsBackground.astro +144 -0
- package/dist/components/HighlightText/HighlightText.astro +106 -0
- package/dist/components/InfiniteMarquee/InfiniteMarquee.astro +339 -0
- package/dist/components/JobCard/JobCard.astro +230 -0
- package/dist/components/LiquidGlassCard/LiquidGlassCard.astro +569 -0
- package/dist/components/Loader/Loader.astro +156 -0
- package/dist/components/Loader/index.js +3 -0
- package/dist/components/Loader/index.js.map +1 -0
- package/dist/components/NewsletterPopupCard/NewsletterPopupCard.astro +331 -0
- package/dist/components/ProductReviewCard/ProductReviewCard.astro +188 -0
- package/dist/components/ProgressBar/ProgressBar.astro +137 -0
- package/dist/components/ProgressBar/index.js +3 -0
- package/dist/components/ProgressBar/index.js.map +1 -0
- package/dist/components/RevealImage/RevealImage.astro +160 -0
- package/dist/components/RevealImage/index.js +3 -0
- package/dist/components/RevealImage/index.js.map +1 -0
- package/dist/components/ScaleIn/ScaleIn.astro +231 -0
- package/dist/components/ScaleIn/index.js +3 -0
- package/dist/components/ScaleIn/index.js.map +1 -0
- package/dist/components/SlidingOverlayButton/SlidingOverlayButton.astro +126 -0
- package/dist/components/StaggerTextButton/StaggerTextButton.astro +132 -0
- package/dist/components/Tooltip/Tooltip.astro +255 -0
- package/dist/components/Tooltip/index.js +3 -0
- package/dist/components/Tooltip/index.js.map +1 -0
- package/dist/components/TypewriterText/TypewriterText.astro +380 -0
- package/dist/components/TypewriterText/index.js +3 -0
- package/dist/components/TypewriterText/index.js.map +1 -0
- package/dist/components/index.js +33 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/countup.js +90 -0
- package/dist/internal/countup.js.map +1 -0
- package/dist/internal/dropdown.js +166 -0
- package/dist/internal/dropdown.js.map +1 -0
- package/dist/internal/fadein.js +116 -0
- package/dist/internal/fadein.js.map +1 -0
- package/dist/internal/guards.js +12 -0
- package/dist/internal/guards.js.map +1 -0
- package/dist/internal/tabs.js +140 -0
- package/dist/internal/tabs.js.map +1 -0
- package/package.json +229 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Astroanimate
|
|
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
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# astroanimate
|
|
2
|
+
|
|
3
|
+
Astro-native animation components: **AnimatedButton**, **AnimatedCard**, **ExpandableCard**, **FadeInText**, **GlassCard**, **Loader**, **ProgressBar**, **RevealImage**, **ScaleIn**, **Tooltip**, and **TypewriterText**.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @astroanimate/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quality Gates
|
|
12
|
+
|
|
13
|
+
- `npm run build` builds ESM output into `dist/`
|
|
14
|
+
- `npm run typecheck` runs `astro check` and TypeScript validation
|
|
15
|
+
- `npm run lint` validates the package source and tooling config
|
|
16
|
+
- `npm run test` runs the unit and contract test suite
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
**Important:** Due to Astro's component resolution limitations, components must be imported via subpath exports. Named imports from the main barrel (e.g., `import { Tooltip } from "@astroanimate/core"`) will not work.
|
|
21
|
+
|
|
22
|
+
### AnimatedButton
|
|
23
|
+
|
|
24
|
+
CSS-first button effects with no hydration and a visible no-JS baseline.
|
|
25
|
+
|
|
26
|
+
```astro
|
|
27
|
+
---
|
|
28
|
+
import AnimatedButton from "@astroanimate/core/AnimatedButton";
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<AnimatedButton variant="shimmer">Explore</AnimatedButton>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### AnimatedCard
|
|
35
|
+
|
|
36
|
+
Interactive card with lift, scale, shine, and flip effects.
|
|
37
|
+
|
|
38
|
+
```astro
|
|
39
|
+
---
|
|
40
|
+
import AnimatedCard from "@astroanimate/core/AnimatedCard";
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
<AnimatedCard
|
|
44
|
+
title="Lift Card"
|
|
45
|
+
description="Hover to see the lift effect with ambient glow"
|
|
46
|
+
variant="lift"
|
|
47
|
+
color="rose"
|
|
48
|
+
/>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### FadeInText
|
|
52
|
+
|
|
53
|
+
Scroll-triggered fade-in with blur effect (opt-in enhancement).
|
|
54
|
+
|
|
55
|
+
```astro
|
|
56
|
+
---
|
|
57
|
+
import FadeInText from "@astroanimate/core/FadeInText";
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
<FadeInText duration={0.6} delay={0} enhance={true}>
|
|
61
|
+
<h3>Fade In Text</h3>
|
|
62
|
+
<p>This text fades in with blur effect on page load.</p>
|
|
63
|
+
</FadeInText>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
> When `enhance=true`, the element starts hidden and waits for JavaScript to initialise the IntersectionObserver. If JS is slow to load, the element has a built-in 2s CSS fallback animation that fires automatically — ensuring content never stays permanently invisible. This means in slow-JS environments, users may see content appear after roughly 2 seconds before the scroll-trigger behaviour takes over.
|
|
67
|
+
|
|
68
|
+
### Loader
|
|
69
|
+
|
|
70
|
+
CSS-only loading indicators with multiple variants.
|
|
71
|
+
|
|
72
|
+
```astro
|
|
73
|
+
---
|
|
74
|
+
import Loader from "@astroanimate/core/Loader";
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
<Loader type="spinner" size={40} color="#3b82f6" />
|
|
78
|
+
<Loader type="dots" size={40} color="#10b981" />
|
|
79
|
+
<Loader type="pulse" size={40} color="#f59e0b" />
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Tooltip
|
|
83
|
+
|
|
84
|
+
Hover-based tooltip with positioning options and optional enhancement.
|
|
85
|
+
|
|
86
|
+
```astro
|
|
87
|
+
---
|
|
88
|
+
import Tooltip from "@astroanimate/core/Tooltip";
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
<Tooltip content="Tooltip on top" position="top">
|
|
92
|
+
<button>Hover me</button>
|
|
93
|
+
</Tooltip>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### ScaleIn
|
|
97
|
+
|
|
98
|
+
Scale-in animation with optional JS enhancement.
|
|
99
|
+
|
|
100
|
+
```astro
|
|
101
|
+
---
|
|
102
|
+
import ScaleIn from "@astroanimate/core/ScaleIn";
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
<ScaleIn enhance duration={0.6} delay={0}>
|
|
106
|
+
<h3>Scale In Content</h3>
|
|
107
|
+
<p>This content scales in when scrolled into view.</p>
|
|
108
|
+
</ScaleIn>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### TypewriterText
|
|
112
|
+
|
|
113
|
+
Typewriter effect with optional JS enhancement.
|
|
114
|
+
|
|
115
|
+
```astro
|
|
116
|
+
---
|
|
117
|
+
import TypewriterText from "@astroanimate/core/TypewriterText";
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
<TypewriterText text="Hello, World!" enhance speed={50} />
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### GlassCard
|
|
124
|
+
|
|
125
|
+
Glassmorphism UI with 3D tilt effect and dynamic glare.
|
|
126
|
+
|
|
127
|
+
```astro
|
|
128
|
+
---
|
|
129
|
+
import GlassCard from "@astroanimate/core/GlassCard";
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
<GlassCard
|
|
133
|
+
title="Glassmorphism UI"
|
|
134
|
+
subtitle="A New Design Trend"
|
|
135
|
+
description="This card uses the glassmorphism effect to create a sense of depth and transparency."
|
|
136
|
+
enableTilt={true}
|
|
137
|
+
tiltIntensity={10}
|
|
138
|
+
/>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### ExpandableCard
|
|
142
|
+
|
|
143
|
+
Expandable/collapsible card with CSS baseline and optional JS enhancement.
|
|
144
|
+
|
|
145
|
+
```astro
|
|
146
|
+
---
|
|
147
|
+
import ExpandableCard from "@astroanimate/core/ExpandableCard";
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
<ExpandableCard
|
|
151
|
+
title="Expandable Card"
|
|
152
|
+
subtitle="Click to expand"
|
|
153
|
+
initialOpen={false}
|
|
154
|
+
enhance={true}
|
|
155
|
+
duration={300}
|
|
156
|
+
>
|
|
157
|
+
<p>This content can be expanded and collapsed.</p>
|
|
158
|
+
</ExpandableCard>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### ProgressBar
|
|
162
|
+
|
|
163
|
+
Progress bar with size variants (small, medium, large).
|
|
164
|
+
|
|
165
|
+
```astro
|
|
166
|
+
---
|
|
167
|
+
import ProgressBar from "@astroanimate/core/ProgressBar";
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
<ProgressBar value={75} max={100} label="Loading" size="medium" color="#3b82f6" />
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### RevealImage
|
|
174
|
+
|
|
175
|
+
Text with two images that reveal on hover (pure CSS).
|
|
176
|
+
|
|
177
|
+
```astro
|
|
178
|
+
---
|
|
179
|
+
import RevealImage from "@astroanimate/core/RevealImage";
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
<RevealImage
|
|
183
|
+
text="Hover Me"
|
|
184
|
+
image1="/path/to/image1.jpg"
|
|
185
|
+
image2="/path/to/image2.jpg"
|
|
186
|
+
alt="Reveal images"
|
|
187
|
+
/>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Exports
|
|
191
|
+
|
|
192
|
+
| Import | Component |
|
|
193
|
+
| ----------------------------------- | -------------------- |
|
|
194
|
+
| `@astroanimate/core/AnimatedButton` | AnimatedButton.astro |
|
|
195
|
+
| `@astroanimate/core/AnimatedCard` | AnimatedCard.astro |
|
|
196
|
+
| `@astroanimate/core/ExpandableCard` | ExpandableCard.astro |
|
|
197
|
+
| `@astroanimate/core/FadeInText` | FadeInText.astro |
|
|
198
|
+
| `@astroanimate/core/GlassCard` | GlassCard.astro |
|
|
199
|
+
| `@astroanimate/core/Loader` | Loader.astro |
|
|
200
|
+
| `@astroanimate/core/ProgressBar` | ProgressBar.astro |
|
|
201
|
+
| `@astroanimate/core/RevealImage` | RevealImage.astro |
|
|
202
|
+
| `@astroanimate/core/ScaleIn` | ScaleIn.astro |
|
|
203
|
+
| `@astroanimate/core/Tooltip` | Tooltip.astro |
|
|
204
|
+
| `@astroanimate/core/TypewriterText` | TypewriterText.astro |
|
|
205
|
+
|
|
206
|
+
## Peer dependency
|
|
207
|
+
|
|
208
|
+
- **astro** `^4.0.0 || ^5.0.0`
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
as?: "button" | "a";
|
|
4
|
+
href?: string;
|
|
5
|
+
type?: "button" | "submit" | "reset";
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
as = "button",
|
|
11
|
+
href,
|
|
12
|
+
type = "button",
|
|
13
|
+
class: className = "",
|
|
14
|
+
} = Astro.props;
|
|
15
|
+
|
|
16
|
+
const Tag = as === "a" ? "a" : "button";
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<style>
|
|
20
|
+
/* ✅ CRITERION 7: CSS-FIRST (all visuals handled here) */
|
|
21
|
+
[data-abb] {
|
|
22
|
+
--border-radius: 15px;
|
|
23
|
+
--border-width: 4px;
|
|
24
|
+
|
|
25
|
+
appearance: none;
|
|
26
|
+
position: relative;
|
|
27
|
+
padding: 1em 2em;
|
|
28
|
+
border: 0;
|
|
29
|
+
background-color: #ffffff;
|
|
30
|
+
font-family: "Roboto", Arial, "Segoe UI", sans-serif;
|
|
31
|
+
font-size: 18px;
|
|
32
|
+
font-weight: 500;
|
|
33
|
+
color: #000;
|
|
34
|
+
z-index: 2;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
text-decoration: none;
|
|
37
|
+
display: inline-block;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ✅ CRITERION 1: visible baseline without JS */
|
|
41
|
+
[data-abb]::after {
|
|
42
|
+
--m-i: linear-gradient(#000, #000);
|
|
43
|
+
--m-o: content-box, padding-box;
|
|
44
|
+
|
|
45
|
+
content: "";
|
|
46
|
+
position: absolute;
|
|
47
|
+
inset: 0;
|
|
48
|
+
padding: var(--border-width);
|
|
49
|
+
border-radius: var(--border-radius);
|
|
50
|
+
|
|
51
|
+
background-image: conic-gradient(
|
|
52
|
+
#488cfb,
|
|
53
|
+
#29dbbc,
|
|
54
|
+
#ddf505,
|
|
55
|
+
#ff9f0e,
|
|
56
|
+
#e440bb,
|
|
57
|
+
#655adc,
|
|
58
|
+
#488cfb
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
-webkit-mask-image: var(--m-i), var(--m-i);
|
|
62
|
+
mask-image: var(--m-i), var(--m-i);
|
|
63
|
+
-webkit-mask-origin: var(--m-o);
|
|
64
|
+
mask-origin: var(--m-o);
|
|
65
|
+
-webkit-mask-clip: var(--m-o);
|
|
66
|
+
mask-composite: exclude;
|
|
67
|
+
-webkit-mask-composite: destination-out;
|
|
68
|
+
|
|
69
|
+
filter: hue-rotate(0);
|
|
70
|
+
|
|
71
|
+
/* Animation defined but paused */
|
|
72
|
+
animation: abb-rotate-hue linear 500ms infinite;
|
|
73
|
+
animation-play-state: paused;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Hover interaction */
|
|
77
|
+
[data-abb]:hover::after {
|
|
78
|
+
animation-play-state: running;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Active state */
|
|
82
|
+
[data-abb]:active {
|
|
83
|
+
--border-width: 5px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Focus accessibility */
|
|
87
|
+
[data-abb]:focus-visible {
|
|
88
|
+
outline: 2px solid #fff;
|
|
89
|
+
outline-offset: 4px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Box sizing */
|
|
93
|
+
[data-abb],
|
|
94
|
+
[data-abb]::after {
|
|
95
|
+
box-sizing: border-box;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Animation */
|
|
99
|
+
@keyframes abb-rotate-hue {
|
|
100
|
+
to {
|
|
101
|
+
filter: hue-rotate(1turn);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ✅ CRITERION 6: REDUCED MOTION (ALL animations disabled) */
|
|
106
|
+
@media (prefers-reduced-motion: reduce) {
|
|
107
|
+
[data-abb]::after {
|
|
108
|
+
animation: none !important;
|
|
109
|
+
filter: none !important;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
[data-abb]:hover::after {
|
|
113
|
+
animation: none !important;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
</style>
|
|
117
|
+
|
|
118
|
+
<!-- ✅ CRITERION 1: meaningful HTML content -->
|
|
119
|
+
{
|
|
120
|
+
as === "a" ? (
|
|
121
|
+
<a data-abb href={href} class={className}>
|
|
122
|
+
<slot />
|
|
123
|
+
</a>
|
|
124
|
+
) : (
|
|
125
|
+
<button data-abb type={type} class={className}>
|
|
126
|
+
<slot />
|
|
127
|
+
</button>
|
|
128
|
+
)
|
|
129
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js","sourcesContent":[]}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ✅ CRITERION 2: Zero Hydration
|
|
4
|
+
* No hydration directives used. Initial render independent of JS.
|
|
5
|
+
*
|
|
6
|
+
* ✅ CRITERION 8: JS Only Enhances
|
|
7
|
+
* This component is CSS-only by design (Zero JS requested).
|
|
8
|
+
* It provides a premium experience out-of-the-box.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
variant?: "shimmer" | "fill" | "border" | "none";
|
|
13
|
+
href?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
shimmerColor?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
variant = "none",
|
|
22
|
+
href,
|
|
23
|
+
class: className = "",
|
|
24
|
+
shimmerColor = "rgba(255, 255, 255, 0.4)",
|
|
25
|
+
disabled = false,
|
|
26
|
+
...rest
|
|
27
|
+
} = Astro.props;
|
|
28
|
+
|
|
29
|
+
// Prop validation
|
|
30
|
+
const validVariants = ["shimmer", "fill", "border", "none"] as const;
|
|
31
|
+
const validatedVariant = validVariants.includes(variant as any)
|
|
32
|
+
? variant
|
|
33
|
+
: "none";
|
|
34
|
+
|
|
35
|
+
// Validate shimmerColor is a valid CSS color format
|
|
36
|
+
const isValidColor = (color: string): boolean => {
|
|
37
|
+
// Accepts: hex, rgb, rgba, hsl, hsla, and named colors
|
|
38
|
+
return /^(#([0-9a-fA-F]{3}){1,2}|rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+(\s*,\s*[\d.]+)?\s*\)|hsla?\(\s*[\d.]+\s*,\s*[\d.]+%\s*,\s*[\d.]+%(\s*,\s*[\d.]+)?\s*\)|[a-zA-Z][a-zA-Z0-9\s]*)$/.test(
|
|
39
|
+
color,
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
const validatedShimmerColor = isValidColor(shimmerColor)
|
|
43
|
+
? shimmerColor
|
|
44
|
+
: "rgba(255, 255, 255, 0.4)";
|
|
45
|
+
|
|
46
|
+
// Validate href if provided
|
|
47
|
+
const validatedHref = href && href.trim() !== "" ? href : undefined;
|
|
48
|
+
|
|
49
|
+
const Tag = validatedHref ? "a" : "button";
|
|
50
|
+
|
|
51
|
+
// Helper for variant classes
|
|
52
|
+
const variantClasses = {
|
|
53
|
+
shimmer: "button-shimmer",
|
|
54
|
+
fill: "button-fill",
|
|
55
|
+
border: "button-border",
|
|
56
|
+
none: "",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const activeVariantClass = variantClasses[validatedVariant] || "";
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
<!-- ✅ CRITERION 1: Visible Without JS --><!-- Meaningful HTML content exists and is visible at baseline -->
|
|
63
|
+
<Tag
|
|
64
|
+
href={validatedHref}
|
|
65
|
+
type={validatedHref ? undefined : "button"}
|
|
66
|
+
class:list={[
|
|
67
|
+
"animated-button",
|
|
68
|
+
activeVariantClass,
|
|
69
|
+
className,
|
|
70
|
+
disabled && "disabled",
|
|
71
|
+
]}
|
|
72
|
+
data-astro-animated-button
|
|
73
|
+
data-variant={validatedVariant}
|
|
74
|
+
style={validatedVariant === "shimmer"
|
|
75
|
+
? `--shimmer-color: ${validatedShimmerColor};`
|
|
76
|
+
: ""}
|
|
77
|
+
disabled={disabled}
|
|
78
|
+
{...rest}
|
|
79
|
+
>
|
|
80
|
+
<span class="button-content">
|
|
81
|
+
<slot />
|
|
82
|
+
</span>
|
|
83
|
+
|
|
84
|
+
{
|
|
85
|
+
validatedVariant === "shimmer" && (
|
|
86
|
+
<span class="shimmer-effect" aria-hidden="true" />
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
{
|
|
90
|
+
validatedVariant === "fill" && (
|
|
91
|
+
<span class="fill-effect" aria-hidden="true" />
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
{
|
|
95
|
+
validatedVariant === "border" && (
|
|
96
|
+
<>
|
|
97
|
+
<span class="border-line border-line-top" aria-hidden="true" />
|
|
98
|
+
<span class="border-line border-line-right" aria-hidden="true" />
|
|
99
|
+
<span class="border-line border-line-bottom" aria-hidden="true" />
|
|
100
|
+
<span class="border-line border-line-left" aria-hidden="true" />
|
|
101
|
+
</>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
</Tag>
|
|
105
|
+
|
|
106
|
+
<style>
|
|
107
|
+
.button-content {
|
|
108
|
+
position: relative;
|
|
109
|
+
z-index: 10;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* --- BASE STYLES --- */
|
|
113
|
+
.animated-button {
|
|
114
|
+
position: relative;
|
|
115
|
+
display: inline-flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
text-decoration: none;
|
|
121
|
+
border: 1px solid #ccc;
|
|
122
|
+
background: white;
|
|
123
|
+
color: #111;
|
|
124
|
+
font: inherit;
|
|
125
|
+
transition: all 0.3s ease;
|
|
126
|
+
z-index: 0;
|
|
127
|
+
padding: 1rem 1rem;
|
|
128
|
+
border-radius: 0.5em;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.animated-button:hover {
|
|
132
|
+
border-color: #999;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.animated-button:focus-visible {
|
|
136
|
+
outline: 2px solid #667eea;
|
|
137
|
+
outline-offset: 2px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.animated-button.disabled {
|
|
141
|
+
opacity: 0.5;
|
|
142
|
+
cursor: not-allowed;
|
|
143
|
+
pointer-events: none;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.animated-button.disabled:hover {
|
|
147
|
+
border-color: #ccc;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* --- SHIMMER VARIANT --- */
|
|
151
|
+
.button-shimmer {
|
|
152
|
+
background: #111;
|
|
153
|
+
color: white;
|
|
154
|
+
border-color: #111;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.shimmer-effect {
|
|
158
|
+
position: absolute;
|
|
159
|
+
inset: 0;
|
|
160
|
+
z-index: 5;
|
|
161
|
+
background: linear-gradient(
|
|
162
|
+
90deg,
|
|
163
|
+
transparent,
|
|
164
|
+
var(--shimmer-color, rgba(255, 255, 255, 0.4)),
|
|
165
|
+
transparent
|
|
166
|
+
);
|
|
167
|
+
transform: translateX(-100%);
|
|
168
|
+
pointer-events: none;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* ✅ CRITERION 7: CSS-First (Hover triggered by CSS) */
|
|
172
|
+
.button-shimmer:not(.disabled):hover .shimmer-effect {
|
|
173
|
+
animation: shimmer 1.5s infinite;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@keyframes shimmer {
|
|
177
|
+
0% {
|
|
178
|
+
transform: translateX(-100%);
|
|
179
|
+
}
|
|
180
|
+
100% {
|
|
181
|
+
transform: translateX(100%);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* --- FILL VARIANT --- */
|
|
186
|
+
.button-fill {
|
|
187
|
+
border: 1px solid currentColor;
|
|
188
|
+
color: #111;
|
|
189
|
+
background: transparent;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.fill-effect {
|
|
193
|
+
position: absolute;
|
|
194
|
+
inset: 0;
|
|
195
|
+
z-index: 1;
|
|
196
|
+
background: currentColor;
|
|
197
|
+
transform: scaleX(0);
|
|
198
|
+
transform-origin: left;
|
|
199
|
+
transition: transform 0.4s cubic-bezier(0.19, 1, 0.22, 1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* ✅ CRITERION 7: CSS-First */
|
|
203
|
+
.button-fill:not(.disabled):hover .fill-effect {
|
|
204
|
+
transform: scaleX(1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.button-fill:not(.disabled):hover .button-content {
|
|
208
|
+
color: white; /* Basic inversion for visibility */
|
|
209
|
+
mix-blend-mode: difference;
|
|
210
|
+
transition: color 0.4s ease;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* --- BORDER VARIANT --- */
|
|
214
|
+
.button-border {
|
|
215
|
+
padding: 0.5rem 1rem;
|
|
216
|
+
color: white;
|
|
217
|
+
background: #111;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.border-line {
|
|
221
|
+
position: absolute;
|
|
222
|
+
background: white;
|
|
223
|
+
z-index: 5;
|
|
224
|
+
transition: transform 0.3s ease;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.border-line-top {
|
|
228
|
+
top: 0;
|
|
229
|
+
left: 0;
|
|
230
|
+
width: 100%;
|
|
231
|
+
height: 2px;
|
|
232
|
+
transform: scaleX(0);
|
|
233
|
+
transform-origin: left;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.border-line-right {
|
|
237
|
+
top: 0;
|
|
238
|
+
right: 0;
|
|
239
|
+
width: 2px;
|
|
240
|
+
height: 100%;
|
|
241
|
+
transform: scaleY(0);
|
|
242
|
+
transform-origin: top;
|
|
243
|
+
transition-delay: 0.05s;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.border-line-bottom {
|
|
247
|
+
bottom: 0;
|
|
248
|
+
right: 0;
|
|
249
|
+
width: 100%;
|
|
250
|
+
height: 2px;
|
|
251
|
+
transform: scaleX(0);
|
|
252
|
+
transform-origin: right;
|
|
253
|
+
transition-delay: 0.1s;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.border-line-left {
|
|
257
|
+
bottom: 0;
|
|
258
|
+
left: 0;
|
|
259
|
+
width: 2px;
|
|
260
|
+
height: 100%;
|
|
261
|
+
transform: scaleY(0);
|
|
262
|
+
transform-origin: bottom;
|
|
263
|
+
transition-delay: 0.15s;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* ✅ CRITERION 7: CSS-First */
|
|
267
|
+
.button-border:not(.disabled):hover .border-line-top {
|
|
268
|
+
transform: scaleX(1);
|
|
269
|
+
}
|
|
270
|
+
.button-border:not(.disabled):hover .border-line-right {
|
|
271
|
+
transform: scaleY(1);
|
|
272
|
+
}
|
|
273
|
+
.button-border:not(.disabled):hover .border-line-bottom {
|
|
274
|
+
transform: scaleX(1);
|
|
275
|
+
}
|
|
276
|
+
.button-border:not(.disabled):hover .border-line-left {
|
|
277
|
+
transform: scaleY(1);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* ✅ CRITERION 6: Reduced Motion Respected */
|
|
281
|
+
/* ALL animations and transitions wrapped or overridden */
|
|
282
|
+
@media (prefers-reduced-motion: reduce) {
|
|
283
|
+
.animated-button,
|
|
284
|
+
.shimmer-effect,
|
|
285
|
+
.fill-effect,
|
|
286
|
+
.border-line,
|
|
287
|
+
.button-content {
|
|
288
|
+
transition: none !important;
|
|
289
|
+
animation: none !important;
|
|
290
|
+
transform: none !important;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* Fallback states for reduced motion visibility */
|
|
294
|
+
.button-fill:hover {
|
|
295
|
+
background: #111;
|
|
296
|
+
color: white;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js","sourcesContent":[]}
|