feat: split logo scale by viewport

This commit is contained in:
3252a8
2026-06-03 12:24:18 +03:00
parent 602cbc7ee6
commit d8fe88eb01
14 changed files with 269 additions and 97 deletions
@@ -178,10 +178,8 @@
return `#${hex}`;
}
function homeLogoScale(theme) {
const scale = Number(theme.tokens?.home_logo_scale || 100);
if (!Number.isFinite(scale)) return 100;
return Math.min(300, Math.max(50, Math.round(scale)));
function homeLogoScale(theme, mode) {
return themesStore.resolveThemeHomeLogoScale(theme, mode);
}
function openThemeAccentPicker(theme) {
@@ -296,8 +294,8 @@
themesStore.setThemeAccent(theme.key, value);
}
function setThemeHomeLogoScale(theme, value) {
themesStore.setThemeHomeLogoScale(theme.key, value);
function setThemeHomeLogoScale(theme, mode, value) {
themesStore.setThemeHomeLogoScale(theme.key, mode, value);
}
function isThemeControlTarget(target) {
@@ -586,11 +584,11 @@
<span>{at("themes_use_in_admin", {}, "Использовать в админке")}</span>
</label>
<div class="admin-theme-card-option appearance-logo-scale-row">
<span
<span class="appearance-logo-scale-label"
>{at(
"appearance_theme_home_logo_scale",
"appearance_theme_home_logo_scale_desktop",
{},
"Логотип на главной и входе"
"Логотип на десктопе"
)}</span
>
<RangeInput
@@ -598,9 +596,13 @@
min="50"
max="300"
step="5"
ariaLabel={at("appearance_theme_home_logo_scale", {}, "Home logo scale")}
value={homeLogoScale(theme)}
onValueChange={(value) => setThemeHomeLogoScale(theme, value)}
ariaLabel={at(
"appearance_theme_home_logo_scale_desktop",
{},
"Desktop logo scale"
)}
value={homeLogoScale(theme, "desktop")}
onValueChange={(value) => setThemeHomeLogoScale(theme, "desktop", value)}
/>
<span class="appearance-logo-scale-value">
<Input
@@ -609,8 +611,44 @@
min="50"
max="300"
step="5"
value={homeLogoScale(theme)}
oninput={(event) => setThemeHomeLogoScale(theme, event.currentTarget.value)}
value={homeLogoScale(theme, "desktop")}
oninput={(event) =>
setThemeHomeLogoScale(theme, "desktop", event.currentTarget.value)}
/>
%
</span>
</div>
<div class="admin-theme-card-option appearance-logo-scale-row">
<span class="appearance-logo-scale-label"
>{at(
"appearance_theme_home_logo_scale_mobile",
{},
"Логотип на мобильных"
)}</span
>
<RangeInput
class="appearance-logo-scale-range"
min="50"
max="300"
step="5"
ariaLabel={at(
"appearance_theme_home_logo_scale_mobile",
{},
"Mobile logo scale"
)}
value={homeLogoScale(theme, "mobile")}
onValueChange={(value) => setThemeHomeLogoScale(theme, "mobile", value)}
/>
<span class="appearance-logo-scale-value">
<Input
class="input"
type="number"
min="50"
max="300"
step="5"
value={homeLogoScale(theme, "mobile")}
oninput={(event) =>
setThemeHomeLogoScale(theme, "mobile", event.currentTarget.value)}
/>
%
</span>
@@ -847,10 +885,15 @@
.appearance-logo-scale-row {
display: grid;
grid-template-columns: auto minmax(96px, 1fr) auto;
grid-template-columns: minmax(112px, 0.8fr) minmax(96px, 1fr) auto;
width: 100%;
}
.appearance-logo-scale-label {
min-width: 0;
line-height: 1.25;
}
:global(.appearance-logo-scale-range) {
width: 100%;
accent-color: var(--accent);
@@ -910,5 +953,13 @@
.appearance-url-row {
grid-template-columns: 1fr;
}
.appearance-logo-scale-row {
grid-template-columns: minmax(0, 1fr) auto;
}
:global(.appearance-logo-scale-range) {
grid-column: 1 / -1;
}
}
</style>
+7 -3
View File
@@ -29,9 +29,13 @@ function applyDemoThemeTokens(catalog) {
const themes = catalog?.themes || [];
for (const theme of themes) {
theme.tokens = theme.tokens || {};
const currentScale = Number(theme.tokens.home_logo_scale);
if (!Number.isFinite(currentScale) || currentScale > DEMO_HOME_LOGO_SCALE) {
theme.tokens.home_logo_scale = DEMO_HOME_LOGO_SCALE;
const logoScaleKeys = ["home_logo_scale_desktop", "home_logo_scale_mobile"];
for (const key of ["home_logo_scale", ...logoScaleKeys]) {
if (key !== "home_logo_scale" && !(key in theme.tokens)) continue;
const currentScale = Number(theme.tokens[key]);
if (!Number.isFinite(currentScale) || currentScale > DEMO_HOME_LOGO_SCALE) {
theme.tokens[key] = DEMO_HOME_LOGO_SCALE;
}
}
}
}
+37 -17
View File
@@ -4,6 +4,23 @@ function cloneCatalog(catalog) {
import { writable } from "svelte/store";
const HOME_LOGO_SCALE_TOKEN = {
desktop: "home_logo_scale_desktop",
mobile: "home_logo_scale_mobile",
};
function normalizeHomeLogoScale(scale) {
if (String(scale ?? "").trim() === "") scale = 100;
const numeric = Number(scale);
return Number.isFinite(numeric) ? Math.min(300, Math.max(50, Math.round(numeric))) : 100;
}
function resolveThemeHomeLogoScale(theme, mode = "desktop") {
const tokens = theme?.tokens || {};
const modeKey = HOME_LOGO_SCALE_TOKEN[mode] || HOME_LOGO_SCALE_TOKEN.desktop;
return normalizeHomeLogoScale(tokens[modeKey] ?? tokens.home_logo_scale ?? 100);
}
export function createThemesStore({ api, onThemesSaved, flash, at }) {
const state = writable({
themesCatalog: { default_theme: "dark", themes: [] },
@@ -214,27 +231,29 @@ export function createThemesStore({ api, onThemesSaved, flash, at }) {
}));
}
function setThemeHomeLogoScale(key, scale) {
if (String(scale ?? "").trim() === "") scale = 100;
const numeric = Number(scale);
const nextScale = Number.isFinite(numeric)
? Math.min(300, Math.max(50, Math.round(numeric)))
: 100;
function setThemeHomeLogoScale(key, mode, scale) {
const normalizedMode = mode === "mobile" ? "mobile" : "desktop";
const nextScale = normalizeHomeLogoScale(scale);
state.update((s) => ({
...s,
themesCatalog: {
...s.themesCatalog,
themes: (s.themesCatalog.themes || []).map((theme) =>
theme.key === key
? {
...theme,
tokens: {
...(theme.tokens || {}),
home_logo_scale: nextScale === 100 ? null : nextScale,
},
}
: theme
),
themes: (s.themesCatalog.themes || []).map((theme) => {
if (theme.key !== key) return theme;
const desktopScale =
normalizedMode === "desktop" ? nextScale : resolveThemeHomeLogoScale(theme, "desktop");
const mobileScale =
normalizedMode === "mobile" ? nextScale : resolveThemeHomeLogoScale(theme, "mobile");
return {
...theme,
tokens: {
...(theme.tokens || {}),
home_logo_scale: null,
home_logo_scale_desktop: desktopScale === 100 ? null : desktopScale,
home_logo_scale_mobile: mobileScale === 100 ? null : mobileScale,
},
};
}),
},
}));
}
@@ -246,6 +265,7 @@ export function createThemesStore({ api, onThemesSaved, flash, at }) {
setCurrentTheme,
setThemeAccent,
setThemeHomeLogoScale,
resolveThemeHomeLogoScale,
togglePrimaryAccent,
toggleAdminUse,
uploadLogoFile,
+48
View File
@@ -113529,6 +113529,54 @@ export const DEMO_DATASET = {
},
},
},
{
key: "admin_appearance_theme_home_logo_scale_desktop",
audience: "internal",
values: {
ru: {
base: "Логотип на десктопе",
fallback: "Логотип на десктопе",
effective: "Логотип на десктопе",
override: "",
overridden: false,
updated_at: null,
updated_by: null,
},
en: {
base: "Desktop logo scale",
fallback: "Логотип на десктопе",
effective: "Desktop logo scale",
override: "",
overridden: false,
updated_at: null,
updated_by: null,
},
},
},
{
key: "admin_appearance_theme_home_logo_scale_mobile",
audience: "internal",
values: {
ru: {
base: "Логотип на мобильных",
fallback: "Логотип на мобильных",
effective: "Логотип на мобильных",
override: "",
overridden: false,
updated_at: null,
updated_by: null,
},
en: {
base: "Mobile logo scale",
fallback: "Логотип на мобильных",
effective: "Mobile logo scale",
override: "",
overridden: false,
updated_at: null,
updated_by: null,
},
},
},
{
key: "admin_appearance_themes_sub",
audience: "internal",
+9 -1
View File
@@ -33,6 +33,8 @@ const TOKEN_TO_CSS_VAR = {
font_logo: "--font-logo",
font_mono: "--font-mono",
home_logo_scale: "--home-logo-scale",
home_logo_scale_desktop: "--home-logo-scale-desktop",
home_logo_scale_mobile: "--home-logo-scale-mobile",
admin_bg: "--admin-bg",
admin_surface: "--admin-surface",
admin_surface_2: "--admin-surface-2",
@@ -44,6 +46,12 @@ const TOKEN_TO_CSS_VAR = {
admin_dim: "--admin-dim",
};
const LOGO_SCALE_TOKEN_KEYS = new Set([
"home_logo_scale",
"home_logo_scale_desktop",
"home_logo_scale_mobile",
]);
export function themeTokensToInlineStyle(tokens, primaryFallback = "#00fe7a", options = {}) {
const t = tokens && typeof tokens === "object" ? tokens : {};
const parts = [];
@@ -54,7 +62,7 @@ export function themeTokensToInlineStyle(tokens, primaryFallback = "#00fe7a", op
if (key === "accent") continue;
const value = t[key];
if (value === undefined || value === null || value === "") continue;
if (key === "home_logo_scale") {
if (LOGO_SCALE_TOKEN_KEYS.has(key)) {
const scale = Number(value);
if (!Number.isFinite(scale) || scale <= 0) continue;
parts.push(`${cssVar}:${scale / 100}`);
+7
View File
@@ -111,6 +111,7 @@ input {
}
.app-shell {
--home-logo-scale-effective: var(--home-logo-scale-mobile, var(--home-logo-scale, 1));
min-height: 100dvh;
background: var(--bg, #02070b) !important;
}
@@ -119,3 +120,9 @@ input {
--desktop-rail-width: 252px;
--desktop-page-gutter: clamp(28px, 4vw, 72px);
}
@media (min-width: 1024px) {
.app-shell {
--home-logo-scale-effective: var(--home-logo-scale-desktop, var(--home-logo-scale, 1));
}
}
+12 -12
View File
@@ -64,9 +64,9 @@ a {
}
.app-shell .loader .brand-mark.brand-mark-lg {
width: calc(4.125rem * var(--home-logo-scale, 1));
height: calc(4.125rem * var(--home-logo-scale, 1));
font-size: calc(2.875rem * var(--home-logo-scale, 1));
width: calc(4.125rem * var(--home-logo-scale-effective, 1));
height: calc(4.125rem * var(--home-logo-scale-effective, 1));
font-size: calc(2.875rem * var(--home-logo-scale-effective, 1));
}
.app-header,
@@ -2207,15 +2207,15 @@ a {
}
.app-shell .home-brand .brand-mark.brand-mark-xl {
width: calc(6rem * var(--home-logo-scale, 1));
height: calc(6rem * var(--home-logo-scale, 1));
font-size: calc(4.375rem * var(--home-logo-scale, 1));
width: calc(6rem * var(--home-logo-scale-effective, 1));
height: calc(6rem * var(--home-logo-scale-effective, 1));
font-size: calc(4.375rem * var(--home-logo-scale-effective, 1));
}
.app-shell .login-brand-auth .brand-mark.brand-mark-xl {
width: calc(116px * var(--home-logo-scale, 1));
height: calc(116px * var(--home-logo-scale, 1));
font-size: calc(84px * var(--home-logo-scale, 1));
width: calc(116px * var(--home-logo-scale-effective, 1));
height: calc(116px * var(--home-logo-scale-effective, 1));
font-size: calc(84px * var(--home-logo-scale-effective, 1));
}
.home-bottom {
@@ -3027,9 +3027,9 @@ a {
.app-shell .home-brand .brand-mark,
.app-shell .home-brand .brand-mark.brand-mark-lg,
.app-shell .home-brand .brand-mark.brand-mark-xl {
width: calc(clamp(180px, 18vw, 260px) * var(--home-logo-scale, 1));
height: calc(clamp(180px, 18vw, 260px) * var(--home-logo-scale, 1));
font-size: calc(clamp(124px, 13vw, 184px) * var(--home-logo-scale, 1));
width: calc(clamp(180px, 18vw, 260px) * var(--home-logo-scale-effective, 1));
height: calc(clamp(180px, 18vw, 260px) * var(--home-logo-scale-effective, 1));
font-size: calc(clamp(124px, 13vw, 184px) * var(--home-logo-scale-effective, 1));
}
.home-bottom {