diff --git a/libs/yggdrasil-core/src/style.css b/libs/yggdrasil-core/src/style.css index b95cfc1..0726010 100644 --- a/libs/yggdrasil-core/src/style.css +++ b/libs/yggdrasil-core/src/style.css @@ -1,9 +1,15 @@ /* ========== 主题切换圆形展开(View Transitions API) ========== */ -/* 在 View Transition 期间强制禁用所有 CSS 过渡,确保截图捕获的是最终状态。 - 不要在这里设置 ::view-transition-old/new 的 animation: - 静态 animation: none 会让 new 在注入 @keyframes 之前完全不透明地覆盖 old, - 导致暗→亮方向看不到动画。所有 VT 伪元素样式由 JS 在 vt.ready 中统一注入。 */ +/* mix-blend-mode 不可通过 Web Animations API 设置,只能用 CSS。 + UA 默认 plus-lighter 会导致新旧层颜色叠加发亮,需覆盖为 normal。 + author 样式天然覆盖 UA 样式,无需 !important。 */ +::view-transition-old(root), +::view-transition-new(root) { + mix-blend-mode: normal; +} + +/* 在 View Transition 期间强制禁用所有 CSS 过渡, + 确保 VT 回调中 toggle dark class 后截图捕获的是最终颜色。 */ html.is-theme-transitioning, html.is-theme-transitioning *, html.is-theme-transitioning *::before, diff --git a/libs/yggdrasil-core/src/theme-transition.test.ts b/libs/yggdrasil-core/src/theme-transition.test.ts index 09a1295..f9e1561 100644 --- a/libs/yggdrasil-core/src/theme-transition.test.ts +++ b/libs/yggdrasil-core/src/theme-transition.test.ts @@ -13,18 +13,14 @@ import './index'; describe('startThemeTransition', () => { beforeEach(() => { document.documentElement.classList.remove('dark'); + document.documentElement.classList.remove('is-theme-transitioning'); document.documentElement.style.cssText = ''; - document.head.querySelectorAll('style').forEach((s) => { - if (s.textContent?.includes('tt-')) s.remove(); - }); vi.restoreAllMocks(); }); afterEach(() => { document.documentElement.classList.remove('dark'); + document.documentElement.classList.remove('is-theme-transitioning'); document.documentElement.style.cssText = ''; - document.head.querySelectorAll('style').forEach((s) => { - if (s.textContent?.includes('tt-')) s.remove(); - }); }); it('降级:无 startViewTransition 时,亮→暗(无 dark class 时 add)', () => { @@ -43,7 +39,7 @@ describe('startThemeTransition', () => { expect(document.documentElement.classList.contains('dark')).toBe(false); }); - it('主路径:有 startViewTransition 时调用它,并注入带 keyframes 的 style', async () => { + it('主路径:有 startViewTransition 时调用它,callback 切换 dark class', async () => { const cbRef: { cb: (() => void) | null } = { cb: null }; const readyP = Promise.resolve(); const finishedP = Promise.resolve(); @@ -57,25 +53,38 @@ describe('startThemeTransition', () => { writable: true, }); + // Mock animate on documentElement (happy-dom 不支持 pseudoElement) + const animateSpy = vi.fn(() => ({ finished: Promise.resolve() })); + document.documentElement.animate = animateSpy as unknown as typeof document.documentElement.animate; + window.__startThemeTransition(100, 200); expect(startVT).toHaveBeenCalledTimes(1); - // 样式预注入:在 startViewTransition 之前就已注入