astro-reveal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nicolás Picoto
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,146 @@
1
+ # astro-reveal
2
+
3
+ Scroll reveal animations for [Astro](https://astro.build) — smooth slide-ins, fades, scales — that work with **any** UI framework or no framework at all.
4
+
5
+ - **Zero runtime JS by default.** Uses native CSS [scroll-driven animations](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline). In purist mode your page ships a single stylesheet and nothing else.
6
+ - **Opt-in JS engine** (`mode: "observer"`) when you want the classic "reveal once and stay" behaviour and universal browser support — about **0.6 KB** gzipped.
7
+ - **UI-agnostic.** Astro outputs HTML in the end, so the same `data-reveal` attribute animates content whether it came from a React, Vue, Svelte island, or plain HTML.
8
+ - **FOUC-proof and accessible by design.** No flash of hidden content, and `prefers-reduced-motion` is respected out of the box.
9
+ - **Plays nice with View Transitions** and late-hydrating islands.
10
+
11
+ It's the [AOS](https://github.com/michalsnik/aos) idea, rebuilt for the Astro era.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ npx astro add astro-reveal
17
+ ```
18
+
19
+ Or manually:
20
+
21
+ ```sh
22
+ npm install astro-reveal
23
+ ```
24
+
25
+ ```js
26
+ // astro.config.mjs
27
+ import { defineConfig } from "astro/config";
28
+ import reveal from "astro-reveal";
29
+
30
+ export default defineConfig({
31
+ integrations: [reveal()],
32
+ });
33
+ ```
34
+
35
+ That's it. The integration injects the stylesheet for you — no manual CSS import needed.
36
+
37
+ ## Usage
38
+
39
+ Use the component:
40
+
41
+ ```astro
42
+ ---
43
+ import Reveal from "astro-reveal/Reveal.astro";
44
+ ---
45
+ <Reveal animation="up">
46
+ <h1>I rise into place as you scroll.</h1>
47
+ </Reveal>
48
+
49
+ <Reveal animation="scale" distance="3rem" as="section">
50
+ <p>Render as any tag with `as`.</p>
51
+ </Reveal>
52
+ ```
53
+
54
+ Or the raw attribute (works on anything, including markup from other frameworks):
55
+
56
+ ```html
57
+ <div data-reveal="fade">Hello</div>
58
+ <img data-reveal="left" src="/photo.jpg" alt="" />
59
+ ```
60
+
61
+ ### Animations
62
+
63
+ | Value | Effect |
64
+ | ------- | --------------------------------------- |
65
+ | `up` | rises into place (starts below) |
66
+ | `down` | drops into place (starts above) |
67
+ | `left` | slides left (starts to the right) |
68
+ | `right` | slides right (starts to the left) |
69
+ | `fade` | opacity only |
70
+ | `scale` | scales up from slightly smaller |
71
+ | `blur` | un-blurs into focus |
72
+
73
+ ## The two engines
74
+
75
+ You pick the engine; the API stays identical.
76
+
77
+ | `mode` | Runtime JS | Behaviour | Browser support |
78
+ | -------------------- | ---------- | ---------------------------------- | ------------------------------------------- |
79
+ | `"scroll"` (default) | **none** | scrubbed — reverses on scroll up | animates where supported, static elsewhere |
80
+ | `"observer"` | ~0.6 KB | plays **once** and stays | everywhere |
81
+ | `"auto"` | ~0.6 KB | native where supported, JS fallback | identical everywhere |
82
+
83
+ ```js
84
+ reveal({ mode: "observer" }); // the "on steroids" mode
85
+ ```
86
+
87
+ Why this matters: in `"scroll"` mode the hidden ("from") state only exists inside an `@supports (animation-timeline: view())` block. A browser that can't animate it **never hides the content** — so there is no flash and nothing can get stuck invisible. The trade-off is that scroll-driven animations are *scrubbed*: scroll back up and they play in reverse. If you want "appear once and stay put", switch to `"observer"`.
88
+
89
+ ## Options
90
+
91
+ ```ts
92
+ reveal({
93
+ mode: "scroll", // "scroll" | "observer" | "auto"
94
+ once: true, // observer/auto: reveal once and keep it
95
+ threshold: 0.15, // observer/auto: IntersectionObserver threshold
96
+ rootMargin: "0px 0px -10% 0px", // observer/auto: fire slightly before the fold
97
+ });
98
+ ```
99
+
100
+ ## Customisation
101
+
102
+ Tune per element with CSS custom properties (via the component or inline style):
103
+
104
+ ```html
105
+ <div data-reveal="up" style="--reveal-distance: 4rem; --reveal-duration: 1s;"></div>
106
+ ```
107
+
108
+ | Variable | Default | Applies to |
109
+ | -------------------- | ------------------------------ | ----------------- |
110
+ | `--reveal-distance` | `1.5rem` | up/down/left/right |
111
+ | `--reveal-scale` | `0.94` | scale |
112
+ | `--reveal-blur` | `8px` | blur |
113
+ | `--reveal-duration` | `700ms` | observer mode |
114
+ | `--reveal-easing` | `cubic-bezier(.16,1,.3,1)` | observer mode |
115
+ | `--reveal-index` | `0` | stagger position |
116
+ | `--reveal-stagger` | `90ms` | observer mode |
117
+
118
+ Set them globally by targeting `[data-reveal]` in your own CSS. Everything ships inside `@layer astro-reveal`, so your styles always win without specificity battles.
119
+
120
+ ### Stagger
121
+
122
+ ```astro
123
+ {items.map((item, i) => <Reveal animation="up" index={i}>{item}</Reveal>)}
124
+ ```
125
+
126
+ In `observer` mode each item gets `transition-delay: index * --reveal-stagger` for a crisp cascade. In `scroll` mode stagger is approximated by nudging each item's scroll range (grouped staggers are crisper in `observer` mode).
127
+
128
+ ## Accessibility
129
+
130
+ Users with `prefers-reduced-motion: reduce` see content in its final state immediately, in every mode. This is built into the CSS, not bolted on — there's no configuration to forget.
131
+
132
+ ## Browser support
133
+
134
+ Native scroll-driven animations (`animation-timeline: view()`) ship in Chromium-based browsers. Firefox and Safari support has been moving — **check [caniuse.com/css-scroll-timeline](https://caniuse.com/?search=animation-timeline) for the current state** before relying on purist mode for a specific audience.
135
+
136
+ - If you target only modern Chromium → `"scroll"` (zero JS).
137
+ - If you need identical behaviour everywhere → `"auto"` (native where it can, JS where it can't).
138
+ - If you specifically want "reveal once and stay" → `"observer"`.
139
+
140
+ ## How it works
141
+
142
+ A tiny head-inline script (only injected in `observer`/`auto` modes) decides which engine runs by toggling a single `reveal-js` class on `<html>` before paint. The two CSS rule sets — one scoped to `html:not(.reveal-js)` (native), one to `html.reveal-js` (JS) — are therefore mutually exclusive by construction. No double-animating, no race conditions.
143
+
144
+ ## License
145
+
146
+ MIT © Nicolás Picotto
package/Reveal.astro ADDED
@@ -0,0 +1,54 @@
1
+ ---
2
+ /*
3
+ * <Reveal> — ergonomic wrapper over the `data-reveal` attribute.
4
+ * Works the same under both engines because it only sets the attribute and a
5
+ * few CSS custom properties; the integration's CSS/JS does the rest.
6
+ *
7
+ * <Reveal animation="up">…</Reveal>
8
+ * <Reveal animation="scale" distance="3rem" index={2} as="section">…</Reveal>
9
+ *
10
+ * For raw HTML (no component), just use the attribute directly:
11
+ * <div data-reveal="fade">…</div>
12
+ */
13
+ export type RevealAnimation = "up" | "down" | "left" | "right" | "fade" | "scale" | "blur";
14
+
15
+ interface Props {
16
+ /** Direction the element travels as it appears. @default "up" */
17
+ animation?: RevealAnimation;
18
+ /** Element/tag to render. @default "div" */
19
+ as?: string;
20
+ /** Travel distance (up/down/left/right) e.g. "2rem". */
21
+ distance?: string;
22
+ /** Stagger position within a group (0,1,2,…). */
23
+ index?: number;
24
+ /** Transition duration in observer mode, e.g. "500ms". */
25
+ duration?: string;
26
+ class?: string;
27
+ [key: string]: unknown;
28
+ }
29
+
30
+ const {
31
+ animation = "up",
32
+ as: Tag = "div",
33
+ distance,
34
+ index,
35
+ duration,
36
+ class: className,
37
+ style: userStyle,
38
+ ...rest
39
+ } = Astro.props;
40
+
41
+ const vars = [
42
+ distance ? `--reveal-distance:${distance}` : "",
43
+ index != null ? `--reveal-index:${index}` : "",
44
+ duration ? `--reveal-duration:${duration}` : "",
45
+ ]
46
+ .filter(Boolean)
47
+ .join(";");
48
+
49
+ const style = [vars, userStyle].filter(Boolean).join(";") || undefined;
50
+ ---
51
+
52
+ <Tag data-reveal={animation} class={className} style={style} {...rest}>
53
+ <slot />
54
+ </Tag>
@@ -0,0 +1,34 @@
1
+ import type { AstroIntegration } from "astro";
2
+ export type RevealMode = "scroll" | "observer" | "auto";
3
+ export interface RevealOptions {
4
+ /**
5
+ * Which engine to use.
6
+ * - "scroll" (default) Native CSS scroll-driven animations. Zero runtime
7
+ * JS. Scrubbed: reverses as you scroll back up. Browsers
8
+ * without support show content normally (no animation, no FOUC).
9
+ * - "observer" IntersectionObserver. ~1KB JS. Plays once and stays. Works
10
+ * everywhere. This is the "on steroids" mode.
11
+ * - "auto" Native where supported, falls back to observer where not, so
12
+ * it looks identical across browsers (always ships the ~1KB).
13
+ * @default "scroll"
14
+ */
15
+ mode?: RevealMode;
16
+ /**
17
+ * Observer/auto only. Reveal once and keep it revealed (true), or re-hide
18
+ * when the element leaves the viewport (false).
19
+ * @default true
20
+ */
21
+ once?: boolean;
22
+ /**
23
+ * Observer/auto only. IntersectionObserver threshold.
24
+ * @default 0.15
25
+ */
26
+ threshold?: number;
27
+ /**
28
+ * Observer/auto only. IntersectionObserver rootMargin. The default trims the
29
+ * bottom so reveals fire slightly before the element hits the fold.
30
+ * @default "0px 0px -10% 0px"
31
+ */
32
+ rootMargin?: string;
33
+ }
34
+ export default function astroReveal(options?: RevealOptions): AstroIntegration;
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ import { readFileSync } from "node:fs";
2
+ // Read our own package name so injected imports keep resolving even if the
3
+ // package is renamed/forked. (dist/index.js -> ../package.json = package root.)
4
+ const PKG = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
5
+ const DEFAULTS = {
6
+ mode: "scroll",
7
+ once: true,
8
+ threshold: 0.15,
9
+ rootMargin: "0px 0px -10% 0px",
10
+ };
11
+ function headInline(opts) {
12
+ const runtime = {
13
+ mode: opts.mode,
14
+ once: opts.once,
15
+ threshold: opts.threshold,
16
+ rootMargin: opts.rootMargin,
17
+ };
18
+ // Runs in <head> before paint. Decides the engine by toggling the marker
19
+ // class, so the CSS rule sets stay mutually exclusive. No marker => purist.
20
+ return `(function(){var o=${JSON.stringify(runtime)};window.__ASTRO_REVEAL__=o;try{var s=window.CSS&&CSS.supports&&CSS.supports("animation-timeline: view()");if(o.mode==="observer"||(o.mode==="auto"&&!s)){document.documentElement.classList.add("reveal-js");}}catch(e){document.documentElement.classList.add("reveal-js");}})();`;
21
+ }
22
+ export default function astroReveal(options = {}) {
23
+ const config = { ...DEFAULTS, ...options };
24
+ return {
25
+ name: PKG.name,
26
+ hooks: {
27
+ "astro:config:setup": ({ injectScript, logger }) => {
28
+ // The stylesheet is always needed. Injecting it at the `page-ssr`
29
+ // stage associates it with each page's server render, so Astro emits
30
+ // the <link>/inline <style> reliably — and ships ZERO client JS.
31
+ injectScript("page-ssr", `import ${JSON.stringify(`${PKG.name}/styles.css`)};`);
32
+ if (config.mode !== "scroll") {
33
+ injectScript("head-inline", headInline(config));
34
+ injectScript("page", `import ${JSON.stringify(`${PKG.name}/observer`)};`);
35
+ }
36
+ logger.info(`enabled in "${config.mode}" mode` +
37
+ (config.mode === "scroll" ? " (zero runtime JS)" : ` (once: ${config.once})`));
38
+ },
39
+ },
40
+ };
41
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ /*
2
+ * astro-reveal — observer runtime ("on steroids" mode)
3
+ * Injected only when mode is "observer" or "auto". Loaded deferred.
4
+ *
5
+ * Reads config from window.__ASTRO_REVEAL__ (set by the head-inline script,
6
+ * which also decides whether the `.reveal-js` marker belongs on <html>).
7
+ *
8
+ * Responsibilities — the four things AOS gets wrong:
9
+ * 1. Never touch anything unless this browser is actually using the JS
10
+ * engine (the `.reveal-js` marker is present). In "auto" mode a browser
11
+ * with native scroll-timeline support has no marker, so we no-op.
12
+ * 2. Re-scan after Astro View Transitions (`astro:page-load`).
13
+ * 3. Watch for late-hydrated islands via MutationObserver.
14
+ * 4. Bail entirely under prefers-reduced-motion (CSS already shows content).
15
+ */
16
+ const REVEALED = "is-revealed";
17
+ function getConfig() {
18
+ const w = window;
19
+ return (w.__ASTRO_REVEAL__ ?? {
20
+ mode: "observer",
21
+ once: true,
22
+ threshold: 0.15,
23
+ rootMargin: "0px 0px -10% 0px",
24
+ });
25
+ }
26
+ function shouldRun() {
27
+ // Only the JS engine touches the DOM. If the marker isn't there
28
+ // (purist / auto-on-supporting-browser), the CSS engine owns this page.
29
+ if (!document.documentElement.classList.contains("reveal-js"))
30
+ return false;
31
+ // Respect the user. The hidden state is gated by no-preference in CSS,
32
+ // so content is already visible — nothing for us to do.
33
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)
34
+ return false;
35
+ return true;
36
+ }
37
+ let observer = null;
38
+ let mutationObserver = null;
39
+ const tracked = new WeakSet();
40
+ function reveal(el) {
41
+ el.classList.add(REVEALED);
42
+ }
43
+ function observe(el, config) {
44
+ if (tracked.has(el) || el.classList.contains(REVEALED))
45
+ return;
46
+ tracked.add(el);
47
+ observer.observe(el);
48
+ }
49
+ function collect(root = document) {
50
+ return Array.from(root.querySelectorAll("[data-reveal]"));
51
+ }
52
+ function setup() {
53
+ if (!shouldRun())
54
+ return;
55
+ const config = getConfig();
56
+ observer?.disconnect();
57
+ observer = new IntersectionObserver((entries) => {
58
+ for (const entry of entries) {
59
+ if (!entry.isIntersecting) {
60
+ if (!config.once)
61
+ entry.target.classList.remove(REVEALED);
62
+ continue;
63
+ }
64
+ reveal(entry.target);
65
+ if (config.once)
66
+ observer.unobserve(entry.target);
67
+ }
68
+ }, { threshold: config.threshold, rootMargin: config.rootMargin });
69
+ for (const el of collect())
70
+ observe(el, config);
71
+ // Watch for elements added after first paint (hydrated islands, CMS
72
+ // fragments swapped in, client:visible components, etc.)
73
+ mutationObserver?.disconnect();
74
+ mutationObserver = new MutationObserver((mutations) => {
75
+ for (const m of mutations) {
76
+ for (const node of Array.from(m.addedNodes)) {
77
+ if (!(node instanceof Element))
78
+ continue;
79
+ if (node.matches?.("[data-reveal]"))
80
+ observe(node, config);
81
+ for (const child of collect(node))
82
+ observe(child, config);
83
+ }
84
+ }
85
+ });
86
+ mutationObserver.observe(document.body, { childList: true, subtree: true });
87
+ }
88
+ function init() {
89
+ if (document.readyState === "loading") {
90
+ document.addEventListener("DOMContentLoaded", setup, { once: true });
91
+ }
92
+ else {
93
+ setup();
94
+ }
95
+ }
96
+ init();
97
+ // Astro View Transitions: the DOM is swapped, observers are stale. Re-arm.
98
+ document.addEventListener("astro:page-load", setup);
99
+ export {};
@@ -0,0 +1,129 @@
1
+ /*
2
+ * astro-reveal — scroll reveal animations for Astro
3
+ * --------------------------------------------------
4
+ * Two engines, one API, never both at once:
5
+ *
6
+ * PURIST (default) : native CSS scroll-driven animations.
7
+ * 0 bytes of runtime JS. Scrubbed (reverses on scroll up).
8
+ * Active on html:not(.reveal-js) + @supports view().
9
+ *
10
+ * OBSERVER (steroids): IntersectionObserver toggles `.is-revealed`.
11
+ * ~1KB JS. Plays once and stays. Works everywhere.
12
+ * Active when html.reveal-js is present.
13
+ *
14
+ * The `.reveal-js` marker is added (or not) by a tiny head-inline script
15
+ * that the integration injects only when the chosen mode needs it. That
16
+ * single class is what flips a browser from one engine to the other, so the
17
+ * two rule sets below are mutually exclusive by construction.
18
+ *
19
+ * Everything lives in `@layer astro-reveal` so your own styles always win
20
+ * without specificity fights.
21
+ */
22
+
23
+ @layer astro-reveal {
24
+
25
+ /* Tunable knobs. Override per-element via inline style or globally here. */
26
+ [data-reveal] {
27
+ --reveal-distance: 1.5rem;
28
+ --reveal-blur: 8px;
29
+ --reveal-scale: 0.94;
30
+ --reveal-duration: 700ms;
31
+ --reveal-easing: cubic-bezier(0.16, 1, 0.3, 1); /* easeOutExpo-ish */
32
+ --reveal-index: 0; /* stagger position within a group */
33
+ --reveal-stagger: 90ms; /* per-index delay (observer mode) */
34
+ }
35
+
36
+ /* ============================================================
37
+ * PURIST ENGINE — native scroll-driven animations
38
+ * Only on browsers that support it AND when JS engine is off.
39
+ * If unsupported, NOTHING here applies, so content shows normally.
40
+ * => FOUC / hidden-forever content is structurally impossible.
41
+ * ============================================================ */
42
+ @media (prefers-reduced-motion: no-preference) {
43
+ @supports (animation-timeline: view()) {
44
+ :where(html:not(.reveal-js)) [data-reveal] {
45
+ animation: linear both;
46
+ animation-timeline: view();
47
+ /* Best-effort stagger: nudge the scroll window per index.
48
+ Grouped stagger is crisper in observer mode. */
49
+ animation-range:
50
+ entry calc(0% + var(--reveal-index) * 4%)
51
+ cover calc(30% + var(--reveal-index) * 4%);
52
+ }
53
+
54
+ :where(html:not(.reveal-js)) [data-reveal="up"] { animation-name: reveal-up; }
55
+ :where(html:not(.reveal-js)) [data-reveal="down"] { animation-name: reveal-down; }
56
+ :where(html:not(.reveal-js)) [data-reveal="left"] { animation-name: reveal-left; }
57
+ :where(html:not(.reveal-js)) [data-reveal="right"] { animation-name: reveal-right; }
58
+ :where(html:not(.reveal-js)) [data-reveal="fade"] { animation-name: reveal-fade; }
59
+ :where(html:not(.reveal-js)) [data-reveal="scale"] { animation-name: reveal-scale; }
60
+ :where(html:not(.reveal-js)) [data-reveal="blur"] { animation-name: reveal-blur; }
61
+ }
62
+ }
63
+
64
+ /* ============================================================
65
+ * OBSERVER ENGINE — IntersectionObserver + class toggle
66
+ * Hidden state is scoped under `.reveal-js`, which is only ever
67
+ * added by JS. So if JS is disabled, the class never appears and
68
+ * content stays visible. Reduced-motion users never get hidden.
69
+ * ============================================================ */
70
+ @media (prefers-reduced-motion: no-preference) {
71
+ html.reveal-js [data-reveal] {
72
+ opacity: 0;
73
+ transition-property: opacity, transform, filter;
74
+ transition-duration: var(--reveal-duration);
75
+ transition-timing-function: var(--reveal-easing);
76
+ transition-delay: calc(var(--reveal-index) * var(--reveal-stagger));
77
+ will-change: opacity, transform;
78
+ }
79
+
80
+ html.reveal-js [data-reveal="up"] { transform: translateY(var(--reveal-distance)); }
81
+ html.reveal-js [data-reveal="down"] { transform: translateY(calc(-1 * var(--reveal-distance))); }
82
+ html.reveal-js [data-reveal="left"] { transform: translateX(var(--reveal-distance)); }
83
+ html.reveal-js [data-reveal="right"] { transform: translateX(calc(-1 * var(--reveal-distance))); }
84
+ html.reveal-js [data-reveal="scale"] { transform: scale(var(--reveal-scale)); }
85
+ html.reveal-js [data-reveal="blur"] { filter: blur(var(--reveal-blur)); }
86
+ /* "fade" needs no transform, opacity:0 above is enough */
87
+
88
+ /* The revealed state: everything snaps back to neutral. */
89
+ html.reveal-js [data-reveal].is-revealed {
90
+ opacity: 1;
91
+ transform: none;
92
+ filter: none;
93
+ }
94
+ }
95
+
96
+ /* ============================================================
97
+ * KEYFRAMES (shared by the purist engine)
98
+ * Naming follows AOS: the value is the direction the element
99
+ * TRAVELS as it appears. `up` rises into place (starts below).
100
+ * ============================================================ */
101
+ @keyframes reveal-up {
102
+ from { opacity: 0; transform: translateY(var(--reveal-distance)); }
103
+ to { opacity: 1; transform: translateY(0); }
104
+ }
105
+ @keyframes reveal-down {
106
+ from { opacity: 0; transform: translateY(calc(-1 * var(--reveal-distance))); }
107
+ to { opacity: 1; transform: translateY(0); }
108
+ }
109
+ @keyframes reveal-left {
110
+ from { opacity: 0; transform: translateX(var(--reveal-distance)); }
111
+ to { opacity: 1; transform: translateX(0); }
112
+ }
113
+ @keyframes reveal-right {
114
+ from { opacity: 0; transform: translateX(calc(-1 * var(--reveal-distance))); }
115
+ to { opacity: 1; transform: translateX(0); }
116
+ }
117
+ @keyframes reveal-fade {
118
+ from { opacity: 0; }
119
+ to { opacity: 1; }
120
+ }
121
+ @keyframes reveal-scale {
122
+ from { opacity: 0; transform: scale(var(--reveal-scale)); }
123
+ to { opacity: 1; transform: scale(1); }
124
+ }
125
+ @keyframes reveal-blur {
126
+ from { opacity: 0; filter: blur(var(--reveal-blur)); }
127
+ to { opacity: 1; filter: blur(0); }
128
+ }
129
+ }
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "astro-reveal",
3
+ "version": "0.1.0",
4
+ "description": "Scroll reveal animations for Astro. Zero-JS by default via native CSS scroll-driven animations, with an opt-in IntersectionObserver engine. UI-framework agnostic.",
5
+ "type": "module",
6
+ "author": "Nicolás Picoto",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "astro",
10
+ "astro-integration",
11
+ "astro-component",
12
+ "withastro",
13
+ "animation",
14
+ "scroll",
15
+ "reveal",
16
+ "scroll-driven-animations",
17
+ "aos",
18
+ "intersection-observer"
19
+ ],
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "./Reveal.astro": "./Reveal.astro",
26
+ "./observer": "./dist/runtime/observer.js",
27
+ "./styles.css": "./dist/styles/reveal.css"
28
+ },
29
+ "main": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "files": [
32
+ "dist",
33
+ "Reveal.astro",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc && node scripts/copy-assets.mjs",
39
+ "dev": "tsc --watch",
40
+ "typecheck": "tsc --noEmit",
41
+ "changeset": "changeset",
42
+ "version": "changeset version",
43
+ "release": "npm run build && changeset publish",
44
+ "prepublishOnly": "npm run build"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "peerDependencies": {
50
+ "astro": ">=4.0.0"
51
+ },
52
+ "devDependencies": {
53
+ "@changesets/cli": "^2.31.0",
54
+ "@types/node": "^20.11.0",
55
+ "astro": "^4.16.0",
56
+ "typescript": "^5.4.0"
57
+ },
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "https://github.com/nicopicoto/astro-reveal.git"
61
+ }
62
+ }