diff --git a/src/app.css b/src/app.css index 84afe66d..042b823e 100644 --- a/src/app.css +++ b/src/app.css @@ -5,6 +5,19 @@ @font-face { font-family: 'JetBrains Mono'; font-weight: 400; font-style: normal; font-display: swap; src: url('/fonts/JetBrainsMono-Regular.woff2') format('woff2'); } @font-face { font-family: 'JetBrains Mono'; font-weight: 500; font-style: normal; font-display: swap; src: url('/fonts/JetBrainsMono-Medium.woff2') format('woff2'); } +/* Theme transition — radial reveal via View Transitions API */ +::view-transition-old(root), +::view-transition-new(root) { + animation: none; + mix-blend-mode: normal; +} +::view-transition-old(root) { + z-index: 1; +} +::view-transition-new(root) { + z-index: 9999; +} + /* Modern Design System */ :root { /* ===== SURFACES (2-tier elevation) ===== */ diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index dd68f66d..2d41326b 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -55,6 +55,35 @@ import Tooltip, { tooltip } from '$lib/components/Tooltip.svelte'; import { isInputFocused } from '$lib/utils/focus'; + // Theme toggle button ref for radial transition origin + let themeToggleBtn: HTMLButtonElement; + + function toggleThemeWithTransition(e?: MouseEvent) { + const apply = () => themeStore.toggle(); + + if (!document.startViewTransition) { apply(); return; } + + let x: number, y: number; + if (e) { + x = e.clientX; y = e.clientY; + } else if (themeToggleBtn) { + const rect = themeToggleBtn.getBoundingClientRect(); + x = rect.left + rect.width / 2; + y = rect.top + rect.height / 2; + } else { + apply(); return; + } + + const maxRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)); + const transition = document.startViewTransition(apply); + transition.ready.then(() => { + document.documentElement.animate( + { clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${maxRadius}px at ${x}px ${y}px)`] }, + { duration: 500, easing: 'ease-out', pseudoElement: '::view-transition-new(root)' } + ); + }); + } + // Track mouse position for paste operations let mousePosition = $state({ x: 0, y: 0 }); @@ -731,7 +760,7 @@ return; case 't': event.preventDefault(); - themeStore.toggle(); + toggleThemeWithTransition(); return; case '+': case '=': @@ -1197,7 +1226,8 @@