feat: port cms header/footer chrome visuals + mobile drawer into trade-pro
Task 10j: reshape the platform chrome DOM (sf-site-head/pro-header/ pro-navigation/pro-mega/pro-footer) into the cms Header.astro/Footer.astro design via a pure CSS layer in trade-pro pro.css (--pro-* token bridge + scoped 0,2,0 overrides): sticky glass header, right-aligned semibold nav with brand underline hover, white dropdown cards with desktop hover/focus reveal (>=1025px only; drawer semantics below), cms button CTA, dark four-column-style footer with hairline copyright bar, cms wrap geometry. Mobile drawer reuses the upstream V8.21.3 four-theme mechanism verbatim (commercial.css locked styles + pro.js generic [data-pro-menu]/ #pro-navigation hooks trade-pro already emits); mobile_tabs is template-driven so trade-pro gains the render block (English labels, data-mobile-open-menu opens the shared drawer). pro.html: +footer_nav-gated <h3>Quick Links</h3> (cms column heading), +mobile tabs block. No DOM restructuring elsewhere; site_design data flow (announcement/CTA/search/footer/mobile_tabs) and pro.js hooks intact. Tests: +test_trade_pro_chrome_layout (structure/hooks/EN fallback nav/ css evidence/drawer assets + safe-area/mobile_tabs publish cycle). 35 passed in theme suite; full run 364 passed + same 10 known infra fails. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -798,6 +798,112 @@ def test_trade_pro_standard_layout_structure(factory):
|
||||
assert ssoup.select_one('.sf-tp-page-hero-lead') is not None # bootstrap 摘要在导语位
|
||||
|
||||
|
||||
def test_trade_pro_chrome_layout(factory):
|
||||
# Task 10j:cms 页眉/页脚 chrome 视觉还原 + 手机端抽屉导航。
|
||||
# 设计源 D:/project/cms/runtimes/astro-site-runtime/src/components/
|
||||
# Header.astro(玻璃吸顶 + 右对齐半粗导航 + 悬停下划线/下拉卡 + 移动
|
||||
# 右滑抽屉)/ Footer.astro(深色四栏 + 版权条),10j 以纯样式层把平台
|
||||
# chrome DOM(sf-site-head/pro-header/pro-navigation/pro-footer)重塑为
|
||||
# cms 设计——平台数据流(site_design 导航/公告/CTA/页脚选项)与 pro.js
|
||||
# 交互钩子(data-pro-menu/data-pro-catalog/data-pro-mega/pro-nav-disclose)
|
||||
# 全部保留。抽屉复用上游 V8.21.3 四主题机制(commercial.css 样式段 +
|
||||
# pro.js 通用逻辑:选择器寻钩子而非主题分支,trade-pro 输出同款类名
|
||||
# 结构即自动可用);mobile_tabs 为模板驱动,10j 给 trade-pro 补上
|
||||
# 渲染块后配置即生效(下方发布前后双构建断言)。
|
||||
import re
|
||||
sid = site(factory)
|
||||
assert factory.post(f'/api/factory/sites/{sid}/bootstrap').status_code == 200
|
||||
assert factory.put(f'/api/factory/sites/{sid}/theme', json={'theme': 'trade-pro', 'version': '1.0.0'}).status_code == 200
|
||||
bid = factory.post(f'/api/sites/{sid}/builds', json={}).json()['version']
|
||||
html = factory.get(f'/preview/{sid}/{bid}/').text
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
# ── 平台 chrome 结构与交互钩子保留(数据流不破坏的结构证据)──
|
||||
header = soup.select_one('header.sf-site-head.pro-header')
|
||||
assert header is not None
|
||||
assert header.select_one('.pro-wrap.pro-head-inner') is not None
|
||||
assert header.select_one('a.pro-logo[href$="/"]') is not None # /preview/ 重写根相对 href,用后缀匹配
|
||||
toggle = header.select_one('button.pro-mobile-toggle[data-pro-menu]')
|
||||
assert toggle is not None and toggle.get('aria-controls') == 'pro-navigation'
|
||||
nav = header.select_one('nav#pro-navigation.pro-navigation')
|
||||
assert nav is not None and nav.get('aria-label')
|
||||
# catalog/mega 钩子(aria-controls 对应 mega 面板 id;面板本体按 categories 条件渲染)
|
||||
catalog = header.select_one('button.pro-catalog-toggle[data-pro-catalog]')
|
||||
assert catalog is not None and catalog.get('aria-controls') == 'pro-mega-content'
|
||||
# CTA 双挂 sf-tp-btn(cms 蓝按钮形态)+ chrome.header CTA 数据位
|
||||
cta = header.select_one('a.pro-header-cta.sf-tp-btn')
|
||||
assert cta is not None and cta.get('href', '').endswith('/contact/')
|
||||
# 公告条数据位仍在(未发布 site_design 时渲染英文兜底文案)
|
||||
assert soup.select_one('.pro-utility .pro-wrap') is not None
|
||||
# ── 英文兜底导航(tp_nav):7 项默认栏目全渲染(尾部可跟 Search 链接——
|
||||
# chrome.header.show_search 数据位默认开)──
|
||||
nav_links = nav.select(':scope > a, :scope > .pro-nav-group > a')
|
||||
nav_text = [a.get_text(strip=True) for a in nav_links]
|
||||
assert nav_text[:7] == ['Home', 'Products', 'Brands', 'News', 'Cases', 'About', 'Contact']
|
||||
assert nav_text[7:] in ([], ['Search'])
|
||||
# ── 页脚:平台 DOM + cms 栏目标题(Quick Links 随 footer_nav 数据流)
|
||||
footer = soup.select_one('footer.sf-site-foot.pro-footer')
|
||||
assert footer is not None
|
||||
assert footer.select_one('.pro-footer-top') is not None
|
||||
heading = footer.select_one('.pro-footer-links h3')
|
||||
assert heading is not None and heading.get_text(strip=True) == 'Quick Links'
|
||||
footer_nav = footer.select('.pro-footer-links ul li a')
|
||||
assert len(footer_nav) == 7 and footer_nav[0].get_text(strip=True) == 'Home'
|
||||
assert footer.select_one('.pro-footer-bottom') is not None
|
||||
# ── 10j 视觉层随构建分发(pro.css:cms chrome 映射的样式证据)──
|
||||
pro_css = factory.get(f'/preview/{sid}/{bid}/assets/pro.css').text
|
||||
assert 'Task 10j' in pro_css
|
||||
# 玻璃拟态吸顶(Header.astro .site-header)
|
||||
assert re.search(r'\.sf-theme-trade-pro \.pro-header\{[^}]*position:sticky', pro_css)
|
||||
assert 'backdrop-filter:blur(10px)' in pro_css
|
||||
# 导航:cms 右对齐半粗 + 悬停下划线(.nav-links a 的 border-block-end 方案)
|
||||
assert re.search(r'\.sf-theme-trade-pro \.pro-navigation \.pro-nav-group>a[^{]*\{[^}]*border-block-end', pro_css)
|
||||
assert 'var(--sf-tp-color-border-brand)' in pro_css
|
||||
# 下拉卡(.sub-menu):白卡 + 发丝边 + radius-sm + shadow-md 的令牌引用
|
||||
assert re.search(r'\.sf-theme-trade-pro \.pro-nav-children\{[^}]*var\(--sf-tp-color-surface-default\)', pro_css)
|
||||
# 深色页脚(.site-footer 令牌桥:surface-inverse + 版权条 border-inverse)
|
||||
assert re.search(r'\.sf-theme-trade-pro \.pro-footer\{[^}]*var\(--sf-tp-color-surface-inverse\)', pro_css)
|
||||
assert 'var(--sf-tp-color-border-inverse)' in pro_css
|
||||
# 抽屉品牌化变量桥:平台 drawer 样式段消费 --pro-* 变量,10j 注入 cms 令牌
|
||||
assert '--pro-background:var(--sf-tp-color-surface-default)' in pro_css
|
||||
# ── 上游抽屉机制复用(commercial.css/pro.js 随构建分发 + 安全区适配)──
|
||||
assert 'assets/commercial.css' in html and 'assets/pro.js' in html
|
||||
commercial = factory.get(f'/preview/{sid}/{bid}/assets/commercial.css').text
|
||||
assert '.sf-mobile-drawer-backdrop' in commercial and 'sf-drawer-open' in commercial
|
||||
assert 'env(safe-area-inset-bottom' in commercial # 抽屉/底部 tabs 的安全区适配
|
||||
pro_js = factory.get(f'/preview/{sid}/{bid}/assets/pro.js').text
|
||||
assert 'sf-drawer-header' in pro_js and 'sf-drawer-extra' in pro_js and 'Escape' in pro_js
|
||||
# ── mobile_tabs:发布 site_design 前不渲染,发布后渲染(含 menu 动作开抽屉)──
|
||||
assert 'data-mobile-tabs' not in html
|
||||
import copy
|
||||
from app import site_design_v87
|
||||
config = copy.deepcopy(site_design_v87.DEFAULTS)
|
||||
config['mobile_tabs'] = {'enabled': True, 'items': [
|
||||
{'title': 'Home', 'icon': 'home', 'route': '/', 'action': 'link'},
|
||||
{'title': 'Products', 'icon': 'products', 'route': '/products/', 'action': 'link'},
|
||||
{'title': 'Menu', 'icon': 'menu', 'route': '/', 'action': 'menu'}]}
|
||||
url = f'/api/sites/{sid}/site-design'
|
||||
assert factory.put(url, json={'config': config, 'expected_revision': 0}).status_code == 200
|
||||
assert factory.post(url + '/publish', json={'expected_revision': 1}).status_code == 200
|
||||
bid2 = factory.post(f'/api/sites/{sid}/builds', json={}).json()['version']
|
||||
html2 = factory.get(f'/preview/{sid}/{bid2}/').text
|
||||
assert 'data-mobile-tabs' in html2 and 'data-mobile-open-menu' in html2
|
||||
tabs = BeautifulSoup(html2, 'html.parser').select('.sf-mobile-tabs .sf-mobile-tab')
|
||||
assert len(tabs) == 3
|
||||
assert [t.select_one('.sf-tab-label').get_text(strip=True) for t in tabs] == ['Home', 'Products', 'Menu']
|
||||
assert tabs[0].select_one('.sf-tab-icon') is not None # 图标位渲染
|
||||
# 发布后 chrome_is_custom 生效:公告条走配置数据位(announcement 文案 +
|
||||
# show_announcement 开关)。publish 会清空草稿 → 新 PUT 从 revision 0 重新开始
|
||||
announced = {**config, 'header': {**config['header'], 'announcement': 'Global sourcing update', 'show_announcement': True}}
|
||||
assert factory.put(url, json={'config': announced, 'expected_revision': 0}).status_code == 200
|
||||
assert factory.post(url + '/publish', json={'expected_revision': 1}).status_code == 200
|
||||
bid3 = factory.post(f'/api/sites/{sid}/builds', json={}).json()['version']
|
||||
html3 = factory.get(f'/preview/{sid}/{bid3}/').text
|
||||
assert 'Global sourcing update' in html3
|
||||
# 本构建链接全通(chrome 改动不产生断链)
|
||||
report = factory.get(f'/api/sites/{sid}/builds/{bid3}/report').json()
|
||||
assert not report['errors'] and not report['broken_links']
|
||||
|
||||
|
||||
def test_frontend_entries_include_trade_pro():
|
||||
from pathlib import Path
|
||||
base = Path(__file__).resolve().parent.parent
|
||||
|
||||
@@ -589,3 +589,159 @@
|
||||
-body-section 仅作模板定位钩子,无视觉规则;响应式(767/620 hero 竖版
|
||||
与正文紧凑字号)全部由 10c/10d 既有媒体块经共用类名覆盖——无自有媒体块
|
||||
即无级联顺序风险(10c 教训:同特异性按源顺序取胜)。 #}
|
||||
|
||||
/* ── Task 10j:cms 页眉/页脚 chrome 视觉还原 + 手机端抽屉导航 ──
|
||||
源 src/components/Header.astro(玻璃拟态吸顶:brand-soft 88% + blur(10px)、
|
||||
右对齐半粗导航 + 透明下划线悬停转品牌线、白卡下拉(hover/键盘聚焦展开)、
|
||||
汉堡 + 右滑抽屉 + 遮罩)与 Footer.astro(surface-inverse 深底四栏栅格 +
|
||||
h3 栏目标题 + 发丝线版权条),像素基准 dist/hesupipe/en/。
|
||||
纯样式层重塑平台 chrome DOM(.pro-utility/.pro-header/.pro-head-inner/
|
||||
.pro-navigation/.pro-nav-children/.pro-mega/.pro-footer 等),数据流与
|
||||
pro.js 钩子全保留。机制复用说明:
|
||||
- 抽屉 = 上游 V8.21.3 四主题统一机制(commercial.css 的 ≤1024px 样式段 +
|
||||
pro.js 通用逻辑按 [data-pro-menu]/#pro-navigation 选择器寻钩子,与主题
|
||||
无关);trade-pro 的 pro.html 本就输出同款类名结构,机制自动可用。
|
||||
- 平台 drawer/tabs 样式段消费 --pro-* 变量且大量 !important 锁定(宽度/
|
||||
定位/内边距/链接色等),本块以 .sf-theme-trade-pro 作用域的变量桥把
|
||||
cms 令牌注入 --pro-*(body 级声明压过 pro-base 的 :root,继承取胜,
|
||||
零 !important 对抗)——抽屉/底部 tabs/下拉卡的配色随桥整体品牌化。
|
||||
- mobile_tabs 为模板驱动(pro.html 渲染块 + commercial.css 767px 样式 +
|
||||
pro.js [data-mobile-open-menu] 开抽屉),10j 给 trade-pro 补上渲染块。
|
||||
级联:本块 0,2,0+ 作用域规则晚于 pro-base/commercial(链接序 theme →
|
||||
blocks → factory → pro-base → pro → commercial)与本文件 Phase 2 tint
|
||||
层;对 commercial 中同特异性晚载规则(如 .pro-end-cta h2、
|
||||
.pro-header-cta:hover)以更高特异性取胜,不用 !important。 */
|
||||
|
||||
/* 变量桥:平台 chrome(含 commercial 抽屉/底部 tabs 的 !important 锁定段)
|
||||
全部经 --pro-* 消费颜色 → cms 令牌直通(含焦点环/激活 tab 色) */
|
||||
.sf-theme-trade-pro{--pro-background:var(--sf-tp-color-surface-default);--pro-surface:var(--sf-tp-color-surface-muted);--pro-border:var(--sf-tp-color-border-subtle);--pro-text:var(--sf-tp-color-text-primary);--pro-muted:var(--sf-tp-color-text-muted);--pro-accent:var(--sf-tp-color-brand-primary);--pro-hero:var(--sf-tp-color-surface-inverse);--pro-radius:var(--sf-tp-radius-sm)}
|
||||
|
||||
/* chrome 容器 = cms .wrap 几何(min(1440px,90vw),无内边距靠宽度收边);
|
||||
页面内容模板已用 .wrap 同款(10a-10h),页眉/页脚自此与正文对齐。
|
||||
620 的 mobile_container_padding 配置项被下方 620 块的 cms 几何覆盖
|
||||
(等价意图:宽度收边),其余 site_design 选项(导航/公告/CTA/页脚/
|
||||
mobile_tabs)不受影响 */
|
||||
.sf-theme-trade-pro .pro-wrap{width:min(1440px,90vw);max-width:none;padding:0;margin-inline:auto}
|
||||
/* mega 例外:面板收在页面容器内(cms 下拉卡语义,非全幅横幅) */
|
||||
.sf-theme-trade-pro .pro-mega.pro-wrap{width:min(100%,var(--sf-tp-container-page-max))}
|
||||
|
||||
/* 公告条(chrome.header.announcement 数据位):cms 无对应物——按 cms 深色
|
||||
版权条语汇收敛为藏青细条(宽字距小字) */
|
||||
.sf-theme-trade-pro .pro-utility{background:var(--sf-tp-color-surface-inverse);color:var(--sf-tp-color-text-inverse-muted);font-size:var(--sf-tp-font-size-xs);letter-spacing:var(--sf-tp-letter-spacing-wide)}
|
||||
.sf-theme-trade-pro .pro-utility .pro-wrap{padding-block:var(--sf-tp-space-2)}
|
||||
|
||||
/* 页眉(Header.astro .site-header):sticky 玻璃拟态,cms 无底边线。
|
||||
body 的 overflow-x:hidden 会禁用后代 sticky(滚动容器劫持)——theme.css
|
||||
的 body.sf-theme-trade-pro(0,1,1) 载于本文件之前,此处同特异性晚载以
|
||||
clip 覆写(支持 clip 的浏览器内 sticky 生效,不支持则回落现状) */
|
||||
body.sf-theme-trade-pro{overflow-x:hidden;overflow-x:clip}
|
||||
.sf-theme-trade-pro .pro-header{position:sticky;top:0;z-index:var(--sf-tp-z-sticky);background:color-mix(in srgb,var(--sf-tp-color-surface-brand-soft) 88%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-block-end:0}
|
||||
.sf-theme-trade-pro .pro-head-inner{min-height:var(--sf-tp-space-16);gap:var(--sf-tp-space-8)}
|
||||
/* Logo(cms .logo-img:高 44px 宽自适应;max-width 兜底防超宽图溢出) */
|
||||
.sf-theme-trade-pro .pro-logo{min-width:0}
|
||||
.sf-theme-trade-pro .pro-brand-logo{height:calc(var(--sf-tp-space-10) + var(--sf-tp-space-1));width:auto;max-width:min(30vw,220px);max-height:none;object-fit:contain}
|
||||
.sf-theme-trade-pro .pro-logo-name{font-size:var(--sf-tp-font-size-lg);font-weight:var(--sf-tp-font-weight-semibold);letter-spacing:var(--sf-tp-letter-spacing-tight)}
|
||||
.sf-theme-trade-pro .pro-logo-name small{color:var(--sf-tp-color-text-muted)}
|
||||
|
||||
/* 导航(cms .nav-links):右对齐(pro-base margin-left:auto 保留)、半粗、
|
||||
拉伸满高、透明下划线悬停转品牌色实线(cms active 态的平台化——静态
|
||||
产物无当前页标记,悬停即反馈);搜索链接(show_search 数据位)并入导航
|
||||
字级,pro-base 的 19px!important 需同强度覆写 */
|
||||
.sf-theme-trade-pro .pro-navigation{gap:var(--sf-tp-space-8);align-self:stretch;align-items:stretch}
|
||||
.sf-theme-trade-pro .pro-navigation>a,.sf-theme-trade-pro .pro-navigation .pro-nav-group>a{display:flex;align-items:center;color:var(--sf-tp-color-text-primary);font-size:var(--sf-tp-font-size-md);font-weight:var(--sf-tp-font-weight-semibold);border-block-end:var(--sf-tp-border-width-medium) solid transparent;transition:color var(--sf-tp-duration-fast) var(--sf-tp-easing-standard),border-color var(--sf-tp-duration-fast) var(--sf-tp-easing-standard)}
|
||||
.sf-theme-trade-pro .pro-navigation>a:hover,.sf-theme-trade-pro .pro-navigation .pro-nav-group>a:hover{color:var(--sf-tp-color-text-link);border-block-end-color:var(--sf-tp-color-border-brand);opacity:1}
|
||||
.sf-theme-trade-pro .pro-search-link{font-size:var(--sf-tp-font-size-sm)!important;color:var(--sf-tp-color-text-secondary);gap:var(--sf-tp-space-1)}
|
||||
|
||||
/* caret(cms .caret ▾ 旋转 180°):disclose 钮去按钮化,随组悬停/展开旋转 */
|
||||
.sf-theme-trade-pro .pro-navigation .pro-nav-disclose{padding:var(--sf-tp-space-2);color:var(--sf-tp-color-text-muted);align-self:center}
|
||||
.sf-theme-trade-pro .pro-navigation .pro-nav-disclose .sf-tp-icon{transition:transform var(--sf-tp-duration-fast) var(--sf-tp-easing-standard)}
|
||||
.sf-theme-trade-pro .pro-navigation .pro-nav-group:hover .pro-nav-disclose .sf-tp-icon,.sf-theme-trade-pro .pro-navigation .pro-nav-disclose[aria-expanded="true"] .sf-tp-icon{transform:rotate(180deg)}
|
||||
|
||||
/* CTA(chrome.header.show_cta 数据位):cms 按钮语汇(蓝底白字、radius 10px、
|
||||
悬浮上抬 + shadow-sm);commercial 的 .pro-header-cta:hover(0,2,0) 晚载
|
||||
——以 0,3,0 取胜 */
|
||||
.sf-theme-trade-pro .pro-header-cta{min-height:calc(var(--sf-tp-button-height-md) - var(--sf-tp-space-1));padding-inline:var(--sf-tp-button-padding-x-sm);font-size:var(--sf-tp-font-size-sm);font-weight:var(--sf-tp-button-font-weight);border-radius:var(--sf-tp-button-border-radius);background:var(--sf-tp-color-surface-brand);border:var(--sf-tp-border-width-hairline) solid var(--sf-tp-color-border-brand);color:var(--sf-tp-color-text-on-brand)}
|
||||
.sf-theme-trade-pro .pro-header-cta:hover{transform:translateY(calc(-1 * var(--sf-tp-space-1)));background:var(--sf-tp-color-brand-primary-strong);box-shadow:var(--sf-tp-shadow-sm);color:var(--sf-tp-color-text-on-brand)}
|
||||
|
||||
/* 下拉卡(cms .sub-menu):白卡 + 发丝边 + radius-sm + shadow-md + 小字链接
|
||||
brand-soft 悬停(叶子项缩进灰字) */
|
||||
.sf-theme-trade-pro .pro-nav-children{min-width:220px;padding:var(--sf-tp-space-2);display:grid;gap:var(--sf-tp-space-1);background:var(--sf-tp-color-surface-default);border:var(--sf-tp-border-width-hairline) solid var(--sf-tp-color-border-subtle);border-radius:var(--sf-tp-radius-sm);box-shadow:var(--sf-tp-shadow-md)}
|
||||
.sf-theme-trade-pro .pro-nav-children a{padding:var(--sf-tp-space-2) var(--sf-tp-space-3);font-size:var(--sf-tp-font-size-sm);font-weight:var(--sf-tp-font-weight-medium);color:var(--sf-tp-color-text-secondary);border-radius:var(--sf-tp-radius-xs);white-space:normal}
|
||||
.sf-theme-trade-pro .pro-nav-children a:hover{background:var(--sf-tp-color-surface-brand-soft);color:var(--sf-tp-color-text-link)}
|
||||
.sf-theme-trade-pro .pro-nav-children .pro-nav-leaf{padding-inline-start:var(--sf-tp-space-6);color:var(--sf-tp-color-text-muted)}
|
||||
|
||||
/* mega 面板(data-pro-mega 钩子保留;cms 无对应物——产品分类条按下拉卡
|
||||
语汇收敛:去下划线列表改 brand-soft 悬停) */
|
||||
.sf-theme-trade-pro .pro-mega{padding:var(--sf-tp-space-5);border:var(--sf-tp-border-width-hairline) solid var(--sf-tp-color-border-subtle);border-radius:0 0 var(--sf-tp-radius-sm) var(--sf-tp-radius-sm);box-shadow:var(--sf-tp-shadow-md)}
|
||||
.sf-theme-trade-pro .pro-mega>span{font-size:var(--sf-tp-font-size-xs);font-weight:var(--sf-tp-font-weight-semibold);letter-spacing:var(--sf-tp-letter-spacing-wide);text-transform:uppercase;color:var(--sf-tp-color-text-muted)}
|
||||
.sf-theme-trade-pro .pro-mega>div a{font-size:var(--sf-tp-font-size-sm);font-weight:var(--sf-tp-font-weight-medium);color:var(--sf-tp-color-text-secondary);border-block-end:0;border-radius:var(--sf-tp-radius-xs);transition:background-color var(--sf-tp-duration-fast) var(--sf-tp-easing-standard),color var(--sf-tp-duration-fast) var(--sf-tp-easing-standard)}
|
||||
.sf-theme-trade-pro .pro-mega>div a:hover{background:var(--sf-tp-color-surface-brand-soft);color:var(--sf-tp-color-text-link)}
|
||||
.sf-theme-trade-pro .pro-mega>div a .sf-tp-icon{color:var(--sf-tp-color-text-muted)}
|
||||
|
||||
/* 汉堡(cms .drawer-toggle 三线形态):44px 触达目标保留(commercial 锁定),
|
||||
☰ 字形以三道 2px 线替代(font-size:0 隐字,纯视觉层;桌面本就 display:none) */
|
||||
.sf-theme-trade-pro .pro-mobile-toggle{font-size:0;color:var(--sf-tp-color-text-primary);border:var(--sf-tp-border-width-hairline) solid var(--sf-tp-color-border-default);border-radius:var(--sf-tp-radius-xs);background:linear-gradient(var(--sf-tp-color-text-primary),var(--sf-tp-color-text-primary)) center top 12px/18px 2px no-repeat,linear-gradient(var(--sf-tp-color-text-primary),var(--sf-tp-color-text-primary)) center/18px 2px no-repeat,linear-gradient(var(--sf-tp-color-text-primary),var(--sf-tp-color-text-primary)) center bottom 12px/18px 2px no-repeat}
|
||||
|
||||
/* 页脚(Footer.astro .site-footer):surface-inverse 深底 + 栏目栅格(cms
|
||||
1.7/0.8/0.9/1.25 四栏 → 平台三栏 DOM 映射为 1.7/0.9/1.25)+ h3 栏目
|
||||
标题(Quick Links 随 footer_nav 数据流)+ 发丝线版权条 */
|
||||
.sf-theme-trade-pro .pro-footer{background:var(--sf-tp-color-surface-inverse);color:var(--sf-tp-color-text-inverse-muted);padding:calc(var(--sf-tp-space-12) + var(--sf-tp-space-4)) 0 var(--sf-tp-space-5)}
|
||||
.sf-theme-trade-pro .pro-footer-top{display:grid;grid-template-columns:1.7fr 0.9fr 1.25fr;gap:calc(var(--sf-tp-space-12) + var(--sf-tp-space-4))}
|
||||
.sf-theme-trade-pro .pro-footer .pro-eyebrow{color:var(--sf-tp-color-text-inverse-muted)}
|
||||
.sf-theme-trade-pro .pro-footer h2{font-size:var(--sf-tp-font-size-xl);font-weight:var(--sf-tp-font-weight-bold);letter-spacing:var(--sf-tp-letter-spacing-tight);color:var(--sf-tp-color-text-inverse);margin-block-end:var(--sf-tp-space-3)}
|
||||
.sf-theme-trade-pro .pro-footer h3{font-size:var(--sf-tp-font-size-md);color:var(--sf-tp-color-text-inverse);margin-block-end:var(--sf-tp-space-3)}
|
||||
.sf-theme-trade-pro .pro-footer p{font-size:var(--sf-tp-font-size-sm);color:var(--sf-tp-color-text-inverse-muted);margin:0 0 var(--sf-tp-space-2)}
|
||||
.sf-theme-trade-pro .pro-footer a{color:var(--sf-tp-color-text-inverse-muted)}
|
||||
.sf-theme-trade-pro .pro-footer a:hover{color:var(--sf-tp-color-text-inverse)}
|
||||
.sf-theme-trade-pro .pro-footer-links ul{display:flex;flex-direction:column;gap:var(--sf-tp-space-1);margin:0;padding:0;list-style:none}
|
||||
.sf-theme-trade-pro .pro-footer-links a{font-size:var(--sf-tp-font-size-sm);padding-block:var(--sf-tp-space-1)}
|
||||
/* cms 页脚链接为纯文字(无箭头)——装饰性 SVG 隐藏,DOM 保留 */
|
||||
.sf-theme-trade-pro .pro-footer-links a .sf-tp-icon{display:none}
|
||||
.sf-theme-trade-pro .pro-footer-bottom{border-block-start:var(--sf-tp-border-width-hairline) solid var(--sf-tp-color-border-inverse);margin-block-start:var(--sf-tp-space-8);padding-block-start:var(--sf-tp-space-5);font-size:var(--sf-tp-font-size-xs);letter-spacing:normal;color:var(--sf-tp-color-neutral-400)}
|
||||
|
||||
/* 结尾 CTA 带(pro.html 全局 pro-end-cta):与 10a sf-tp-cta 同款深色渐变
|
||||
语汇(cms Cta 组件),白底按钮反色 */
|
||||
.sf-theme-trade-pro .pro-end-cta{background:linear-gradient(90deg,color-mix(in srgb,var(--sf-tp-color-surface-inverse) 94%,transparent),color-mix(in srgb,var(--sf-tp-color-brand-primary) 67%,transparent));color:var(--sf-tp-color-text-inverse);padding-block:calc(var(--sf-tp-space-12) + var(--sf-tp-space-4))}
|
||||
.sf-theme-trade-pro .pro-end-cta .pro-eyebrow{color:var(--sf-tp-color-text-inverse-muted)}
|
||||
.sf-theme-trade-pro .pro-end-cta h2{font-size:clamp(30px,4vw,var(--sf-tp-font-size-4xl));color:var(--sf-tp-color-text-inverse);letter-spacing:var(--sf-tp-letter-spacing-tight)}
|
||||
.sf-theme-trade-pro .pro-end-cta p{color:color-mix(in srgb,var(--sf-tp-color-text-inverse) 83%,transparent)}
|
||||
.sf-theme-trade-pro .pro-end-cta .sf-tp-btn{background:var(--sf-tp-color-surface-default);border-color:var(--sf-tp-color-surface-default);color:var(--sf-tp-color-text-brand)}
|
||||
.sf-theme-trade-pro .pro-end-cta .sf-tp-btn:hover{background:var(--sf-tp-color-brand-primary);border-color:var(--sf-tp-color-brand-primary);color:var(--sf-tp-color-text-on-brand)}
|
||||
|
||||
/* 桌面下拉悬停/键盘聚焦展开(cms sub-menu 交互的纯 CSS 复刻)。仅 ≥1025px:
|
||||
≤1024 走抽屉,[hidden]=display:none 的折叠语义由 commercial !important
|
||||
锁定保留。[hidden] 面板改为 opacity/visibility 隐藏后随组悬停浮现;
|
||||
pro.js 点击 disclose 置 hidden=false 仍是持久展开——两通道共存互不破坏 */
|
||||
@media (min-width:1025px){
|
||||
.sf-theme-trade-pro .pro-navigation .pro-nav-children[hidden]{display:grid!important;opacity:0;visibility:hidden;pointer-events:none;transform:translateY(var(--sf-tp-space-1));transition:opacity var(--sf-tp-duration-fast) var(--sf-tp-easing-standard),transform var(--sf-tp-duration-fast) var(--sf-tp-easing-standard),visibility var(--sf-tp-duration-fast)}
|
||||
.sf-theme-trade-pro .pro-navigation .pro-nav-group:hover>.pro-nav-children,.sf-theme-trade-pro .pro-navigation .pro-nav-group:focus-within>.pro-nav-children{opacity:1;visibility:visible;pointer-events:auto;transform:translateY(0)}
|
||||
}
|
||||
|
||||
/* 抽屉/移动端精修(≤1024;形态与尺寸由 commercial !important 锁定段提供——
|
||||
右滑面板 min(88vw,360px)/100dvh/安全区 padding、遮罩、sticky 抽屉头、
|
||||
44px 触达、焦点环与 sf-drawer-extra 均为上游机制)。本块只做 cms 语汇的
|
||||
未锁定属性:cms 头部 ≤1023 的 68px 头高/32px logo、链接悬停 brand-soft、
|
||||
抽屉列表紧凑节奏(gap 归零靠链接自身 padding 分隔) */
|
||||
@media (max-width:1024px){
|
||||
.sf-theme-trade-pro .pro-head-inner{min-height:68px;gap:var(--sf-tp-space-4)}
|
||||
.sf-theme-trade-pro .pro-brand-logo{height:var(--sf-tp-space-8)}
|
||||
.sf-theme-trade-pro .pro-navigation{gap:0}
|
||||
.sf-theme-trade-pro .pro-navigation>a:hover,.sf-theme-trade-pro .pro-navigation .pro-nav-group>a:hover,.sf-theme-trade-pro .pro-navigation .sf-drawer-extra>a:hover{background:var(--sf-tp-color-surface-brand-soft)}
|
||||
.sf-theme-trade-pro .sf-drawer-header{font-size:var(--sf-tp-font-size-md);font-weight:var(--sf-tp-font-weight-semibold);letter-spacing:var(--sf-tp-letter-spacing-normal)}
|
||||
}
|
||||
|
||||
/* chrome 响应式(cms 断点):1280 导航降一档字级防溢出;900 页脚折两栏
|
||||
(联系栏整行,cms Footer 900 同构);620 单栏 + 版权条堆叠 + wrap 收边
|
||||
(cms .wrap 620 = calc(100% - 30px)) */
|
||||
@media (max-width:1280px){
|
||||
.sf-theme-trade-pro .pro-navigation>a,.sf-theme-trade-pro .pro-navigation .pro-nav-group>a{font-size:var(--sf-tp-font-size-sm)}
|
||||
}
|
||||
@media (max-width:900px){
|
||||
.sf-theme-trade-pro .pro-footer-top{grid-template-columns:1.5fr 1fr;gap:var(--sf-tp-space-10)}
|
||||
.sf-theme-trade-pro .pro-footer-top>div:last-child{grid-column:1/-1}
|
||||
}
|
||||
@media (max-width:620px){
|
||||
.sf-theme-trade-pro .pro-wrap{width:calc(100% - 30px)}
|
||||
.sf-theme-trade-pro .pro-footer-top{grid-template-columns:1fr;gap:var(--sf-tp-space-8)}
|
||||
.sf-theme-trade-pro .pro-footer-bottom{display:block}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
|
||||
{% if has_custom and kind!='notfound' %}<main id="main-content" class="sf-canvas pro-custom-content">{{custom_blocks}}</main>{% else %}<main id="main-content" class="sf-canvas pro-custom-content" aria-hidden="true"></main>{% endif %}
|
||||
{% if kind not in ('notfound','search') %}<section class="pro-end-cta"><div class="pro-wrap"><div><span class="pro-eyebrow">LET'S CONNECT</span><h2>Ready to source with confidence?</h2><p>{{site.tagline or 'Send your specifications and get a quotation within one business day.'}}</p></div><a class="sf-tp-btn" href="/contact/">Contact us <span>{{ icon('arrow-right') }}</span></a></div></section>{% endif %}
|
||||
<footer class="sf-site-foot pro-footer"><div class="pro-wrap"><div class="pro-footer-top"><div><span class="pro-eyebrow">TRADE PARTNERSHIP</span><h2>{{chrome.footer.title or site.name}}</h2><p>{{footer_text if (chrome_is_custom or footer_text) else 'Quality products, transparent specifications and dependable worldwide delivery.'}}</p></div><div class="pro-footer-links">{{footer_links(tp_nav if footer_nav else [])}}</div><div><span class="pro-eyebrow">BUSINESS ENQUIRY</span><p>{% if footer_email %}<a href="mailto:{{footer_email}}">{{footer_email}}</a>{% else %}Configure the business email in the admin console.{% endif %}</p></div></div><div class="pro-footer-bottom"><span>© {{year}} {{site.name}}</span><span>ENGINEERED FOR GLOBAL TRADE</span></div></div></footer>
|
||||
<footer class="sf-site-foot pro-footer"><div class="pro-wrap"><div class="pro-footer-top"><div><span class="pro-eyebrow">TRADE PARTNERSHIP</span><h2>{{chrome.footer.title or site.name}}</h2><p>{{footer_text if (chrome_is_custom or footer_text) else 'Quality products, transparent specifications and dependable worldwide delivery.'}}</p></div><div class="pro-footer-links">{% if footer_nav %}<h3>Quick Links</h3>{% endif %}{{footer_links(tp_nav if footer_nav else [])}}</div><div><span class="pro-eyebrow">BUSINESS ENQUIRY</span><p>{% if footer_email %}<a href="mailto:{{footer_email}}">{{footer_email}}</a>{% else %}Configure the business email in the admin console.{% endif %}</p></div></div><div class="pro-footer-bottom"><span>© {{year}} {{site.name}}</span><span>ENGINEERED FOR GLOBAL TRADE</span></div></div></footer>
|
||||
{% if chrome.mobile_tabs['enabled'] and chrome.mobile_tabs['items'] %}<nav class="sf-mobile-tabs" style="--sf-mobile-tab-count:{{ chrome.mobile_tabs['items']|length }}" aria-label="Mobile quick navigation" data-mobile-tabs>
|
||||
{% for tab in chrome.mobile_tabs['items'] %}{% if tab.action == 'menu' %}<button type="button" class="sf-mobile-tab" data-mobile-open-menu aria-label="{{tab.title}}"><span class="sf-tab-icon" aria-hidden="true">{{ mobile_tab_icons[tab.icon] }}</span><span class="sf-tab-label">{{tab.title}}</span></button>{% else %}<a class="sf-mobile-tab" href="{{tab.route}}"{% if tab.route == route %} aria-current="page"{% endif %}><span class="sf-tab-icon" aria-hidden="true">{{ mobile_tab_icons[tab.icon] }}</span><span class="sf-tab-label">{{tab.title}}</span></a>{% endif %}{% endfor %}</nav>{% endif %}
|
||||
<script defer src="assets/site-runtime.js"></script><script defer src="assets/pro.js"></script>
|
||||
<script>
|
||||
/* cms reveal port (Task 10a): progressive enhancement only. Without JS or
|
||||
|
||||
Reference in New Issue
Block a user