Files
cms-SiteForge/themes/trade-pro/_professional_components.html
T
浅念 1dd510285a polish: icon sizing hardened for cross-browser (review I-1/M2/M3/M4)
- .sf-tp-icon CSS width/height (Safari doesn't support var() in SVG
  presentation attributes); --sm variant class replaces size= call sites
- search icon centered via translateY, decoupled from input height
- bare-arrow test: full charset (→←↗↘⌕▾) + line-level › whitelist
- new test: site.html tp_arrow must stay in sync with macro library path

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-23 09:00:50 +08:00

44 lines
5.1 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 macro library: passthrough copy of the shared
themes/_professional_components.html with English strings, plus the
trade-pro inline SVG icon set (Task 9). Icons are decorative
server-side inlines: no JS dependency, currentColor strokes. #}
{# ── SVG icon set (Task 9) ──────────────────────────────────────────────
Style contract: 24×24 viewBox, stroke=currentColor, stroke-width 1.5,
fill none, round caps/joins — the same modern line language as the
ported cms design. Every glyph is ONE <path> (subpaths allowed) so the
d attribute survives autoescaping without |safe. Unknown names fall
back to arrow-right instead of raising (page code typos must never
take a build down). #}
{% set _tp_icon_paths={
'arrow-right':'M5 12h14M12 5l7 7-7 7',
'arrow-left':'M19 12H5M12 19l-7-7 7-7',
'chevron-down':'M6 9l6 6 6-6',
'check':'M20 6 9 17 4 12',
'phone':'M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z',
'mail':'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2Zm18 2-10 7L2 6',
'location':'M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0ZM12 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z',
'download':'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3',
'search':'M11 3a8 8 0 1 0 0 16 8 8 0 0 0 0-16ZM21 21l-4.35-4.35',
'quote':'M8 3a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2 1 1 0 0 0-1 1v1a1 1 0 0 0 1 1 2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2 1 1 0 0 0 1-1v-1a1 1 0 0 0-1-1 2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4ZM16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a1 1 0 0 1-1 1 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2 1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1 2 2 0 0 0 2-2V5a2 2 0 0 0-2-2h-4Z'
} %}
{% macro icon(name, size=None, cls='') -%}
{%- set _d=_tp_icon_paths.get(name) or _tp_icon_paths['arrow-right'] -%}
<svg class="sf-tp-icon{% if cls %} {{ cls }}{% endif %}" width="{{ size if size else 'var(--sf-tp-icon-size-md, 20px)' }}" height="{{ size if size else 'var(--sf-tp-icon-size-md, 20px)' }}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="{{ _d }}"/></svg>
{%- endmacro %}
{% macro cover(item, kind='product') %}
{% if item.cover %}<img class="pro-photo" src="{{ safe_image(item.cover) }}" alt="{{ item.title }}" loading="lazy">
{% else %}<div class="pro-art pro-art-{{ kind }}"><span class="pro-art-ring"></span><span class="pro-art-center">{{ (item.title or 'SF')[:2] }}</span><span class="pro-art-label">{{ kind|upper }}</span></div>{% endif %}
{% endmacro %}
{% macro card(item, kind='product', position=0) %}
<a class="pro-card pro-card-{{ kind }}" href="{{ safe_href(item.route) }}" data-category="{{ fields_of(item).get('category','') }}" data-brand="{{ fields_of(item).get('brand','') }}">
<div class="pro-card-cover">{{ cover(item,kind) }}</div>
<div class="pro-card-body"><div class="pro-card-meta"><span>{{ fields_of(item).get('brand') or fields_of(item).get('category') or {'product':'PRODUCT','vehicle':'VEHICLE','post':'NEWS','case':'CASE','brand':'BRAND','service':'SERVICE'}.get(item.type,'CONTENT') }}</span><span>{{ '%02d'|format(position+1) }}</span></div>
<h3>{{ item.title }}</h3><p>{{ (item.summary or '')[:128] }}</p><span class="pro-card-link">View details {{ icon('arrow-right') }}</span></div>
</a>
{% endmacro %}
{% macro grid(items,kind='product') %}<div class="pro-grid pro-grid-{{ kind }}">{% for item in items %}{{ card(item,kind,loop.index0) }}{% else %}<div class="pro-empty">No published content in this section yet.</div>{% endfor %}</div>{% endmacro %}
{% macro section_title(eyebrow,heading,href='') %}<div class="pro-section-head"><div><span class="pro-eyebrow">{{ eyebrow }}</span><h2>{{ heading }}</h2></div>{% if href %}<a class="pro-text-link" href="{{ href }}">Browse all {{ icon('arrow-right') }}</a>{% endif %}</div>{% endmacro %}
{% macro filters() %}<div class="pro-filter-panel"><label><span>Search</span><span class="pro-filter-search-box">{{ icon('search') }}<input type="search" data-pro-filter-search placeholder="Filter by name or content"></span></label><label><span>Brand</span><select data-pro-filter-brand><option value="">All brands</option></select></label><label><span>Category</span><select data-pro-filter-category><option value="">All categories</option></select></label><span class="pro-filter-count" data-pro-filter-count role="status"></span></div>{% endmacro %}
{% macro footer_links(nav) %}<ul>{% for item in nav[:7] %}<li><a href="{{item.route}}">{{item.title}} {{ icon('arrow-right', cls='sf-tp-icon--sm') }}</a></li>{% endfor %}</ul>{% endmacro %}
{% macro body_copy(content,page) %}{% if content %}<div class="pro-rich">{{ content }}</div>{% elif page.summary %}<p class="pro-lead">{{ page.summary }}</p>{% else %}<p class="pro-muted">Complete this page's content in the admin console.</p>{% endif %}{% endmacro %}