feat: refine responsive hero and preference dropdowns
This commit is contained in:
+1
-1
@@ -4,4 +4,4 @@ services:
|
|||||||
container_name: roncarve-web
|
container_name: roncarve-web
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8080:80"
|
- "127.0.0.1:8081:80"
|
||||||
|
|||||||
+147
-62
@@ -207,57 +207,81 @@
|
|||||||
.nav-link.active::after { opacity: 1; transform: translateX(-50%) scale(1); }
|
.nav-link.active::after { opacity: 1; transform: translateX(-50%) scale(1); }
|
||||||
|
|
||||||
.nav-actions { display: flex; align-items: center; gap: 10px; }
|
.nav-actions { display: flex; align-items: center; gap: 10px; }
|
||||||
.theme-switch {
|
.preference-menu { position: relative; }
|
||||||
|
.preference-trigger {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 3px;
|
gap: 8px;
|
||||||
min-height: 38px;
|
min-height: 38px;
|
||||||
padding: 3px;
|
padding: 0 11px;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
background: rgba(255, 255, 255, .025);
|
background: rgba(255, 255, 255, .025);
|
||||||
transition: border-color .2s ease, background .2s ease;
|
color: var(--text-soft);
|
||||||
|
font: 600 12px/1 var(--mono);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color .2s ease, background .2s ease, color .2s ease;
|
||||||
}
|
}
|
||||||
.theme-switch:hover { border-color: var(--line-strong); background: rgba(54, 228, 218, .04); }
|
.preference-trigger:hover,
|
||||||
.theme-option {
|
.preference-menu.open .preference-trigger {
|
||||||
position: relative;
|
border-color: var(--line-strong);
|
||||||
|
background: rgba(54, 228, 218, .06);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.preference-icon {
|
||||||
display: inline-grid;
|
display: inline-grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: var(--text);
|
||||||
|
font: 500 17px/1 "Segoe UI Symbol", "Noto Sans Symbols", sans-serif;
|
||||||
|
}
|
||||||
|
.preference-icon--globe { font-size: 16px; }
|
||||||
|
.preference-chevron { color: var(--text-faint); font: 700 13px/1 var(--mono); transition: transform .2s ease; }
|
||||||
|
.preference-menu.open .preference-chevron { transform: rotate(180deg); }
|
||||||
|
.preference-popover {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 10px);
|
||||||
|
right: 0;
|
||||||
|
z-index: 1100;
|
||||||
|
display: grid;
|
||||||
|
min-width: 174px;
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid var(--line-strong);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: color-mix(in srgb, var(--ink-900) 94%, transparent);
|
||||||
|
box-shadow: 0 18px 44px rgba(0, 0, 0, .2), 0 0 0 1px rgba(255, 255, 255, .035);
|
||||||
|
backdrop-filter: blur(18px) saturate(135%);
|
||||||
|
-webkit-backdrop-filter: blur(18px) saturate(135%);
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateY(-6px) scale(.98);
|
||||||
|
transform-origin: top right;
|
||||||
|
transition: opacity .18s ease, transform .18s var(--ease);
|
||||||
|
}
|
||||||
|
.preference-menu.open .preference-popover { opacity: 1; pointer-events: auto; transform: translateY(0) scale(1); }
|
||||||
|
.preference-item {
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 30px;
|
gap: 10px;
|
||||||
height: 30px;
|
width: 100%;
|
||||||
padding: 0;
|
min-height: 38px;
|
||||||
|
padding: 0 10px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 6px;
|
border-radius: 7px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
|
font: 600 12px/1.4 var(--mono);
|
||||||
|
text-align: left;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background .2s ease, box-shadow .2s ease, transform .2s ease;
|
transition: color .18s ease, background .18s ease;
|
||||||
}
|
}
|
||||||
.theme-option:hover { background: color-mix(in srgb, var(--cyan) 8%, transparent); transform: translateY(-1px); }
|
.preference-item:hover,
|
||||||
.theme-option[aria-checked="true"] { background: color-mix(in srgb, var(--cyan) 14%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cyan) 28%, transparent); }
|
.preference-item[aria-checked="true"] { color: var(--text); background: color-mix(in srgb, var(--cyan) 12%, transparent); }
|
||||||
.theme-option-icon { width: 13px; height: 13px; border: 1px solid var(--line-strong); border-radius: 50%; box-shadow: 0 0 10px color-mix(in srgb, var(--cyan) 16%, transparent); }
|
.preference-item[aria-checked="true"] { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cyan) 25%, transparent); }
|
||||||
.theme-option-icon.system { background: linear-gradient(90deg, #f7fbff 0 49%, #0a1721 51% 100%); }
|
.preference-item-label { flex: 1; }
|
||||||
.theme-option-icon.light { background: #eef7ff; box-shadow: 0 0 0 2px rgba(51, 92, 255, .11), 0 0 9px rgba(74, 141, 255, .22); }
|
.preference-item-check { color: var(--cyan); opacity: 0; font-size: 13px; }
|
||||||
.theme-option-icon.dark { background: #07131d; box-shadow: inset -3px 0 0 #54ddea, 0 0 9px rgba(54, 228, 218, .18); }
|
.preference-item[aria-checked="true"] .preference-item-check { opacity: 1; }
|
||||||
.lang-switch {
|
|
||||||
display: inline-flex;
|
|
||||||
padding: 3px;
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
border-radius: 9px;
|
|
||||||
background: rgba(255, 255, 255, .025);
|
|
||||||
}
|
|
||||||
.lang-btn {
|
|
||||||
min-width: 34px;
|
|
||||||
padding: 5px 8px;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--text-faint);
|
|
||||||
font: 650 12px/1.4 var(--mono);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.lang-btn.active { color: var(--ink-1000); background: var(--cyan); }
|
|
||||||
|
|
||||||
.nav-cta, .button {
|
.nav-cta, .button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -694,12 +718,10 @@
|
|||||||
|
|
||||||
:root[data-theme="light"] .navbar { background: rgba(250, 252, 255, .72); }
|
:root[data-theme="light"] .navbar { background: rgba(250, 252, 255, .72); }
|
||||||
:root[data-theme="light"] .navbar.scrolled { background: rgba(250, 252, 255, .93); box-shadow: 0 12px 40px rgba(35, 53, 87, .08); }
|
:root[data-theme="light"] .navbar.scrolled { background: rgba(250, 252, 255, .93); box-shadow: 0 12px 40px rgba(35, 53, 87, .08); }
|
||||||
:root[data-theme="light"] .lang-switch,
|
:root[data-theme="light"] .preference-trigger,
|
||||||
:root[data-theme="light"] .theme-switch,
|
|
||||||
:root[data-theme="light"] .menu-btn { background: rgba(255, 255, 255, .58); }
|
:root[data-theme="light"] .menu-btn { background: rgba(255, 255, 255, .58); }
|
||||||
:root[data-theme="light"] .nav-cta,
|
:root[data-theme="light"] .nav-cta,
|
||||||
:root[data-theme="light"] .button-primary,
|
:root[data-theme="light"] .button-primary { color: #fff; }
|
||||||
:root[data-theme="light"] .lang-btn.active { color: #fff; }
|
|
||||||
:root[data-theme="light"] .button-secondary { background: rgba(255, 255, 255, .58); }
|
:root[data-theme="light"] .button-secondary { background: rgba(255, 255, 255, .58); }
|
||||||
|
|
||||||
:root[data-theme="light"] .hero {
|
:root[data-theme="light"] .hero {
|
||||||
@@ -733,6 +755,13 @@
|
|||||||
.hero-spatial { width: min(100%, 760px); min-height: 420px; margin-left: auto; }
|
.hero-spatial { width: min(100%, 760px); min-height: 420px; margin-left: auto; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Let desktop layouts use the available canvas on ultrawide screens. */
|
||||||
|
@media (min-width: 1440px) {
|
||||||
|
:root { --max-width: 88vw; }
|
||||||
|
.hero-layout { gap: clamp(64px, 6vw, 132px); }
|
||||||
|
.section-header { gap: clamp(48px, 6vw, 120px); }
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 860px) {
|
@media (max-width: 860px) {
|
||||||
:root[data-theme="light"] .nav-links { background: rgba(250, 252, 255, .98); }
|
:root[data-theme="light"] .nav-links { background: rgba(250, 252, 255, .98); }
|
||||||
}
|
}
|
||||||
@@ -901,20 +930,38 @@
|
|||||||
<a class="nav-link" href="#advantages" data-i18n="nav_advantages">核心优势</a>
|
<a class="nav-link" href="#advantages" data-i18n="nav_advantages">核心优势</a>
|
||||||
<a class="nav-link" href="#offices" data-i18n="nav_offices">交付网络</a>
|
<a class="nav-link" href="#offices" data-i18n="nav_offices">交付网络</a>
|
||||||
<div class="nav-actions">
|
<div class="nav-actions">
|
||||||
<div class="theme-switch" id="themeSwitch" role="radiogroup" aria-label="外观模式" data-i18n-aria="theme_label">
|
<div class="preference-menu" id="themeSwitch">
|
||||||
<button class="theme-option" type="button" role="radio" aria-checked="true" data-theme-option="system" aria-label="跟随系统" data-i18n-aria="theme_system">
|
<button class="preference-trigger" type="button" aria-haspopup="menu" aria-expanded="false" aria-label="外观模式" data-i18n-aria="theme_label">
|
||||||
<span class="theme-option-icon system" aria-hidden="true"></span>
|
<span class="preference-icon" data-theme-current-icon aria-hidden="true">◐</span>
|
||||||
|
<span class="preference-trigger-label" data-theme-current>Auto</span>
|
||||||
|
<span class="preference-chevron" aria-hidden="true">⌄</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="theme-option" type="button" role="radio" aria-checked="false" data-theme-option="light" aria-label="浅色外观" data-i18n-aria="theme_light">
|
<div class="preference-popover" role="menu">
|
||||||
<span class="theme-option-icon light" aria-hidden="true"></span>
|
<button class="preference-item" type="button" role="menuitemradio" aria-checked="true" data-theme-option="system" aria-label="跟随系统" data-i18n-aria="theme_system">
|
||||||
|
<span class="preference-icon" aria-hidden="true">◐</span><span class="preference-item-label" data-i18n="theme_system_short">Auto</span><span class="preference-item-check" aria-hidden="true">✓</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="theme-option" type="button" role="radio" aria-checked="false" data-theme-option="dark" aria-label="深色外观" data-i18n-aria="theme_dark">
|
<button class="preference-item" type="button" role="menuitemradio" aria-checked="false" data-theme-option="dark" aria-label="深色外观" data-i18n-aria="theme_dark">
|
||||||
<span class="theme-option-icon dark" aria-hidden="true"></span>
|
<span class="preference-icon" aria-hidden="true">☾</span><span class="preference-item-label" data-i18n="theme_dark_short">Dark</span><span class="preference-item-check" aria-hidden="true">✓</span>
|
||||||
|
</button>
|
||||||
|
<button class="preference-item" type="button" role="menuitemradio" aria-checked="false" data-theme-option="light" aria-label="浅色外观" data-i18n-aria="theme_light">
|
||||||
|
<span class="preference-icon" aria-hidden="true">☼</span><span class="preference-item-label" data-i18n="theme_light_short">Light</span><span class="preference-item-check" aria-hidden="true">✓</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="preference-menu" id="langSwitch">
|
||||||
|
<button class="preference-trigger" type="button" aria-haspopup="menu" aria-expanded="false" aria-label="语言切换" data-i18n-aria="lang_label">
|
||||||
|
<span class="preference-icon preference-icon--globe" aria-hidden="true">◎</span>
|
||||||
|
<span class="preference-trigger-label" data-language-current>简体中文</span>
|
||||||
|
<span class="preference-chevron" aria-hidden="true">⌄</span>
|
||||||
|
</button>
|
||||||
|
<div class="preference-popover" role="menu">
|
||||||
|
<button class="preference-item" type="button" role="menuitemradio" aria-checked="true" data-lang="zh">
|
||||||
|
<span class="preference-icon preference-icon--globe" aria-hidden="true">◎</span><span class="preference-item-label" data-i18n="language_zh">简体中文</span><span class="preference-item-check" aria-hidden="true">✓</span>
|
||||||
|
</button>
|
||||||
|
<button class="preference-item" type="button" role="menuitemradio" aria-checked="false" data-lang="en">
|
||||||
|
<span class="preference-icon preference-icon--globe" aria-hidden="true">◎</span><span class="preference-item-label" data-i18n="language_en">English</span><span class="preference-item-check" aria-hidden="true">✓</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="lang-switch" id="langSwitch" aria-label="语言切换" data-i18n-aria="lang_label">
|
|
||||||
<button class="lang-btn active" type="button" data-lang="zh" aria-pressed="true">中</button>
|
|
||||||
<button class="lang-btn" type="button" data-lang="en" aria-pressed="false">EN</button>
|
|
||||||
</div>
|
</div>
|
||||||
<a class="nav-cta" href="#contact" data-i18n="nav_contact">预约咨询</a>
|
<a class="nav-cta" href="#contact" data-i18n="nav_contact">预约咨询</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -1109,7 +1156,7 @@
|
|||||||
const i18n = {
|
const i18n = {
|
||||||
zh: {
|
zh: {
|
||||||
title: '海南荣刻科技有限公司 - 数据筑基 · AI 落地', skip: '跳到主要内容', brand: '荣刻科技', nav_label: '主导航', home_label: '荣刻科技首页', lang_label: '语言切换', menu_open: '打开导航菜单', menu_close: '关闭导航菜单',
|
title: '海南荣刻科技有限公司 - 数据筑基 · AI 落地', skip: '跳到主要内容', brand: '荣刻科技', nav_label: '主导航', home_label: '荣刻科技首页', lang_label: '语言切换', menu_open: '打开导航菜单', menu_close: '关闭导航菜单',
|
||||||
theme_label: '外观模式选择', theme_system: '跟随系统', theme_light: '浅色外观', theme_dark: '深色外观',
|
theme_label: '外观模式选择', theme_system: '跟随系统', theme_light: '浅色外观', theme_dark: '深色外观', theme_system_short: 'Auto', theme_light_short: 'Light', theme_dark_short: 'Dark', language_zh: '简体中文', language_en: 'English',
|
||||||
nav_about: '能力链路', nav_services: '解决方案', nav_advantages: '核心优势', nav_offices: '交付网络', nav_contact: '预约咨询',
|
nav_about: '能力链路', nav_services: '解决方案', nav_advantages: '核心优势', nav_offices: '交付网络', nav_contact: '预约咨询',
|
||||||
hero_eyebrow: 'AI 与数据基础设施', hero_line1: '让企业数据', hero_line2: '成为 AI 生产力',
|
hero_eyebrow: 'AI 与数据基础设施', hero_line1: '让企业数据', hero_line2: '成为 AI 生产力',
|
||||||
hero_desc: '从数据治理、模型优化到私有化部署,荣刻科技为企业构建可落地、可持续、可掌控的 AI 能力体系。',
|
hero_desc: '从数据治理、模型优化到私有化部署,荣刻科技为企业构建可落地、可持续、可掌控的 AI 能力体系。',
|
||||||
@@ -1143,7 +1190,7 @@
|
|||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
title: 'Hainan Roncarve Technology - Data Foundation · AI Delivery', skip: 'Skip to main content', brand: 'Roncarve Tech', nav_label: 'Main navigation', home_label: 'Roncarve Tech home', lang_label: 'Language selector', menu_open: 'Open navigation menu', menu_close: 'Close navigation menu',
|
title: 'Hainan Roncarve Technology - Data Foundation · AI Delivery', skip: 'Skip to main content', brand: 'Roncarve Tech', nav_label: 'Main navigation', home_label: 'Roncarve Tech home', lang_label: 'Language selector', menu_open: 'Open navigation menu', menu_close: 'Close navigation menu',
|
||||||
theme_label: 'Appearance selector', theme_system: 'Follow System', theme_light: 'Light appearance', theme_dark: 'Dark appearance',
|
theme_label: 'Appearance selector', theme_system: 'Follow System', theme_light: 'Light appearance', theme_dark: 'Dark appearance', theme_system_short: 'Auto', theme_light_short: 'Light', theme_dark_short: 'Dark', language_zh: '简体中文', language_en: 'English',
|
||||||
nav_about: 'Capabilities', nav_services: 'Solutions', nav_advantages: 'Advantages', nav_offices: 'Delivery', nav_contact: 'Book a Call',
|
nav_about: 'Capabilities', nav_services: 'Solutions', nav_advantages: 'Advantages', nav_offices: 'Delivery', nav_contact: 'Book a Call',
|
||||||
hero_eyebrow: 'AI + DATA INFRASTRUCTURE', hero_line1: 'Turn enterprise data', hero_line2: 'into AI productivity',
|
hero_eyebrow: 'AI + DATA INFRASTRUCTURE', hero_line1: 'Turn enterprise data', hero_line2: 'into AI productivity',
|
||||||
hero_desc: 'From data governance and model optimization to private deployment, Roncarve builds practical, sustainable and controllable enterprise AI systems.',
|
hero_desc: 'From data governance and model optimization to private deployment, Roncarve builds practical, sustainable and controllable enterprise AI systems.',
|
||||||
@@ -1187,6 +1234,8 @@
|
|||||||
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
let currentLanguage = 'zh';
|
let currentLanguage = 'zh';
|
||||||
let themePreference = document.documentElement.dataset.themePreference || 'system';
|
let themePreference = document.documentElement.dataset.themePreference || 'system';
|
||||||
|
const themeLabels = { system: 'theme_system_short', light: 'theme_light_short', dark: 'theme_dark_short' };
|
||||||
|
const themeIcons = { system: '◐', light: '☼', dark: '☾' };
|
||||||
|
|
||||||
function resolvedTheme(preference) {
|
function resolvedTheme(preference) {
|
||||||
return preference === 'system' ? (themeQuery.matches ? 'dark' : 'light') : preference;
|
return preference === 'system' ? (themeQuery.matches ? 'dark' : 'light') : preference;
|
||||||
@@ -1201,6 +1250,10 @@
|
|||||||
document.querySelectorAll('[data-theme-option]').forEach(function (button) {
|
document.querySelectorAll('[data-theme-option]').forEach(function (button) {
|
||||||
button.setAttribute('aria-checked', String(button.dataset.themeOption === selected));
|
button.setAttribute('aria-checked', String(button.dataset.themeOption === selected));
|
||||||
});
|
});
|
||||||
|
const themeCurrent = document.querySelector('[data-theme-current]');
|
||||||
|
const themeCurrentIcon = document.querySelector('[data-theme-current-icon]');
|
||||||
|
if (themeCurrent) themeCurrent.textContent = i18n[currentLanguage][themeLabels[selected]];
|
||||||
|
if (themeCurrentIcon) themeCurrentIcon.textContent = themeIcons[selected];
|
||||||
const themeColors = { light: '#f7faff', dark: '#05070a' };
|
const themeColors = { light: '#f7faff', dark: '#05070a' };
|
||||||
const themeMeta = document.querySelector('meta[name="theme-color"]');
|
const themeMeta = document.querySelector('meta[name="theme-color"]');
|
||||||
if (themeMeta) themeMeta.setAttribute('content', themeColors[resolved]);
|
if (themeMeta) themeMeta.setAttribute('content', themeColors[resolved]);
|
||||||
@@ -1224,11 +1277,17 @@
|
|||||||
if (i18n[selected][key]) element.setAttribute('aria-label', i18n[selected][key]);
|
if (i18n[selected][key]) element.setAttribute('aria-label', i18n[selected][key]);
|
||||||
});
|
});
|
||||||
menuBtn.setAttribute('aria-label', i18n[selected][navLinks.classList.contains('open') ? 'menu_close' : 'menu_open']);
|
menuBtn.setAttribute('aria-label', i18n[selected][navLinks.classList.contains('open') ? 'menu_close' : 'menu_open']);
|
||||||
document.querySelectorAll('.lang-btn').forEach(function (button) {
|
document.querySelectorAll('[data-lang]').forEach(function (button) {
|
||||||
const active = button.dataset.lang === selected;
|
const active = button.dataset.lang === selected;
|
||||||
button.classList.toggle('active', active);
|
button.setAttribute('aria-checked', String(active));
|
||||||
button.setAttribute('aria-pressed', String(active));
|
|
||||||
});
|
});
|
||||||
|
const languageCurrent = document.querySelector('[data-language-current]');
|
||||||
|
if (languageCurrent) languageCurrent.textContent = i18n[selected].language_zh;
|
||||||
|
if (languageCurrent && selected === 'en') languageCurrent.textContent = i18n[selected].language_en;
|
||||||
|
if (themePreference) {
|
||||||
|
const themeCurrent = document.querySelector('[data-theme-current]');
|
||||||
|
if (themeCurrent) themeCurrent.textContent = i18n[selected][themeLabels[themePreference]];
|
||||||
|
}
|
||||||
try { localStorage.setItem('site_lang', selected); } catch (error) { /* Storage may be disabled. */ }
|
try { localStorage.setItem('site_lang', selected); } catch (error) { /* Storage may be disabled. */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1240,19 +1299,39 @@
|
|||||||
document.body.classList.remove('menu-open');
|
document.body.classList.remove('menu-open');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closePreferenceMenus(except) {
|
||||||
|
document.querySelectorAll('.preference-menu.open').forEach(function (menu) {
|
||||||
|
if (menu !== except) {
|
||||||
|
menu.classList.remove('open');
|
||||||
|
menu.querySelector('.preference-trigger').setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[themeSwitch, langSwitch].forEach(function (menu) {
|
||||||
|
const trigger = menu.querySelector('.preference-trigger');
|
||||||
|
trigger.addEventListener('click', function () {
|
||||||
|
const open = !menu.classList.contains('open');
|
||||||
|
closePreferenceMenus(menu);
|
||||||
|
menu.classList.toggle('open', open);
|
||||||
|
trigger.setAttribute('aria-expanded', String(open));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
langSwitch.addEventListener('click', function (event) {
|
langSwitch.addEventListener('click', function (event) {
|
||||||
const button = event.target.closest('.lang-btn');
|
const button = event.target.closest('[data-lang]');
|
||||||
if (button) applyLanguage(button.dataset.lang);
|
if (!button) return;
|
||||||
|
applyLanguage(button.dataset.lang);
|
||||||
|
langSwitch.classList.remove('open');
|
||||||
|
langSwitch.querySelector('.preference-trigger').setAttribute('aria-expanded', 'false');
|
||||||
});
|
});
|
||||||
|
|
||||||
themeSwitch.addEventListener('click', function (event) {
|
themeSwitch.addEventListener('click', function (event) {
|
||||||
const button = event.target.closest('[data-theme-option]');
|
const button = event.target.closest('[data-theme-option]');
|
||||||
if (!button) return;
|
if (!button) return;
|
||||||
applyTheme(button.dataset.themeOption, true);
|
applyTheme(button.dataset.themeOption, true);
|
||||||
if (window.matchMedia('(max-width: 860px)').matches) {
|
themeSwitch.classList.remove('open');
|
||||||
closeMenu();
|
themeSwitch.querySelector('.preference-trigger').setAttribute('aria-expanded', 'false');
|
||||||
menuBtn.focus();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSystemTheme = function () {
|
const handleSystemTheme = function () {
|
||||||
@@ -1272,7 +1351,13 @@
|
|||||||
|
|
||||||
navLinks.querySelectorAll('a').forEach(function (link) { link.addEventListener('click', closeMenu); });
|
navLinks.querySelectorAll('a').forEach(function (link) { link.addEventListener('click', closeMenu); });
|
||||||
document.addEventListener('keydown', function (event) {
|
document.addEventListener('keydown', function (event) {
|
||||||
if (event.key === 'Escape') closeMenu();
|
if (event.key === 'Escape') {
|
||||||
|
closeMenu();
|
||||||
|
closePreferenceMenus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener('click', function (event) {
|
||||||
|
if (!event.target.closest('.preference-menu')) closePreferenceMenus();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('scroll', function () { navbar.classList.toggle('scrolled', window.scrollY > 24); }, { passive: true });
|
window.addEventListener('scroll', function () { navbar.classList.toggle('scrolled', window.scrollY > 24); }, { passive: true });
|
||||||
|
|||||||
+6
-1
@@ -783,7 +783,12 @@ if (root && canvas) {
|
|||||||
|
|
||||||
const aspect = width / height;
|
const aspect = width / height;
|
||||||
worldCamera.aspect = aspect;
|
worldCamera.aspect = aspect;
|
||||||
worldCamera.fov = aspect < 0.75 ? 38 : 33;
|
// Keep the prism's authored scale on ultrawide displays. The
|
||||||
|
// background can fill the full canvas, but the 3D composition
|
||||||
|
// should not become disproportionately small as the viewport
|
||||||
|
// gets wider than the original desktop frame.
|
||||||
|
const compositionAspect = Math.min(aspect, 2.25);
|
||||||
|
worldCamera.fov = aspect < 0.75 ? 38 : (aspect > 1.8 ? 33 * 1.8 / compositionAspect : 33);
|
||||||
worldCamera.updateProjectionMatrix();
|
worldCamera.updateProjectionMatrix();
|
||||||
if (aspect > 1.1) {
|
if (aspect > 1.1) {
|
||||||
worldRoot.position.set(Math.min(1.92, aspect * 1.15), -0.02, 0);
|
worldRoot.position.set(Math.min(1.92, aspect * 1.15), -0.02, 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user