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
+11 -2
View File
@@ -8,7 +8,7 @@ from bs4 import BeautifulSoup
from test_factory_v2 import factory, site, zip_theme
from app.core import THEMES
def test_four_complete_theme_families_have_distinct_markup(factory):
def test_complete_theme_families_have_distinct_markup(factory):
main=[]
for theme in THEMES:
for page_type in ('home','about','contact','listing','product','article','brand','standard','notfound'):
@@ -23,7 +23,16 @@ def test_four_complete_theme_families_have_distinct_markup(factory):
if page_type=='listing':assert soup.select_one('[data-pro-filter-search]')
if page_type=='article':assert soup.select_one('.pro-article-hero')
main.append(factory.get(f'/api/factory/themes/{theme}/preview?page_type=home').text)
assert len(set(re.findall(r'class="pro-home-hero [^"]+"',s)[0] for s in main))==4
# Every theme must carry its own hero markup signature. The legacy families
# share the pro-home-hero base class with a per-theme variant; trade-pro
# ports the cms design with its own sf-tp-hero class. Five themes, five
# distinct hero class strings ("4" was the pre-trade-pro coincidence).
def hero_classes(html):
soup=BeautifulSoup(html,'html.parser')
el=soup.find('section',class_=re.compile(r'^(pro-home-hero|sf-tp-hero)$'))
assert el is not None,'home hero section not found'
return ' '.join(el.get('class',[]))
assert len(set(hero_classes(s) for s in main))==5
def test_theme_build_derived_category_brand_search_and_seo(factory):