fix(theme): 消除首帧主题闪动并柔化明暗过渡
- index.html 首帧按本机偏好设置 dark/light,避免先浅后深 - 实际 scheme 只写 dark/light(之前 auto 会覆盖成浅色) - 切换时给所有表面加 240ms 颜色/边框/阴影过渡,结束后移除 - 背景图深色下淡出而不是 display:none - 页头操作图标与分组导航等设置加载后再渲染,避免先显示后消失
This commit is contained in:
@@ -4,7 +4,15 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="color-scheme" content="light" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<script>
|
||||
// 首帧先按本机偏好上色,避免先浅色再切深色的闪动;站点默认值随后由设置接管。
|
||||
try {
|
||||
var saved = localStorage.getItem('stash-theme')
|
||||
var dark = saved ? saved === 'dark' : matchMedia('(prefers-color-scheme: dark)').matches
|
||||
document.documentElement.dataset.scheme = dark ? 'dark' : 'light'
|
||||
} catch (e) {}
|
||||
</script>
|
||||
<!-- 与 Alright 主题一致的正文字体:霞鹜文楷 -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
|
||||
+27
-2
@@ -22,6 +22,7 @@
|
||||
height: 65vh;
|
||||
background: url('./assets/site-bg.jpg') center top / cover no-repeat;
|
||||
pointer-events: none;
|
||||
transition: opacity 240ms var(--su-ease-in-out);
|
||||
}
|
||||
|
||||
.site-background::after {
|
||||
@@ -34,9 +35,9 @@
|
||||
background: linear-gradient(to bottom, transparent, var(--su-bg-page));
|
||||
}
|
||||
|
||||
/* 这张背景图是浅色渐变图,深色下留着会糊成一层脏灰,直接不显示 */
|
||||
/* 这张背景图是浅色渐变图,深色下留着会糊成一层脏灰,淡出而不是直接消失 */
|
||||
[data-scheme='dark'] .site-background {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ---------- 页头 ---------- */
|
||||
@@ -1328,3 +1329,27 @@ a.entry-href:hover {
|
||||
color: var(--su-color-danger);
|
||||
background-color: color-mix(in srgb, var(--su-color-danger) 10%, transparent);
|
||||
}
|
||||
|
||||
/* ---------- 明暗切换:短暂给所有表面补颜色过渡 ---------- */
|
||||
/* 只在切换的 260ms 内加 !important,避免覆盖组件平时的 hover/展开动画。 */
|
||||
html.theme-transition,
|
||||
html.theme-transition *,
|
||||
html.theme-transition *::before,
|
||||
html.theme-transition *::after {
|
||||
transition:
|
||||
background-color 240ms var(--su-ease-in-out),
|
||||
border-color 240ms var(--su-ease-in-out),
|
||||
color 240ms var(--su-ease-in-out),
|
||||
fill 240ms var(--su-ease-in-out),
|
||||
stroke 240ms var(--su-ease-in-out),
|
||||
box-shadow 240ms var(--su-ease-in-out) !important;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html.theme-transition,
|
||||
html.theme-transition *,
|
||||
html.theme-transition *::before,
|
||||
html.theme-transition *::after {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,13 +300,21 @@ export default function AppNext() {
|
||||
readThemeOverride(),
|
||||
)
|
||||
const effectiveTheme = themeOverride ?? site.site.theme
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.dataset.scheme = effectiveTheme
|
||||
}, [effectiveTheme])
|
||||
|
||||
/** 当前实际是深色吗(auto 时按系统偏好算) */
|
||||
const isDark = isDarkScheme(effectiveTheme)
|
||||
const scheme = isDark ? 'dark' : 'light'
|
||||
|
||||
useEffect(() => {
|
||||
const root = document.documentElement
|
||||
if (root.dataset.scheme === scheme) return
|
||||
root.classList.add('theme-transition')
|
||||
root.dataset.scheme = scheme
|
||||
const timer = window.setTimeout(() => root.classList.remove('theme-transition'), 260)
|
||||
return () => {
|
||||
window.clearTimeout(timer)
|
||||
root.classList.remove('theme-transition')
|
||||
}
|
||||
}, [scheme])
|
||||
|
||||
/** 页头的明暗切换:只写本机偏好,不需要登录;站点默认值在后台改 */
|
||||
const toggleTheme = () => {
|
||||
@@ -663,6 +671,7 @@ export default function AppNext() {
|
||||
headerSpeed={site.header.speed}
|
||||
headerGap={site.header.gap}
|
||||
site={site.site}
|
||||
ready={settings !== null}
|
||||
authenticated={session?.authenticated === true}
|
||||
username={session?.username ?? undefined}
|
||||
refreshing={refreshing}
|
||||
@@ -707,7 +716,7 @@ export default function AppNext() {
|
||||
onOpenApi={() => navigate('/interface')}
|
||||
/>
|
||||
)}
|
||||
{settings !== null && site.sidebar.show_nav !== false && (
|
||||
{settings !== null && !loading && site.sidebar.show_nav !== false && (
|
||||
<div className="nav-card">
|
||||
<Menu
|
||||
items={sidebarItems}
|
||||
|
||||
@@ -25,6 +25,8 @@ type SiteHeaderProps = {
|
||||
headerGap?: number
|
||||
/** 品牌与操作工具配置 */
|
||||
site: SiteMetaSettings
|
||||
/** 站点设置是否已加载;未加载时不渲染依赖配置的图标,避免先显示再消失 */
|
||||
ready?: boolean
|
||||
/** 是否已登录:决定右侧是「登录」还是「退出登录」 */
|
||||
authenticated: boolean
|
||||
username?: string
|
||||
@@ -55,6 +57,7 @@ export function SiteHeader({
|
||||
headerSpeed,
|
||||
headerGap,
|
||||
site,
|
||||
ready = true,
|
||||
authenticated,
|
||||
username,
|
||||
refreshing = false,
|
||||
@@ -77,7 +80,7 @@ export function SiteHeader({
|
||||
/>
|
||||
|
||||
<div className="header-ticker-slot">
|
||||
{headerEnabled && (
|
||||
{ready && headerEnabled && (
|
||||
<Ticker speed={headerSpeed} gap={headerGap} label="运行状态" className="header-ticker">
|
||||
{headerItems.map((item, index) => (
|
||||
<span className="header-ticker-item" key={`${index}-${item.text}`}>
|
||||
@@ -94,6 +97,8 @@ export function SiteHeader({
|
||||
</div>
|
||||
|
||||
<div className="header-actions">
|
||||
{ready && (
|
||||
<>
|
||||
{site.actions.theme && (
|
||||
<IconButton
|
||||
label={dark ? '切换到浅色' : '切换到深色'}
|
||||
@@ -110,7 +115,11 @@ export function SiteHeader({
|
||||
/>
|
||||
)}
|
||||
{site.actions.settings && authenticated && (
|
||||
<IconButton label="站点设置" icon={<Icon name="settings_1_line" />} onClick={onSettings} />
|
||||
<IconButton
|
||||
label="站点设置"
|
||||
icon={<Icon name="settings_1_line" />}
|
||||
onClick={onSettings}
|
||||
/>
|
||||
)}
|
||||
{site.actions.session &&
|
||||
(authenticated ? (
|
||||
@@ -122,6 +131,8 @@ export function SiteHeader({
|
||||
) : (
|
||||
<IconButton label="登录" icon={<Icon name="user_3_line" />} onClick={onLogin} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
html[data-scheme='dark'] {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
|
||||
Reference in New Issue
Block a user