feat(主题): 添加强制暗色模式支持并优化图片加载效果

- 在主题配置中新增forceDarkMode选项强制使用暗色模式
- 根据配置隐藏主题切换器和暗色模式切换组件
- 为图片组件添加加载动画和悬停效果
- 在页脚添加暗色模式IP检测iframe
- 格式化package.json文件
This commit is contained in:
afoim
2025-06-24 22:51:40 +08:00
parent 4a5495f8a8
commit 7f9999ab5c
7 changed files with 130 additions and 89 deletions

View File

@@ -1,70 +1,70 @@
{
"name": "fuwari",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build && pagefind --site dist",
"preview": "astro preview",
"astro": "astro",
"type-check": "tsc --noEmit --isolatedDeclarations",
"new-post": "node scripts/new-post.js",
"format": "biome format --write ./src",
"lint": "biome check --write ./src",
"preinstall": "npx only-allow pnpm"
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/rss": "^4.0.11",
"@astrojs/sitemap": "^3.3.1",
"@astrojs/svelte": "7.0.12",
"@astrojs/tailwind": "^6.0.2",
"@fontsource-variable/jetbrains-mono": "^5.2.5",
"@fontsource/roboto": "^5.2.5",
"@iconify-json/fa6-brands": "^1.2.5",
"@iconify-json/fa6-regular": "^1.2.3",
"@iconify-json/fa6-solid": "^1.2.3",
"@iconify-json/material-symbols": "^1.2.20",
"@iconify/svelte": "^4.2.0",
"@swup/astro": "^1.6.0",
"@tailwindcss/typography": "^0.5.16",
"astro": "5.7.9",
"astro-icon": "^1.1.5",
"hastscript": "^9.0.1",
"katex": "^0.16.22",
"markdown-it": "^14.1.0",
"mdast-util-to-string": "^4.0.0",
"overlayscrollbars": "^2.11.1",
"pagefind": "^1.3.0",
"photoswipe": "^5.4.4",
"reading-time": "^1.5.0",
"rehype-autolink-headings": "^7.1.0",
"rehype-components": "^0.3.0",
"rehype-katex": "^7.0.1",
"rehype-slug": "^6.0.0",
"remark-directive": "^3.0.1",
"remark-directive-rehype": "^0.4.2",
"remark-github-admonitions-to-directives": "^1.0.5",
"remark-math": "^6.0.0",
"remark-sectionize": "^2.1.0",
"sanitize-html": "^2.16.0",
"sharp": "^0.34.1",
"stylus": "^0.64.0",
"svelte": "^5.28.2",
"tailwindcss": "^3.4.17",
"typescript": "^5.8.3",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@astrojs/ts-plugin": "^1.10.4",
"@biomejs/biome": "1.9.4",
"@rollup/plugin-yaml": "^4.1.2",
"@types/markdown-it": "^14.1.2",
"@types/mdast": "^4.0.4",
"@types/sanitize-html": "^2.15.0",
"postcss-import": "^16.1.0",
"postcss-nesting": "^13.0.1"
},
"packageManager": "pnpm@9.14.4"
"name": "fuwari",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build && pagefind --site dist",
"preview": "astro preview",
"astro": "astro",
"type-check": "tsc --noEmit --isolatedDeclarations",
"new-post": "node scripts/new-post.js",
"format": "biome format --write ./src",
"lint": "biome check --write ./src",
"preinstall": "npx only-allow pnpm"
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/rss": "^4.0.11",
"@astrojs/sitemap": "^3.3.1",
"@astrojs/svelte": "7.0.12",
"@astrojs/tailwind": "^6.0.2",
"@fontsource-variable/jetbrains-mono": "^5.2.5",
"@fontsource/roboto": "^5.2.5",
"@iconify-json/fa6-brands": "^1.2.5",
"@iconify-json/fa6-regular": "^1.2.3",
"@iconify-json/fa6-solid": "^1.2.3",
"@iconify-json/material-symbols": "^1.2.20",
"@iconify/svelte": "^4.2.0",
"@swup/astro": "^1.6.0",
"@tailwindcss/typography": "^0.5.16",
"astro": "5.7.9",
"astro-icon": "^1.1.5",
"hastscript": "^9.0.1",
"katex": "^0.16.22",
"markdown-it": "^14.1.0",
"mdast-util-to-string": "^4.0.0",
"overlayscrollbars": "^2.11.1",
"pagefind": "^1.3.0",
"photoswipe": "^5.4.4",
"reading-time": "^1.5.0",
"rehype-autolink-headings": "^7.1.0",
"rehype-components": "^0.3.0",
"rehype-katex": "^7.0.1",
"rehype-slug": "^6.0.0",
"remark-directive": "^3.0.1",
"remark-directive-rehype": "^0.4.2",
"remark-github-admonitions-to-directives": "^1.0.5",
"remark-math": "^6.0.0",
"remark-sectionize": "^2.1.0",
"sanitize-html": "^2.16.0",
"sharp": "^0.34.1",
"stylus": "^0.64.0",
"svelte": "^5.28.2",
"tailwindcss": "^3.4.17",
"typescript": "^5.8.3",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@astrojs/ts-plugin": "^1.10.4",
"@biomejs/biome": "1.9.4",
"@rollup/plugin-yaml": "^4.1.2",
"@types/markdown-it": "^14.1.2",
"@types/mdast": "^4.0.4",
"@types/sanitize-html": "^2.15.0",
"postcss-import": "^16.1.0",
"postcss-nesting": "^13.0.1"
},
"packageManager": "pnpm@9.14.4"
}

View File

@@ -10,6 +10,7 @@ const currentYear = new Date().getFullYear();
<div class="transition border-t border-black/10 dark:border-white/15 my-10 border-dashed mx-32"></div>
<!--<div class="transition bg-[oklch(92%_0.01_var(&#45;&#45;hue))] dark:bg-black rounded-2xl py-8 mt-4 mb-8 flex flex-col items-center justify-center px-6">-->
<div class="transition border-dashed border-[oklch(85%_0.01_var(--hue))] dark:border-white/15 rounded-2xl mb-12 flex flex-col items-center justify-center px-6">
<iframe src="https://ip.skk.moe/simple-dark" style="width: 100%; border: 0"></iframe>
<div class="transition text-50 text-sm text-center">
<!-- Project Badges -->
<div class="flex flex-wrap justify-center gap-2 mb-3">

View File

@@ -50,7 +50,9 @@ let links: NavBarLink[] = navBarConfig.links.map(
<Icon name="material-symbols:palette-outline" class="text-[1.25rem]"></Icon>
</button>
)}
<LightDarkSwitch client:only="svelte"></LightDarkSwitch>
{!siteConfig.themeColor.forceDarkMode && (
<LightDarkSwitch client:only="svelte"></LightDarkSwitch>
)}
<button aria-label="Menu" name="Nav Menu" class="btn-plain scale-animation rounded-lg w-11 h-11 active:scale-90 md:!hidden" id="nav-menu-switch">
<Icon name="material-symbols:menu-rounded" class="text-[1.25rem]"></Icon>
</button>

View File

@@ -10,7 +10,7 @@ interface Props {
}
import { Image } from "astro:assets";
import { url } from "../../utils/url-utils";
import { imageFallbackConfig } from "../../config";
import { imageFallbackConfig, siteConfig } from "../../config";
const { id, src, alt, position = "center", basePath = "/" } = Astro.props;
const className = Astro.props.class;
@@ -46,13 +46,43 @@ if (isLocal) {
const imageClass = "w-full h-full object-cover";
const imageStyle = `object-position: ${position}`;
---
<div id={id} class:list={[className, 'overflow-hidden relative']}>
<div id={id} class:list={[className, 'overflow-hidden relative image-wrapper']} style={`--theme-hue: ${siteConfig.themeColor.hue}`}>
<!-- 加载条 -->
<div class="loading-bar absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-32 h-1 bg-gray-200 dark:bg-gray-700 z-10 rounded-full overflow-hidden">
<div class="loading-progress h-full w-8 bg-[oklch(0.70_0.14_var(--theme-hue))] animate-loading-progress rounded-full"></div>
</div>
<!-- 图片内容 -->
<div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div>
{isLocal && img && <Image src={img} alt={alt || ""} class={imageClass} style={imageStyle}/>}
{isLocal && img && <Image src={img} alt={alt || ""} class={`${imageClass} image-content opacity-0 transition-opacity duration-500`} style={imageStyle} onload="this.style.opacity='1'; this.parentElement.querySelector('.loading-bar').style.opacity='0';"/>}
{!isLocal && (
imageFallbackConfig.enable && src.includes(imageFallbackConfig.originalDomain) ?
<img src={isPublic ? url(src) : src} alt={alt || ""} class={imageClass} style={imageStyle} onerror={`this.onerror=null; this.src='${(isPublic ? url(src) : src).replace(imageFallbackConfig.originalDomain, imageFallbackConfig.fallbackDomain)}';`}/> :
<img src={isPublic ? url(src) : src} alt={alt || ""} class={imageClass} style={imageStyle}/>
<img src={isPublic ? url(src) : src} alt={alt || ""} class={`${imageClass} image-content opacity-0 transition-opacity duration-500`} style={imageStyle} onload="this.style.opacity='1'; this.parentElement.querySelector('.loading-bar').style.opacity='0';" onerror={`this.onerror=null; this.src='${(isPublic ? url(src) : src).replace(imageFallbackConfig.originalDomain, imageFallbackConfig.fallbackDomain)}';`}/> :
<img src={isPublic ? url(src) : src} alt={alt || ""} class={`${imageClass} image-content opacity-0 transition-opacity duration-500`} style={imageStyle} onload="this.style.opacity='1'; this.parentElement.querySelector('.loading-bar').style.opacity='0';"/>
)}
</div>
<style>
.loading-bar {
transition: opacity 0.5s ease-out;
}
@keyframes loading-progress {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(400%);
}
}
.animate-loading-progress {
animation: loading-progress 1.5s ease-in-out infinite;
}
.image-wrapper:hover .image-content {
transform: scale(1.05);
transition: transform 0.3s ease-out, opacity 0.5s ease-out;
}
</style>

View File

@@ -14,6 +14,7 @@ export const siteConfig: SiteConfig = {
themeColor: {
hue: 360, // Default hue for the theme color, from 0 to 360. e.g. red: 0, teal: 200, cyan: 250, pink: 345
fixed: true, // Hide the theme color picker for visitors
forceDarkMode: true, // Force dark mode and hide theme switcher
},
banner: {
enable: false,

View File

@@ -110,22 +110,28 @@ const bannerOffset =
))}
<!-- Set the theme before the page is rendered to avoid a flash -->
<script is:inline define:vars={{DEFAULT_THEME, LIGHT_MODE, DARK_MODE, AUTO_MODE, BANNER_HEIGHT_EXTEND, PAGE_WIDTH, configHue}}>
// Load the theme from local storage
const theme = localStorage.getItem('theme') || DEFAULT_THEME;
switch (theme) {
case LIGHT_MODE:
document.documentElement.classList.remove('dark');
break
case DARK_MODE:
document.documentElement.classList.add('dark');
break
case AUTO_MODE:
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
} else {
<script is:inline define:vars={{DEFAULT_THEME, LIGHT_MODE, DARK_MODE, AUTO_MODE, BANNER_HEIGHT_EXTEND, PAGE_WIDTH, configHue, forceDarkMode: siteConfig.themeColor.forceDarkMode}}>
// Force dark mode if configured
if (forceDarkMode) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', DARK_MODE);
} else {
// Load the theme from local storage
const theme = localStorage.getItem('theme') || DEFAULT_THEME;
switch (theme) {
case LIGHT_MODE:
document.documentElement.classList.remove('dark');
}
break
case DARK_MODE:
document.documentElement.classList.add('dark');
break
case AUTO_MODE:
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}
}
// Load the hue from local storage

View File

@@ -9,6 +9,7 @@ export type SiteConfig = {
themeColor: {
hue: number;
fixed: boolean;
forceDarkMode?: boolean;
};
banner: {
enable: boolean;