Skip to content

Commit 8ba530e

Browse files
authored
Merge pull request #273 from pathsim/feat/radial-theme-transition
Add radial theme transition
2 parents 7680f07 + ac8600c commit 8ba530e

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

src/app.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@
55
@font-face { font-family: 'JetBrains Mono'; font-weight: 400; font-style: normal; font-display: swap; src: url('/fonts/JetBrainsMono-Regular.woff2') format('woff2'); }
66
@font-face { font-family: 'JetBrains Mono'; font-weight: 500; font-style: normal; font-display: swap; src: url('/fonts/JetBrainsMono-Medium.woff2') format('woff2'); }
77

8+
/* Theme transition — radial reveal via View Transitions API */
9+
::view-transition-old(root),
10+
::view-transition-new(root) {
11+
animation: none;
12+
mix-blend-mode: normal;
13+
}
14+
::view-transition-old(root) {
15+
z-index: 1;
16+
}
17+
::view-transition-new(root) {
18+
z-index: 9999;
19+
}
20+
821
/* Modern Design System */
922
:root {
1023
/* ===== SURFACES (2-tier elevation) ===== */

src/routes/+page.svelte

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,35 @@
5555
import Tooltip, { tooltip } from '$lib/components/Tooltip.svelte';
5656
import { isInputFocused } from '$lib/utils/focus';
5757
58+
// Theme toggle button ref for radial transition origin
59+
let themeToggleBtn: HTMLButtonElement;
60+
61+
function toggleThemeWithTransition(e?: MouseEvent) {
62+
const apply = () => themeStore.toggle();
63+
64+
if (!document.startViewTransition) { apply(); return; }
65+
66+
let x: number, y: number;
67+
if (e) {
68+
x = e.clientX; y = e.clientY;
69+
} else if (themeToggleBtn) {
70+
const rect = themeToggleBtn.getBoundingClientRect();
71+
x = rect.left + rect.width / 2;
72+
y = rect.top + rect.height / 2;
73+
} else {
74+
apply(); return;
75+
}
76+
77+
const maxRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
78+
const transition = document.startViewTransition(apply);
79+
transition.ready.then(() => {
80+
document.documentElement.animate(
81+
{ clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${maxRadius}px at ${x}px ${y}px)`] },
82+
{ duration: 500, easing: 'ease-out', pseudoElement: '::view-transition-new(root)' }
83+
);
84+
});
85+
}
86+
5887
// Track mouse position for paste operations
5988
let mousePosition = $state({ x: 0, y: 0 });
6089
@@ -731,7 +760,7 @@
731760
return;
732761
case 't':
733762
event.preventDefault();
734-
themeStore.toggle();
763+
toggleThemeWithTransition();
735764
return;
736765
case '+':
737766
case '=':
@@ -1197,7 +1226,8 @@
11971226
</button>
11981227
<button
11991228
class="toggle-btn"
1200-
onclick={() => themeStore.toggle()}
1229+
bind:this={themeToggleBtn}
1230+
onclick={(e) => toggleThemeWithTransition(e)}
12011231
use:tooltip={{ text: currentTheme === 'dark' ? 'Light mode' : 'Dark mode', shortcut: "T", position: "right" }}
12021232
aria-label="Toggle theme"
12031233
>

0 commit comments

Comments
 (0)