Skip to content

Commit a87bf59

Browse files
committed
feat: 初始化随机主题色和主题切换
1 parent 8bdf01c commit a87bf59

File tree

20 files changed

+517
-256
lines changed

20 files changed

+517
-256
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
blog
1+
blog

eslint.config.cjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ const reactHooks = require('eslint-plugin-react-hooks');
77
const { resolve } = require('path');
88
module.exports = [
99
{
10-
ignores: ['node_modules/**', '.next/**', './eslint.config.cjs', '.gitignore', 'analyze/**','plugins/**/*'],
10+
ignores: [
11+
'node_modules/**',
12+
'.next/**',
13+
'./eslint.config.cjs',
14+
'.gitignore',
15+
'analyze/**',
16+
'plugins/**/*',
17+
],
1118
},
1219
{
1320
files: ['**/*.{js,jsx,ts,tsx}'],

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@
1919
"@iconify-json/mingcute": "^1.2.1",
2020
"@next/bundle-analyzer": "^14.2.15",
2121
"@types/prettier": "^3.0.0",
22+
"chroma-js": "^3.1.2",
23+
"colorjs.io": "^0.5.2",
2224
"daisyui": "^4.12.13",
2325
"next": "14.2.15",
26+
"next-themes": "^0.3.0",
27+
"pngjs": "^7.0.0",
2428
"postcss-js": "^4.0.1",
2529
"prettier": "3.3.2",
2630
"react": "^18",

pnpm-lock.yaml

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/(app)/(home)/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
export default function Home() {
2-
return <div>哈哈哈</div>;
4+
return <div>哈哈qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq哈</div>;
35
}

src/app/layout.tsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import '../styles/index.css';
33
import type { PropsWithChildren } from 'react';
44
import { Metadata, Viewport } from 'next';
55

6+
import WebAppProviders from '@/components/providers/root';
7+
import AccentColorStyleInjector from '@/components/modules/shared/AccentColorStyleInjector';
68
import { seo } from '~/seo';
79

810
export const metadata: Metadata = {
@@ -53,9 +55,40 @@ export const viewport: Viewport = {
5355

5456
export default async function RootLayout({ children }: PropsWithChildren) {
5557
return (
56-
<html lang="zh-CN" suppressHydrationWarning>
57-
<head></head>
58-
<body>{children}</body>
58+
<html lang="zh-CN" className=" noise" suppressHydrationWarning>
59+
<head>
60+
<SayHi />
61+
<AccentColorStyleInjector />
62+
</head>
63+
<body>
64+
<WebAppProviders>
65+
<div data-theme>{children}</div>
66+
</WebAppProviders>
67+
</body>
5968
</html>
6069
);
6170
}
71+
72+
const SayHi = () => {
73+
return (
74+
<script
75+
dangerouslySetInnerHTML={{
76+
__html: `
77+
(${function () {
78+
console.log(
79+
'%c Welcome my blog %c https://github.com/coderz-w/blog',
80+
'color: #fff; margin: 1em 0; padding: 5px 0; background: #2980b9;',
81+
'margin: 1em 0; padding: 5px 0; background: #efefef;',
82+
);
83+
console.log('%c', 'color: #000; margin: 1em 0; padding: 5px 0; background: #efefef;');
84+
}.toString()})();`,
85+
}}
86+
/>
87+
);
88+
};
89+
90+
declare global {
91+
interface Window {
92+
version: string;
93+
}
94+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
'use client';
2+
3+
import { useEffect, useState } from 'react';
4+
import Chroma from 'chroma-js';
5+
6+
import { createPngNoiseBackground } from '@/lib/noise';
7+
8+
const hexToOklchString = (hex: string) => {
9+
const color = Chroma(hex);
10+
const oklab = color.oklab();
11+
12+
return oklab.map((value: any) => value.toFixed(2)).join(' ');
13+
};
14+
15+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
16+
const convertHexToRgbString = (hex: string): string => {
17+
const color = Chroma(hex);
18+
const [r, g, b] = color.rgb();
19+
20+
return `${r} ${g} ${b}`;
21+
};
22+
23+
const accentColorLight = [
24+
// 浅葱
25+
'#33A6B8',
26+
'#FF6666',
27+
'#26A69A',
28+
'#fb7287',
29+
'#69a6cc',
30+
];
31+
32+
const accentColorDark = [
33+
// 桃
34+
'#F596AA',
35+
'#A0A7D4',
36+
'#ff7b7b',
37+
'#99D8CF',
38+
'#838BC6',
39+
];
40+
41+
const defaultAccentColor = { light: accentColorLight, dark: accentColorDark };
42+
43+
const lightBg = 'rgb(250, 250, 250)';
44+
const darkBg = 'rgb(0, 2, 18)';
45+
46+
const AccentColorStyleInjector = () => {
47+
const [, setCurrentAccentColorLRef] = useState<string>('');
48+
const [, setCurrentAccentColorDRef] = useState<string>('');
49+
50+
useEffect(() => {
51+
const initializeStyles = async () => {
52+
const { light, dark } = defaultAccentColor;
53+
54+
const lightColors = light;
55+
const darkColors = dark;
56+
57+
const Length = Math.max(lightColors.length ?? 0, darkColors.length ?? 0);
58+
const randomSeedRef = (Math.random() * Length) | 0;
59+
const lightColor = lightColors[randomSeedRef];
60+
const darkColor = darkColors[randomSeedRef];
61+
62+
setCurrentAccentColorLRef(lightColor);
63+
setCurrentAccentColorDRef(darkColor);
64+
65+
const lightOklch = hexToOklchString(lightColor);
66+
const darkOklch = hexToOklchString(darkColor);
67+
68+
const [hl, sl, ll] = lightOklch.split(' ');
69+
const [hd, sd, ld] = darkOklch.split(' ');
70+
71+
const [lightBgImage, darkBgImage] = await Promise.all([
72+
createPngNoiseBackground(lightColor),
73+
createPngNoiseBackground(darkColor),
74+
]);
75+
76+
const lightMixColor = Chroma(lightBg).mix(Chroma(lightColor), 0.05).hex();
77+
const darkMixColor = Chroma(darkBg).mix(Chroma(darkColor), 0.12).hex();
78+
79+
const styleContent = `
80+
html[data-theme='light'] .noise body::before {
81+
position: fixed;
82+
inset: 0;
83+
content: '';
84+
opacity: 0.04;
85+
background-repeat: repeat;
86+
background-image: url(${lightBgImage});
87+
}
88+
html[data-theme='dark'].noise body::before {
89+
position: fixed;
90+
inset: 0;
91+
content: '';
92+
opacity: 0.01;
93+
background-repeat: repeat;
94+
background-image: url(${darkBgImage});
95+
}
96+
html {
97+
--a: ${hl} ${sl} ${ll};
98+
--accent-color: ${lightColor};
99+
}
100+
html[data-theme='dark'] {
101+
--a: ${hd} ${sd} ${ld};
102+
--accent-color: ${darkColor};
103+
}
104+
html[data-theme='light'] {
105+
--root-bg: ${lightMixColor};
106+
background-color: var(--root-bg) !important;
107+
}
108+
html[data-theme='dark'] {
109+
--root-bg: ${darkMixColor};
110+
}
111+
`;
112+
113+
const styleElement = document.createElement('style');
114+
styleElement.id = 'accent-color-style';
115+
styleElement.setAttribute('data-light', lightColor);
116+
styleElement.setAttribute('data-dark', darkColor);
117+
styleElement.innerHTML = styleContent;
118+
document.head.appendChild(styleElement);
119+
120+
return () => {
121+
const existingStyle = document.getElementById('accent-color-style');
122+
123+
if (existingStyle) {
124+
document.head.removeChild(existingStyle);
125+
}
126+
};
127+
};
128+
129+
initializeStyles();
130+
}, []);
131+
132+
return null;
133+
};
134+
135+
export default AccentColorStyleInjector;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import AccentColorStyleInjector from './AccentColorStyleInjector';
2+
3+
export default AccentColorStyleInjector;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
import type { ReactElement, ReactNode } from 'react';
3+
4+
interface ProviderComposerProps {
5+
contexts: ReactElement[];
6+
children: React.ReactNode;
7+
}
8+
9+
const ProviderComposer: React.FC<ProviderComposerProps> = ({ contexts, children }) => {
10+
return contexts.reduceRight((kids: ReactNode, parent: ReactElement) => {
11+
return React.cloneElement(parent, { children: kids });
12+
}, children);
13+
};
14+
15+
export default ProviderComposer;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import ProviderComposer from './ProviderComposer';
2+
export default ProviderComposer;

0 commit comments

Comments
 (0)