Files
cms-SiteForge/themes/trade-pro/pages/brand.html
T
浅念 391d22f1a8 feat: refine trade-pro variant pages (brand/service/faq/search/notfound)
Task 11 (layout port finale): rebuild the five thin variant pages in the
sf-tp design language, informed by upstream industrial/automotive
1.3.0/1.4.0 information architecture.

- brand: page-hero (breadcrumb Home > Brands > name, cover bg, anchor
  button) + related-product card grid (fields.brand == page title) +
  brand profile body only when authored; derived placeholder summary
  excluded, empty brands get an empty state, never fabricated intros.
- service: page-hero + 1.2/0.8 detail grid (article-body typography +
  sticky CTA card with contact/back links); empty body collapses to a
  centered solo column with the CTA card kept.
- faq: page-hero + article-body for the rich-text body (platform has no
  faq content type; sanitize strips details/summary so the typography
  layer carries it, block-driven details coexist via sf-canvas) +
  dark contact-guidance card; empty body swaps to an empty state.
- search: adopts the same platform mechanism as all four upstream
  themes (site-runtime.js ?q= client search over dist-root
  search-index.json); #sf-search/#sf-search-results/#sf-search-count
  hooks and .sf-search-page kept as JS contract, panel and injected
  result cards restyled via scoped cms-token overrides.
- notfound: branded dark-band 404 (icon macro, giant 404 h1, English
  copy, back-home + browse-products buttons); no end-cta.

CSS: Task 11 block appended to pro.css (new sf-tp-* classes only, own
900/620 media queries; shared hero/grid/typography responsive rules
come from existing 10a/10c/10d blocks; search overrides beat
factory.css by specificity without touching hooks).

Tests: test_trade_pro_variant_pages_structure covers brand fixture
(related filtering, empty forms), service/faq body-vs-empty, search
mechanism contract + noindex + index asset, 404 structure, v3 spot
checks (single h1, chrome, SVG arrows) and clean build report.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-26 16:32:15 +08:00

86 lines
4.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{# trade-pro brand detail — 品牌详情精修(Task 11,次要页面收官)。
设计参照(本轮无 cms 源对应):上游 themes/automotive/versions/1.4.0/
pages/brand.html 的信息架构——品牌横幅(面包屑 + 眉题/h1/导语 + 页内
锚点按钮 + 品牌图)→ 车型/产品网格(section heading + "全部"动作位)→
品牌故事(眉题 + h2 + 正文);industrial 1.3.0 为同构薄形态。本模板以
sf-tp 设计语言重组:sf-tp-page-hero(10c 内页 hero:封面底图 + brand-soft
罩层)+ sf-tp-section-head/card-grid(10a 卡片系)+ sf-tp-article-body
排版层双挂(10d)。
数据事实(make_context 对 brand kind):related = fields.brand 与页题
(或 fields.brand 字段)一致的产品/车型集合——品牌页的产品网格只信它;
派生品牌页(builder 从产品 brand 字段派生 /brands/<slug>/)body/cover
恒空、summary 是平台占位串「相关品牌及产品」——导语按 authored 判定
排除占位串(home.html 排除「首页」同款决策),空品牌不虚构介绍:
无产品给数据无关的英文导语 + 空态卡,绝不编造品牌故事。
面包屑 Brands 链接 /brands/(required_sections 恒保证该路由存在);
hero 锚点按钮锚到 #tp-brand-products(构建器豁免 # 锚点,10h 同款)。 #}
{% set tp_brand_summary = page.summary if page.summary and page.summary != '相关品牌及产品' else '' %}
{# 导语优先级:authored 摘要 > 数据派生计数(Jinja 三元与 or 的优先级易误读,
显式分行赋值避免歧义)> 空品牌通用引导 #}
{% if tp_brand_summary %}{% set tp_brand_lead = tp_brand_summary %}
{% elif related %}{% set tp_brand_lead = ('One product from ' if related|length == 1 else related|length ~ ' products from ') ~ page.title ~ ' — specifications and export documentation included.' %}
{% else %}{% set tp_brand_lead = 'Products assigned to this brand in the admin console appear here automatically.' %}{% endif %}
<section class="sf-tp-page-hero sf-tp-brand-hero">
{% if safe_image(page.cover) %}<img class="sf-tp-page-hero-bg" src="{{ safe_image(page.cover) }}" alt="" width="1600" height="900" fetchpriority="high" decoding="async">{% endif %}
<div class="sf-tp-page-hero-overlay" aria-hidden="true"></div>
<div class="wrap">
<nav class="sf-tp-breadcrumb" aria-label="Breadcrumb" data-reveal>
<a href="/">Home</a><a href="/brands/">Brands</a><span class="sf-tp-breadcrumb-current">{{ page.title }}</span>
</nav>
<div class="sf-tp-page-hero-copy" data-reveal style="--reveal-i: 1">
<span class="eyebrow">BRAND PROFILE / PARTNERS</span>
<h1>{{ page.title }}</h1>
<p class="sf-tp-page-hero-lead">{{ tp_brand_lead }}</p>
{% if related %}<a class="sf-tp-btn" href="#tp-brand-products">View brand products {{ icon('arrow-right') }}</a>{% endif %}
</div>
</div>
</section>
{% if related %}
<section class="section section--soft sf-tp-brand-products" id="tp-brand-products">
<div class="wrap">
<div class="sf-tp-section-head" data-reveal>
<div>
<span class="eyebrow">PRODUCT LINE</span>
<h2>{{ page.title }} products</h2>
</div>
<a class="sf-tp-more" href="/products/">All products {{ icon('arrow-right', cls='sf-tp-icon--sm') }}</a>
</div>
<div class="sf-tp-card-grid">
{% for tp_item in related[:8] %}
<article class="sf-tp-card" data-reveal style="--reveal-i: {{ loop.index0 % 4 }}">
<div class="sf-tp-card-media">{{ cover(tp_item, 'product') }}</div>
<div class="sf-tp-card-body">
<h3>{{ tp_item.title }}</h3>
<p>{{ (tp_item.summary or 'Published specifications available on request.').split('.')[0] }}.</p>
<a class="sf-tp-more" href="{{ safe_href(tp_item.route) }}">View details {{ icon('arrow-right', cls='sf-tp-icon--sm') }}</a>
</div>
</article>
{% endfor %}
</div>
</div>
</section>
{% else %}
<section class="section sf-tp-brand-empty-section">
<div class="wrap">
<div class="sf-tp-listing-empty" data-reveal>
<h2>No products listed under this brand yet</h2>
<p>Products assigned to this brand appear here automatically once published.</p>
<a class="sf-tp-btn sf-tp-btn--ghost" href="/products/">Browse all products {{ icon('arrow-right') }}</a>
</div>
</div>
</section>
{% endif %}
{% if content %}
<section class="section sf-tp-brand-profile">
<div class="wrap">
<div class="sf-tp-section-head" data-reveal>
<div>
<span class="eyebrow">BRAND PROFILE</span>
<h2>About {{ page.title }}</h2>
</div>
</div>
<div class="sf-tp-brand-body sf-tp-article-body" data-reveal style="--reveal-i: 1">{{ body_copy(content, page) }}</div>
</div>
</section>
{% endif %}