add theme
This commit is contained in:
34
CLAUDE.md
Normal file
34
CLAUDE.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
npm start # Dev server (production mode)
|
||||
npm run build # Production build
|
||||
npm run build:staging # Staging build
|
||||
npm run fmt # Format with Prettier (*.js, style.css, index.html)
|
||||
```
|
||||
|
||||
No test suite exists. Prettier is the only code quality tool.
|
||||
|
||||
## Architecture
|
||||
|
||||
This is a static IoT learning portal — a single-page app built with vanilla JS and Vite. No framework.
|
||||
|
||||
**Module responsibilities:**
|
||||
- `main.js` — entry point, calls `initApp()`
|
||||
- `app.js` — application lifecycle: reads localStorage, wires event listeners, orchestrates everything
|
||||
- `data.js` — registry of external learning platform links (OJ, code runners, books, etc.)
|
||||
- `render.js` — generates HTML for site cards and pins from `data.js` entries
|
||||
- `i18n.js` — translation system with a `t()` function; supports 11 language variants (Chinese variants, EN, JA, KO, plus joke languages: wenyan, mars, garbled, binary, meow, emoji)
|
||||
- `theme.js` — manages 4 design themes (Fluent, Material You, Terminal, Cyberpunk) and dark/light toggle
|
||||
|
||||
**Data flow:** `app.js` → reads prefs from `localStorage` → calls `render.js` to build the card grid from `data.js` → applies theme via `theme.js` → uses `i18n.js` for all UI strings.
|
||||
|
||||
**State persistence:** language, design theme, and dark/light preference are all stored in `localStorage`.
|
||||
|
||||
**Site URLs** are injected at build time via `import.meta.env.VITE_*` environment variables.
|
||||
|
||||
**Themes:** Terminal and Cyberpunk force dark mode; the light/dark toggle is disabled for those themes.
|
||||
11
i18n.js
11
i18n.js
@@ -139,66 +139,77 @@ export const DESIGN_THEME_LABELS = {
|
||||
"material-you": "Material You",
|
||||
terminal: "终端",
|
||||
cyberpunk: "赛博朋克",
|
||||
nord: "Nord",
|
||||
},
|
||||
"zh-Hant": {
|
||||
fluent: "Fluent",
|
||||
"material-you": "Material You",
|
||||
terminal: "終端",
|
||||
cyberpunk: "賽博龐克",
|
||||
nord: "Nord",
|
||||
},
|
||||
en: {
|
||||
fluent: "Fluent",
|
||||
"material-you": "Material You",
|
||||
terminal: "Terminal",
|
||||
cyberpunk: "Cyberpunk",
|
||||
nord: "Nord",
|
||||
},
|
||||
ja: {
|
||||
fluent: "Fluent",
|
||||
"material-you": "Material You",
|
||||
terminal: "ターミナル",
|
||||
cyberpunk: "サイバーパンク",
|
||||
nord: "Nord",
|
||||
},
|
||||
ko: {
|
||||
fluent: "Fluent",
|
||||
"material-you": "Material You",
|
||||
terminal: "터미널",
|
||||
cyberpunk: "사이버펑크",
|
||||
nord: "Nord",
|
||||
},
|
||||
wenyan: {
|
||||
fluent: "流光",
|
||||
"material-you": "物材",
|
||||
terminal: "终端",
|
||||
cyberpunk: "赛博",
|
||||
nord: "清寒",
|
||||
},
|
||||
mars: {
|
||||
fluent: "流↗光",
|
||||
"material-you": "材↘質",
|
||||
terminal: "終↗★端",
|
||||
cyberpunk: "賽↘!博",
|
||||
nord: "清↗寒★",
|
||||
},
|
||||
garbled: {
|
||||
fluent: "◼è▦",
|
||||
"material-you": "拷▤屯ä锟◽",
|
||||
terminal: "¥¬▤▨¿¿",
|
||||
cyberpunk: "◼çæ¥烫¥",
|
||||
nord: "æ◽屯¿",
|
||||
},
|
||||
bin: {
|
||||
fluent: "0101",
|
||||
"material-you": "010101",
|
||||
terminal: "01010101",
|
||||
cyberpunk: "0101010101",
|
||||
nord: "0101010",
|
||||
},
|
||||
meow: {
|
||||
fluent: "喵喵",
|
||||
"material-you": "喵喵喵",
|
||||
terminal: "喵喵",
|
||||
cyberpunk: "喵喵喵喵",
|
||||
nord: "喵喵喵",
|
||||
},
|
||||
emoji: {
|
||||
fluent: "💧",
|
||||
"material-you": "🧱",
|
||||
terminal: "⌨️",
|
||||
cyberpunk: "⚡",
|
||||
nord: "❄️",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
<li role="option" data-value="cyberpunk" aria-selected="false">
|
||||
Cyberpunk
|
||||
</li>
|
||||
<li role="option" data-value="nord" aria-selected="false">
|
||||
Nord
|
||||
</li>
|
||||
</ul>
|
||||
</label>
|
||||
<label class="design-theme language-switch">
|
||||
|
||||
1353
package-lock.json
generated
1353
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
"fmt": "prettier --write *.js style.css index.html"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.7.4",
|
||||
"vite": "^7.3.0"
|
||||
"prettier": "^3.8.2",
|
||||
"vite": "^8.0.8"
|
||||
}
|
||||
}
|
||||
175
style.css
175
style.css
@@ -1405,3 +1405,178 @@ html[data-design-theme="material-you"] .beian a:hover {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── Nord (nordtheme.com) ──────────────────────────────── */
|
||||
/* Default: Polar Night (dark) */
|
||||
html[data-design-theme="nord"] {
|
||||
color-scheme: dark;
|
||||
|
||||
--accent: #88c0d0;
|
||||
--accent-rgb: 136, 192, 208;
|
||||
--accent-2: #81a1c1;
|
||||
--accent-3: #5e81ac;
|
||||
--accent-secondary-rgb: 129, 161, 193;
|
||||
|
||||
--page-gradient: #2e3440;
|
||||
--page-texture: none;
|
||||
--title-gradient: linear-gradient(135deg, #88c0d0 0%, #81a1c1 50%, #5e81ac 100%);
|
||||
|
||||
--control-bg: rgba(59, 66, 82, 0.85);
|
||||
--control-border: rgba(76, 86, 106, 0.6);
|
||||
--control-inset: rgba(67, 76, 94, 0.5);
|
||||
--control-fg: #eceff4;
|
||||
}
|
||||
|
||||
/* Re-assert Nord dark variables after generic dark mode overrides them */
|
||||
html[data-theme="dark"][data-design-theme="nord"] {
|
||||
color-scheme: dark;
|
||||
|
||||
--accent: #88c0d0;
|
||||
--accent-rgb: 136, 192, 208;
|
||||
--accent-2: #81a1c1;
|
||||
--accent-3: #5e81ac;
|
||||
--accent-secondary-rgb: 129, 161, 193;
|
||||
|
||||
--page-gradient: #2e3440;
|
||||
--page-texture: none;
|
||||
--title-gradient: linear-gradient(135deg, #88c0d0 0%, #81a1c1 50%, #5e81ac 100%);
|
||||
|
||||
--control-bg: rgba(59, 66, 82, 0.85);
|
||||
--control-border: rgba(76, 86, 106, 0.6);
|
||||
--control-inset: rgba(67, 76, 94, 0.5);
|
||||
--control-fg: #eceff4;
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] body {
|
||||
color: #eceff4;
|
||||
}
|
||||
|
||||
/* Snow Storm (light mode) */
|
||||
html[data-theme="light"][data-design-theme="nord"] {
|
||||
color-scheme: light;
|
||||
|
||||
--accent: #5e81ac;
|
||||
--accent-rgb: 94, 129, 172;
|
||||
--accent-2: #81a1c1;
|
||||
--accent-3: #88c0d0;
|
||||
--accent-secondary-rgb: 94, 129, 172;
|
||||
|
||||
--page-gradient: #eceff4;
|
||||
--page-texture: none;
|
||||
--title-gradient: linear-gradient(135deg, #5e81ac 0%, #81a1c1 50%, #88c0d0 100%);
|
||||
|
||||
--control-bg: rgba(229, 233, 240, 0.85);
|
||||
--control-border: rgba(216, 222, 233, 0.8);
|
||||
--control-inset: rgba(236, 239, 244, 0.9);
|
||||
--control-fg: #2e3440;
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] body {
|
||||
color: #2e3440;
|
||||
}
|
||||
|
||||
/* Dark cards (Polar Night) */
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card {
|
||||
background: #3b4252;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border: 1px solid #4c566a;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card h2 {
|
||||
color: #eceff4;
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card:hover h2,
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card:focus h2 {
|
||||
color: #88c0d0;
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card p {
|
||||
color: #d8dee9;
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card:hover p,
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card:focus p {
|
||||
color: #e5e9f0;
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card:hover,
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card:focus {
|
||||
border-color: #88c0d0;
|
||||
box-shadow: 0 4px 16px rgba(136, 192, 208, 0.2);
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] .card.pin {
|
||||
background: #434c5e;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
}
|
||||
|
||||
/* Light cards (Snow Storm) */
|
||||
html[data-theme="light"][data-design-theme="nord"] .card {
|
||||
background: #e5e9f0;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border: 1px solid #d8dee9;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(46, 52, 64, 0.1);
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .card::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .card h2 {
|
||||
color: #2e3440;
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .card:hover h2,
|
||||
html[data-theme="light"][data-design-theme="nord"] .card:focus h2 {
|
||||
color: #5e81ac;
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .card p {
|
||||
color: #4c566a;
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .card:hover p,
|
||||
html[data-theme="light"][data-design-theme="nord"] .card:focus p {
|
||||
color: #3b4252;
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .card:hover,
|
||||
html[data-theme="light"][data-design-theme="nord"] .card:focus {
|
||||
border-color: #5e81ac;
|
||||
box-shadow: 0 4px 16px rgba(94, 129, 172, 0.2);
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .card.pin {
|
||||
background: #eceff4;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border-color: #d8dee9;
|
||||
}
|
||||
|
||||
/* Beian links */
|
||||
html[data-theme="dark"][data-design-theme="nord"] .beian a {
|
||||
color: #d8dee9;
|
||||
}
|
||||
|
||||
html[data-theme="dark"][data-design-theme="nord"] .beian a:hover {
|
||||
color: #88c0d0;
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .beian a {
|
||||
color: #4c566a;
|
||||
}
|
||||
|
||||
html[data-theme="light"][data-design-theme="nord"] .beian a:hover {
|
||||
color: #5e81ac;
|
||||
}
|
||||
|
||||
2
theme.js
2
theme.js
@@ -1,4 +1,4 @@
|
||||
const DESIGN_THEMES = ["fluent", "material-you", "terminal", "cyberpunk"]
|
||||
const DESIGN_THEMES = ["fluent", "material-you", "terminal", "cyberpunk", "nord"]
|
||||
const FORCED_DARK_DESIGN_THEMES = new Set(["terminal", "cyberpunk"])
|
||||
const THEME_BEFORE_FORCED_KEY = "themeBeforeForcedDark"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user