Files
cms-SiteForge/themes/trade-pro/pro.html
T
浅念 a63548c645 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>
2026-10-23 17:53:14 +08:00

45 lines
8.5 KiB
HTML

<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><base href="/"><title>{{ seo_title }}</title><meta name="description" content="{{ seo_description }}"><meta property="og:type" content="{{ 'article' if kind in ('article','case') else 'website' }}"><meta property="og:title" content="{{ seo_title }}"><meta property="og:description" content="{{ seo_description }}">{% if canonical %}<meta property="og:url" content="{{canonical}}">{% endif %}<meta name="theme-color" content="{{theme_color}}"><meta name="robots" content="{{ 'noindex,follow' if noindex else page.get('robots','index,follow') }}">{% if canonical and not noindex %}<link rel="canonical" href="{{ canonical }}">{% endif %}<link rel="stylesheet" href="assets/theme.css"><link rel="stylesheet" href="assets/blocks.css"><link rel="stylesheet" href="assets/factory.css"><link rel="stylesheet" href="assets/pro-base.css"><link rel="stylesheet" href="assets/pro.css"><link rel="stylesheet" href="assets/commercial.css">{% if theme_overrides %}<link rel="stylesheet" href="assets/theme-overrides.css">{% endif %}<link rel="stylesheet" href="assets/custom.css"></head><body class="sf-theme-trade-pro pro-trade-pro"><a class="pro-skip" href="#main-content">Skip to content</a>
{% from '_professional_components.html' import card,cover,grid,section_title,filters,footer_links,body_copy,icon with context %}
{# English fallback navigation (skeleton): the platform always fills nav with
Chinese defaults (首页/产品/品牌/新闻/案例/关于/联系) unless the site configured
its own navigation or published a site design. Detect "unconfigured" by
comparing the nav href set against the platform default href set (identical
for the make_context default and the bootstrap navigation), then render the
English fallback list. Routes are limited to sections every build
guarantees, so no fallback link can become a broken link. #}
{% set tp_default_hrefs=['/','/about/','/brands/','/cases/','/contact/','/news/','/products/'] %}
{% set tp_needs_en_nav=(not chrome_is_custom) and (nav|map(attribute='route')|list|sort==tp_default_hrefs) %}
{# Known false-positive window: the legacy nav editor can save settings.nav with
custom titles but no published site design (chrome_is_custom stays false). If
such a nav happens to keep exactly the default 7 hrefs, its titles are
overridden by the English fallback below. Mitigated (not eliminated) by
keeping all 7 default sections so only the labels change, never coverage. #}
{% set tp_nav=[{'title':'Home','route':'/'},{'title':'Products','route':'/products/'},{'title':'Brands','route':'/brands/'},{'title':'News','route':'/news/'},{'title':'Cases','route':'/cases/'},{'title':'About','route':'/about/'},{'title':'Contact','route':'/contact/'}] if tp_needs_en_nav else nav %}
{% if chrome_is_custom %}<div class="pro-utility"{% if not chrome.header.show_announcement %} hidden{% endif %}><div class="pro-wrap"><span>{{ chrome.header.announcement }}</span><span>{{ site.tagline or site.name }}</span></div></div>{% else %}<div class="pro-utility"><div class="pro-wrap"><span>GLOBAL SOURCING · QUALITY · DELIVERY</span><span>INTERNATIONAL B2B SOLUTIONS</span></div></div>{% endif %}
<header class="sf-site-head pro-header"><div class="pro-wrap pro-head-inner"><a class="pro-logo" href="/" aria-label="{{site.name}} home">{% if safe_image(site.logo) %}<img class="pro-brand-logo" src="{{safe_image(site.logo)}}" alt="{{site.name}} Logo">{% else %}<span class="pro-logo-symbol">▣</span>{% endif %}<span class="pro-logo-name">{{site.name}}<small> GLOBAL B2B</small></span></a><button class="pro-mobile-toggle" type="button" data-pro-menu aria-controls="pro-navigation" aria-expanded="false" aria-label="Open navigation">☰</button><nav id="pro-navigation" class="pro-navigation" aria-label="Main navigation">{% for link in tp_nav %}{% if link.children %}<div class="pro-nav-group"><a href="{{link.route}}">{{link.title}}</a><button type="button" class="pro-nav-disclose" aria-expanded="false" aria-label="Expand {{link.title}} submenu">{{ icon('chevron-down', cls='sf-tp-icon--sm') }}</button><div class="pro-nav-children" hidden>{% for child in link.children %}<a href="{{child.route}}">{{child.title}}</a>{% for leaf in child.children %}<a class="pro-nav-leaf" href="{{leaf.route}}">— {{leaf.title}}</a>{% endfor %}{% endfor %}</div></div>{% else %}<a href="{{link.route}}">{{link.title}}</a>{% endif %}{% endfor %}{% if chrome.header.show_search %}<a class="pro-search-link" href="/search/">Search {{ icon('search', cls='sf-tp-icon--sm') }}</a>{% endif %}</nav><button class="pro-catalog-toggle" type="button" data-pro-catalog aria-controls="pro-mega-content" aria-expanded="false">Products {{ icon('chevron-down', cls='sf-tp-icon--sm') }}</button>{% if chrome_is_custom %}{% if chrome.header.show_cta %}<a href="{{chrome.header.cta_href}}" class="sf-tp-btn pro-header-cta">{{chrome.header.cta_text}} <span>{{ icon('arrow-right') }}</span></a>{% endif %}{% else %}<a href="/contact/" class="sf-tp-btn pro-header-cta">Request a Quote <span>{{ icon('arrow-right') }}</span></a>{% endif %}</div>{% if categories %}<div class="pro-mega pro-wrap" data-pro-mega id="pro-mega-content" hidden><span>Product categories</span><div>{% for cat in categories[:12] %}<a href="{{cat.url}}">{{cat.name}} {{ icon('arrow-right') }}</a>{% endfor %}</div></div>{% endif %}</header>
{% if not designer_replace %}{% include 'trade-pro/pages/' ~ kind ~ '.html' %}{% endif %}
{% 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">{% 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
IntersectionObserver the html.js class is never set, so [data-reveal]
content (theme.css) stays visible; reduced-motion is exempted in CSS. */
(function () {
if (!('IntersectionObserver' in window)) return;
document.documentElement.classList.add('js');
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
entry.target.classList.add('is-visible');
io.unobserve(entry.target);
});
}, { rootMargin: '0px 0px -8% 0px' });
document.querySelectorAll('[data-reveal]').forEach(function (el) { io.observe(el); });
})();
</script></body></html>