Добавлены настройки темы по умолчанию

This commit is contained in:
Ilia Miheev
2026-06-21 00:36:31 +03:00
parent c9347b8d20
commit 810bf469f9
10 changed files with 51 additions and 29 deletions
+4 -1
View File
@@ -7,4 +7,7 @@ VITE_BANNER_SLOGAN=— qr-код без излишеств
VITE_BANNER_URL=https://github.com/IliaMiheev/iQR VITE_BANNER_URL=https://github.com/IliaMiheev/iQR
# Акцентный цвет (hex, rgb или css-имя) # Акцентный цвет (hex, rgb или css-имя)
VITE_ACCENT_COLOR=#ff4500 VITE_ACCENT_COLOR="#ff4500"
# Тема по умолчанию: dark или light (если пользователь ещё не переключал)
VITE_DEFAULT_THEME=light
+2
View File
@@ -26,6 +26,7 @@ npm run dev
| `VITE_BANNER_SLOGAN` | Слоган в плашке | `— qr-код без излишеств` | | `VITE_BANNER_SLOGAN` | Слоган в плашке | `— qr-код без излишеств` |
| `VITE_BANNER_URL` | Ссылка плашки | репозиторий iQR | | `VITE_BANNER_URL` | Ссылка плашки | репозиторий iQR |
| `VITE_ACCENT_COLOR` | Акцентный цвет | `#ff4500` | | `VITE_ACCENT_COLOR` | Акцентный цвет | `#ff4500` |
| `VITE_DEFAULT_THEME` | Тема по умолчанию (`dark` / `light`) | `dark` |
После изменения `.env` перезапустите `npm run dev`. После изменения `.env` перезапустите `npm run dev`.
@@ -46,6 +47,7 @@ environment:
BANNER_SLOGAN: "— быстрые qr-коды" BANNER_SLOGAN: "— быстрые qr-коды"
BANNER_URL: "https://example.com" BANNER_URL: "https://example.com"
ACCENT_COLOR: "#7c3aed" ACCENT_COLOR: "#7c3aed"
DEFAULT_THEME: "light"
``` ```
При старте контейнера генерируется `config.json`, поэтому пересборка образа для смены текстов не нужна. При старте контейнера генерируется `config.json`, поэтому пересборка образа для смены текстов не нужна.
+1
View File
@@ -9,4 +9,5 @@ services:
BANNER_SLOGAN: "— qr-код без излишеств" BANNER_SLOGAN: "— qr-код без излишеств"
BANNER_URL: "https://github.com/IliaMiheev/iQR" BANNER_URL: "https://github.com/IliaMiheev/iQR"
ACCENT_COLOR: "#ff4500" ACCENT_COLOR: "#ff4500"
DEFAULT_THEME: "dark"
restart: unless-stopped restart: unless-stopped
+3 -1
View File
@@ -6,6 +6,7 @@ BANNER_BRAND="${BANNER_BRAND:-iQR}"
BANNER_SLOGAN="${BANNER_SLOGAN:-— qr-код без излишеств}" BANNER_SLOGAN="${BANNER_SLOGAN:-— qr-код без излишеств}"
BANNER_URL="${BANNER_URL:-https://github.com/IliaMiheev/iQR}" BANNER_URL="${BANNER_URL:-https://github.com/IliaMiheev/iQR}"
ACCENT_COLOR="${ACCENT_COLOR:-#ff4500}" ACCENT_COLOR="${ACCENT_COLOR:-#ff4500}"
DEFAULT_THEME="${DEFAULT_THEME:-dark}"
cat > /usr/share/nginx/html/config.json <<EOF cat > /usr/share/nginx/html/config.json <<EOF
{ {
@@ -13,7 +14,8 @@ cat > /usr/share/nginx/html/config.json <<EOF
"bannerBrand": "$(printf '%s' "$BANNER_BRAND" | sed 's/"/\\"/g')", "bannerBrand": "$(printf '%s' "$BANNER_BRAND" | sed 's/"/\\"/g')",
"bannerSlogan": "$(printf '%s' "$BANNER_SLOGAN" | sed 's/"/\\"/g')", "bannerSlogan": "$(printf '%s' "$BANNER_SLOGAN" | sed 's/"/\\"/g')",
"bannerUrl": "$(printf '%s' "$BANNER_URL" | sed 's/"/\\"/g')", "bannerUrl": "$(printf '%s' "$BANNER_URL" | sed 's/"/\\"/g')",
"accentColor": "$(printf '%s' "$ACCENT_COLOR" | sed 's/"/\\"/g')" "accentColor": "$(printf '%s' "$ACCENT_COLOR" | sed 's/"/\\"/g')",
"defaultTheme": "$(printf '%s' "$DEFAULT_THEME" | sed 's/"/\\"/g')"
} }
EOF EOF
+2 -2
View File
@@ -134,7 +134,7 @@ onUnmounted(() => {
<div class="qr-modal__backdrop" @click="closeQrModal" /> <div class="qr-modal__backdrop" @click="closeQrModal" />
<aside class="qr-modal__toolbar" aria-label="Инструменты" @click.stop> <aside class="qr-modal__toolbar" aria-label="Инструменты" @click.stop>
<button type="button" title="Увеличить" @click="zoomIn">+</button> <button type="button" title="Уменьшить" @click="zoomOut"></button>
<button <button
type="button" type="button"
class="qr-modal__tool-btn" class="qr-modal__tool-btn"
@@ -153,7 +153,7 @@ onUnmounted(() => {
/> />
</svg> </svg>
</button> </button>
<button type="button" title="Уменьшить" @click="zoomOut"></button> <button type="button" title="Увеличить" @click="zoomIn">+</button>
</aside> </aside>
<div class="qr-modal__dialog" @click.stop> <div class="qr-modal__dialog" @click.stop>
+13 -11
View File
@@ -474,8 +474,9 @@ html.light .qr-modal__backdrop {
border: 1px solid var(--surface-border); border: 1px solid var(--surface-border);
border-radius: 50%; border-radius: 50%;
background: var(--surface-solid); background: var(--surface-solid);
color: var(--text); color: var(--accent);
font-size: 22px; font-size: 22px;
font-weight: 700;
line-height: 1; line-height: 1;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -486,7 +487,8 @@ html.light .qr-modal__backdrop {
} }
.qr-modal__close:hover { .qr-modal__close:hover {
border-color: var(--accent); background: var(--surface-hover);
border-color: var(--surface-border);
color: var(--accent); color: var(--accent);
transform: scale(1.05); transform: scale(1.05);
} }
@@ -511,7 +513,7 @@ html.light .qr-modal__backdrop {
padding: 6px; padding: 6px;
border-radius: 12px; border-radius: 12px;
background: var(--surface-solid); background: var(--surface-solid);
border: 1px solid var(--surface-border); border: none;
box-shadow: var(--shadow-sm); box-shadow: var(--shadow-sm);
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
@@ -531,11 +533,18 @@ html.light .qr-modal__backdrop {
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 8px; border-radius: 8px;
box-shadow: none; box-shadow: none;
transition:
color 0.2s ease,
border-color 0.2s ease,
background-color 0.2s ease,
box-shadow 0.2s ease,
transform 0.15s ease;
} }
.qr-modal__toolbar button:hover { .qr-modal__toolbar button:hover {
background: var(--surface-hover); background: var(--surface-hover);
border-color: var(--surface-border); color: var(--accent);
border-color: var(--accent);
transform: none; transform: none;
box-shadow: none; box-shadow: none;
} }
@@ -551,11 +560,6 @@ html.light .qr-modal__backdrop {
justify-content: center; justify-content: center;
} }
.qr-modal__tool-btn:hover {
color: var(--accent);
border-color: var(--accent) !important;
}
.qr-modal__status-slot { .qr-modal__status-slot {
position: relative; position: relative;
display: flex; display: flex;
@@ -617,7 +621,6 @@ html.light .qr-modal__backdrop {
#qr-code { #qr-code {
display: inline-flex; display: inline-flex;
padding: 16px;
border-radius: 16px; border-radius: 16px;
background: var(--surface-solid); background: var(--surface-solid);
border: 1px solid var(--surface-border); border: 1px solid var(--surface-border);
@@ -643,7 +646,6 @@ canvas {
display: block; display: block;
border-radius: 8px; border-radius: 8px;
background: var(--canvas-bg); background: var(--canvas-bg);
border: 1px solid var(--canvas-border);
image-rendering: pixelated; image-rendering: pixelated;
transition: border-color 0.35s ease; transition: border-color 0.35s ease;
} }
+4 -13
View File
@@ -1,7 +1,9 @@
import { ref, computed, onMounted } from 'vue' import { inject, ref, computed } from 'vue'
import { resolveTheme } from '../config/applyTheme'
export function useTheme() { export function useTheme() {
const theme = ref('dark') const config = inject('appConfig')
const theme = ref(resolveTheme(config))
const isLightTheme = computed(() => theme.value === 'light') const isLightTheme = computed(() => theme.value === 'light')
@@ -15,16 +17,5 @@ export function useTheme() {
applyTheme(theme.value === 'dark' ? 'light' : 'dark') applyTheme(theme.value === 'dark' ? 'light' : 'dark')
} }
onMounted(() => {
const saved = localStorage.getItem('theme')
if (saved === 'dark' || saved === 'light') {
applyTheme(saved)
return
}
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
applyTheme(prefersDark ? 'dark' : 'light')
})
return { theme, isLightTheme, toggleTheme } return { theme, isLightTheme, toggleTheme }
} }
+12
View File
@@ -1,7 +1,19 @@
import { normalizeTheme } from './defaults'
export function resolveTheme(config) {
const saved = localStorage.getItem('theme')
if (saved === 'dark' || saved === 'light') {
return saved
}
return normalizeTheme(config.defaultTheme)
}
export function applyAppConfig(config) { export function applyAppConfig(config) {
const root = document.documentElement const root = document.documentElement
root.style.setProperty('--accent', config.accentColor) root.style.setProperty('--accent', config.accentColor)
root.className = resolveTheme(config)
const themeMeta = document.querySelector('meta[name="theme-color"]') const themeMeta = document.querySelector('meta[name="theme-color"]')
if (themeMeta) { if (themeMeta) {
+6
View File
@@ -4,4 +4,10 @@ export const defaultConfig = {
bannerSlogan: '— qr-код без излишеств', bannerSlogan: '— qr-код без излишеств',
bannerUrl: 'https://github.com/IliaMiheev/iQR', bannerUrl: 'https://github.com/IliaMiheev/iQR',
accentColor: '#ff4500', accentColor: '#ff4500',
defaultTheme: 'dark',
}
export function normalizeTheme(value) {
const normalized = String(value ?? '').trim().toLowerCase()
return normalized === 'light' ? 'light' : 'dark'
} }
+4 -1
View File
@@ -1,4 +1,4 @@
import { defaultConfig } from './defaults' import { defaultConfig, normalizeTheme } from './defaults'
function fromBuildEnv() { function fromBuildEnv() {
const env = import.meta.env const env = import.meta.env
@@ -9,6 +9,7 @@ function fromBuildEnv() {
bannerSlogan: env.VITE_BANNER_SLOGAN, bannerSlogan: env.VITE_BANNER_SLOGAN,
bannerUrl: env.VITE_BANNER_URL, bannerUrl: env.VITE_BANNER_URL,
accentColor: env.VITE_ACCENT_COLOR, accentColor: env.VITE_ACCENT_COLOR,
defaultTheme: env.VITE_DEFAULT_THEME,
}) })
} }
@@ -44,5 +45,7 @@ export async function loadConfig() {
} }
} }
config.defaultTheme = normalizeTheme(config.defaultTheme)
return config return config
} }