@maxigarcia/theme-transitions 0.2.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 ADDED
@@ -0,0 +1,61 @@
1
+ # @maxigarcia/theme-transitions
2
+
3
+ Lightweight, CSS-first theme transitions for the web. Wrap your theme change in the [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) and reveal the new theme with a **circular** or **sweep** animation—no animation framework required.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @maxigarcia/theme-transitions
9
+ ```
10
+
11
+ ## Animations
12
+
13
+ | Technique | Docs |
14
+ | --------------- | ---------------------------------------------------------------- |
15
+ | Circular reveal | [src/circular-reveal/README.md](./src/circular-reveal/README.md) |
16
+ | Sweep reveal | [src/sweep-reveal/README.md](./src/sweep-reveal/README.md) |
17
+
18
+ Each module has its own README with setup, API, and usage examples.
19
+
20
+ ## Package exports
21
+
22
+ | Import | Purpose |
23
+ | --------------------------------------------------------- | ------------------------------------------------------------ |
24
+ | `@maxigarcia/theme-transitions` | `onCircularRevealAnimation`, `onSweepRevealAnimation`, types |
25
+ | `@maxigarcia/theme-transitions/circular-reveal/index.css` | Styles for circular reveal |
26
+ | `@maxigarcia/theme-transitions/sweep-reveal/index.css` | Styles for sweep reveal |
27
+
28
+ ## Live demo
29
+
30
+ See the [app package](../app/) in this monorepo for an interactive landing page with both techniques.
31
+
32
+ ```bash
33
+ # from monorepo root
34
+ npm install
35
+ npm run dev
36
+ ```
37
+
38
+ ## Development
39
+
40
+ From the monorepo root:
41
+
42
+ ```bash
43
+ npm run build -w @maxigarcia/theme-transitions
44
+ # or: nx run @maxigarcia/theme-transitions:build
45
+ npm run lint -w @maxigarcia/theme-transitions
46
+ ```
47
+
48
+ ### Versioning & publish (Nx Release)
49
+
50
+ Use [Conventional Commits](https://www.conventionalcommits.org/) for changes under this package (`feat`, `fix`, `perf`, etc.). From the monorepo root:
51
+
52
+ ```bash
53
+ npm run version-packages # bump version + changelog (no publish)
54
+ npm run release # publish to npm
55
+ ```
56
+
57
+ CI on `main` versions and tags both projects, but only publishes this package to npm. See the [root README](../../README.md#releasing).
58
+
59
+ ## Browser support
60
+
61
+ Requires [View Transitions](https://caniuse.com/view-transitions) for animated reveals. Unsupported browsers still apply the theme change without animation.
@@ -0,0 +1,49 @@
1
+ @keyframes circular-reveal {
2
+ from {
3
+ clip-path: circle(0% at var(--theme-reveal-x, 50%) var(--theme-reveal-y, 50%));
4
+ }
5
+ to {
6
+ clip-path: circle(150% at var(--theme-reveal-x, 50%) var(--theme-reveal-y, 50%));
7
+ }
8
+ }
9
+
10
+ @keyframes circular-reveal-blur {
11
+ from {
12
+ clip-path: circle(0% at var(--theme-reveal-x, 50%) var(--theme-reveal-y, 50%));
13
+ filter: blur(var(--theme-reveal-blur, 12px));
14
+ }
15
+ to {
16
+ clip-path: circle(150% at var(--theme-reveal-x, 50%) var(--theme-reveal-y, 50%));
17
+ filter: blur(0);
18
+ }
19
+ }
20
+
21
+ html.circular-reveal::view-transition-old(root),
22
+ html.circular-reveal::view-transition-new(root) {
23
+ mix-blend-mode: normal;
24
+ }
25
+
26
+ html.circular-reveal::view-transition-old(root) {
27
+ animation: none;
28
+ z-index: 0;
29
+ }
30
+
31
+ html.circular-reveal::view-transition-new(root) {
32
+ z-index: 1;
33
+ animation: circular-reveal 0.5s ease-in-out;
34
+ }
35
+
36
+ html.circular-reveal--blur-circle::view-transition-new(root) {
37
+ animation: circular-reveal-blur 0.5s ease-in-out;
38
+ }
39
+
40
+ /* Reduce motion */
41
+ @media (prefers-reduced-motion: reduce) {
42
+ html.circular-reveal::view-transition-new(root) {
43
+ animation: none;
44
+ }
45
+
46
+ html.circular-reveal--blur-circle::view-transition-new(root) {
47
+ animation: none;
48
+ }
49
+ }
@@ -0,0 +1,12 @@
1
+ export interface ThemeRevealOrigin {
2
+ clientX: number;
3
+ clientY: number;
4
+ }
5
+ export interface CircularRevealOptions {
6
+ /** Blur inside the expanding circle; clears as the reveal finishes. */
7
+ blurCircle?: boolean;
8
+ /** Blur radius when `blurCircle` is true. Defaults to `12px`. */
9
+ blurAmount?: string;
10
+ }
11
+ export declare function onCircularRevealAnimation(apply: () => void, origin: ThemeRevealOrigin, options?: CircularRevealOptions): void;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/circular-reveal/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,IAAI,EACjB,MAAM,EAAE,iBAAiB,EACzB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CAmCN"}
@@ -0,0 +1,27 @@
1
+ import { $html, $onTransitionEnd } from '../utils/index.js';
2
+ export function onCircularRevealAnimation(apply, origin, options) {
3
+ const x = (origin.clientX / window.innerWidth) * 100;
4
+ const y = (origin.clientY / window.innerHeight) * 100;
5
+ const html = $html();
6
+ html.classList.add('circular-reveal');
7
+ if (options?.blurCircle) {
8
+ html.classList.add('circular-reveal--blur-circle');
9
+ if (options.blurAmount) {
10
+ html.style.setProperty('--theme-reveal-blur', options.blurAmount);
11
+ }
12
+ }
13
+ html.style.setProperty('--theme-reveal-x', `${x}%`);
14
+ html.style.setProperty('--theme-reveal-y', `${y}%`);
15
+ if (!document.startViewTransition) {
16
+ apply();
17
+ return;
18
+ }
19
+ document.startViewTransition(apply);
20
+ const animationComplete = () => {
21
+ html.style.removeProperty('--theme-reveal-x');
22
+ html.style.removeProperty('--theme-reveal-y');
23
+ html.style.removeProperty('--theme-reveal-blur');
24
+ html.classList.remove('circular-reveal', 'circular-reveal--blur-circle');
25
+ };
26
+ $onTransitionEnd(animationComplete, 600);
27
+ }
@@ -0,0 +1,3 @@
1
+ export * from './circular-reveal/index.js';
2
+ export * from './sweep-reveal/index.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './circular-reveal/index.js';
2
+ export * from './sweep-reveal/index.js';
@@ -0,0 +1,30 @@
1
+ @keyframes sweep-reveal {
2
+ from {
3
+ clip-path: var(--theme-reveal-direction-from);
4
+ }
5
+ to {
6
+ clip-path: var(--theme-reveal-direction-to);
7
+ }
8
+ }
9
+
10
+ html.sweep-reveal::view-transition-old(root),
11
+ html.sweep-reveal::view-transition-new(root) {
12
+ animation: none;
13
+ mix-blend-mode: normal;
14
+ }
15
+
16
+ html.sweep-reveal::view-transition-old(root) {
17
+ z-index: 0;
18
+ }
19
+
20
+ html.sweep-reveal::view-transition-new(root) {
21
+ z-index: 1;
22
+ animation: sweep-reveal 0.45s ease-in-out forwards;
23
+ }
24
+
25
+ /* Reduce motion */
26
+ @media (prefers-reduced-motion: reduce) {
27
+ html.sweep-reveal::view-transition-new(root) {
28
+ animation: none;
29
+ }
30
+ }
@@ -0,0 +1,3 @@
1
+ export type SweepDirection = 'up' | 'down' | 'left' | 'right' | 'corner-top-left' | 'corner-top-right' | 'corner-bottom-left' | 'corner-bottom-right';
2
+ export declare function onSweepRevealAnimation(apply: () => void, direction?: SweepDirection): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sweep-reveal/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GACtB,IAAI,GACF,MAAM,GACN,MAAM,GACN,OAAO,GACP,iBAAiB,GACjB,kBAAkB,GAClB,oBAAoB,GACpB,qBAAqB,CAAC;AAE5B,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,IAAI,EACjB,SAAS,GAAE,cAAqB,GAC/B,IAAI,CAkCN"}
@@ -0,0 +1,29 @@
1
+ import { $html, $onTransitionEnd } from '../utils/index.js';
2
+ export function onSweepRevealAnimation(apply, direction = 'up') {
3
+ if (!document.startViewTransition) {
4
+ apply();
5
+ return;
6
+ }
7
+ const html = $html();
8
+ const directions = {
9
+ 'up': ['inset(0 0 100% 0)', 'inset(0 0 0 0)'],
10
+ 'down': ['inset(100% 0 0 0)', 'inset(0 0 0 0)'],
11
+ 'left': ['inset(0 100% 0 0)', 'inset(0 0 0 0)'],
12
+ 'right': ['inset(0 0 0 100%)', 'inset(0 0 0 0)'],
13
+ 'corner-top-left': ['inset(0 100% 100% 0)', 'inset(0 0 0 0)'],
14
+ 'corner-top-right': ['inset(0 0 100% 100%)', 'inset(0 0 0 0)'],
15
+ 'corner-bottom-left': ['inset(100% 100% 0 0)', 'inset(0 0 0 0)'],
16
+ 'corner-bottom-right': ['inset(100% 0 0 100%)', 'inset(0 0 0 0)'],
17
+ };
18
+ const [directionFrom, directionTo] = directions[direction] ?? directions.up;
19
+ html.classList.add('sweep-reveal');
20
+ html.style.setProperty('--theme-reveal-direction-from', directionFrom);
21
+ html.style.setProperty('--theme-reveal-direction-to', directionTo);
22
+ document.startViewTransition(apply);
23
+ const animationComplete = () => {
24
+ html.classList.remove('sweep-reveal');
25
+ html.style.removeProperty('--theme-reveal-direction-from');
26
+ html.style.removeProperty('--theme-reveal-direction-to');
27
+ };
28
+ $onTransitionEnd(animationComplete, 600);
29
+ }
@@ -0,0 +1,3 @@
1
+ export declare function $html(): HTMLElement;
2
+ export declare function $onTransitionEnd(callback: () => void, duration?: number): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,gBAEpB;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,QAAQ,GAAE,MAAY,QAI5E"}
@@ -0,0 +1,8 @@
1
+ export function $html() {
2
+ return document.documentElement;
3
+ }
4
+ export function $onTransitionEnd(callback, duration = 600) {
5
+ setTimeout(() => {
6
+ callback();
7
+ }, duration);
8
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@maxigarcia/theme-transitions",
3
+ "type": "module",
4
+ "version": "0.2.0",
5
+ "license": "ISC",
6
+ "homepage": "https://github.com/MaxiGarcia13/js-theme-animation-monorepo/tree/main/packages/theme-transitions#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/MaxiGarcia13/js-theme-animation-monorepo.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/MaxiGarcia13/js-theme-animation-monorepo/issues"
13
+ },
14
+ "keywords": [
15
+ "theme",
16
+ "transition",
17
+ "view transitions",
18
+ "view transitions api",
19
+ "utility",
20
+ "library",
21
+ "css",
22
+ "javascript",
23
+ "web",
24
+ "dark mode",
25
+ "light mode",
26
+ "dark theme",
27
+ "light theme"
28
+ ],
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ },
34
+ "./circular-reveal/index.css": "./dist/circular-reveal/index.css",
35
+ "./sweep-reveal/index.css": "./dist/sweep-reveal/index.css"
36
+ },
37
+ "main": "./dist/index.js",
38
+ "types": "./dist/index.d.ts",
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "scripts": {
43
+ "clean": "rm -rf dist",
44
+ "prebuild": "npm run clean",
45
+ "build": "tsc && node scripts/copy-css-files.ts",
46
+ "lint": "eslint .",
47
+ "lint:fix": "eslint --fix .",
48
+ "phoenix": "npm run clean && rm -rf node_modules"
49
+ }
50
+ }