feat: port cms home layout into trade-pro (hero/features/cards/cta + reveal JS)

Task 10a (Phase 3 first layout task). home.html now mirrors the cms
astro-site-runtime homepage section sequence: full-bleed hero (single
active slide; the platform has no multi-slide config, so the carousel
dots/JS are not ported), trustbar, two-col about with floating stats,
soft products band (4-card grid), dark applications band (2x2 case
tiles), advantages feature grid, soft news band (3-card grid), and the
closing cta band. Data maps to SiteForge context (page.cover/summary,
showcase, cases, stories, category/brand counts); empty sections hide
instead of rendering shells. All new component classes are sf-tp-*
prefixed and compose on the Task 8 base utilities in theme.css
(.wrap/.section/.eyebrow/.icon-circle/.sf-tp-btn).

CSS: the five cms components' scoped styles (PageHero, FeatureGrid,
CardGrid, Cta, SectionHead + the inline homepage bands) are extracted
into pro.css as the sf-tp-* component layer, tokens rewritten to
--sf-tp-*, breakpoints 900/767/620 preserved. theme.css accordingly
drops the superseded generic .cards/.card/.feature mobile baselines
and the .pro-home-hero em bridge, and retargets the dark-surface ghost
button hook from .cta-band to .sf-tp-cta.

Reveal: cms uses data-reveal everywhere; a ~15-line inline script at
the pro.html tail adds html.js + an IntersectionObserver that toggles
is-visible (the reduced-motion/no-JS exemptions already live in
theme.css from Task 8).

Tests: test_theme_registry gains test_trade_pro_home_layout_structure
(preview-render contract: sf-tp-hero, card grid, feature grid, cta,
no pro-industrial-hero residue, reveal script). The distinct-markup
tripwire in test_theme_pro_v3 is legitimately updated 4 -> 5 (renamed
to drop the stale "four"): with trade-pro now carrying its own
sf-tp-hero class, five themes yield five distinct hero signatures,
which is what the test always meant to enforce.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-23 09:23:30 +08:00
parent 1dd510285a
commit c318829ff3
6 changed files with 397 additions and 84 deletions
+18 -1
View File
@@ -22,4 +22,21 @@
{% 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>
<script defer src="assets/site-runtime.js"></script><script defer src="assets/pro.js"></script></body></html>
<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>