From 22b614118ba1a7300218b999bbdf5a6c77ca84ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Fri, 23 Oct 2026 08:35:52 +0800 Subject: [PATCH] feat: SVG icon macro library replaces bare text arrows in trade-pro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task 9 (new design, not a cms port): _professional_components.html gains an icon(name, size, cls) macro — 10 inline line-style glyphs (arrow-right, arrow-left, chevron-down, check, phone, mail, location, download, search, quote), 24x24 viewBox, stroke=currentColor, stroke-width 1.5, round caps/joins, decorative aria-hidden + focusable=false, default size var(--sf-tp-icon-size-md, 20px), unknown names fall back to arrow-right. Every glyph is a single so the d attribute needs no |safe. All bare text arrows (->, <-, ↗, ▾, ⌕) in the macro library, pro.html, site.html and pages/* are replaced with macro calls (site.html keeps a self-contained copy of the arrow glyph because its rendering envs cannot import the macro library); the filters macro search box gains an inline search icon; footer links gain small arrows. theme.css adds .sf-tp-icon alignment, in-button icon spacing, and search-box icon positioning. Co-Authored-By: Claude Code --- tests/test_theme_registry.py | 31 ++++++++++++++ .../trade-pro/_professional_components.html | 38 +++++++++++++---- themes/trade-pro/pages/article.html | 2 +- themes/trade-pro/pages/case.html | 2 +- themes/trade-pro/pages/category.html | 2 +- themes/trade-pro/pages/contact.html | 4 +- themes/trade-pro/pages/home.html | 6 +-- themes/trade-pro/pages/listing.html | 2 +- themes/trade-pro/pages/notfound.html | 2 +- themes/trade-pro/pages/product.html | 2 +- themes/trade-pro/pro.html | 6 +-- themes/trade-pro/site.html | 13 ++++-- themes/trade-pro/theme.css | 41 +++++++++++++++++++ 13 files changed, 127 insertions(+), 24 deletions(-) diff --git a/tests/test_theme_registry.py b/tests/test_theme_registry.py index efa9305..59ddff3 100644 --- a/tests/test_theme_registry.py +++ b/tests/test_theme_registry.py @@ -177,6 +177,37 @@ def test_theme_color_map_covers_trade_pro(): {'title': 'Home', 'route': '/'}, [], mode='home') assert ' (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'] -%} + +{%- endmacro %} {% macro cover(item, kind='product') %} {% if item.cover %}{{ item.title }} {% else %}
{{ (item.title or 'SF')[:2] }}{{ kind|upper }}
{% endif %} @@ -9,11 +33,11 @@
{{ cover(item,kind) }}
{{ 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') }}{{ '%02d'|format(position+1) }}
-

{{ item.title }}

{{ (item.summary or '')[:128] }}

View details
+

{{ item.title }}

{{ (item.summary or '')[:128] }}

View details {{ icon('arrow-right') }}
{% endmacro %} {% macro grid(items,kind='product') %}
{% for item in items %}{{ card(item,kind,loop.index0) }}{% else %}
No published content in this section yet.
{% endfor %}
{% endmacro %} -{% macro section_title(eyebrow,heading,href='') %}
{{ eyebrow }}

{{ heading }}

{% if href %}Browse all ↗{% endif %}
{% endmacro %} -{% macro filters() %}
{% endmacro %} -{% macro footer_links(nav) %}{% endmacro %} +{% macro section_title(eyebrow,heading,href='') %}
{{ eyebrow }}

{{ heading }}

{% if href %}Browse all {{ icon('arrow-right') }}{% endif %}
{% endmacro %} +{% macro filters() %}
{% endmacro %} +{% macro footer_links(nav) %}{% endmacro %} {% macro body_copy(content,page) %}{% if content %}
{{ content }}
{% elif page.summary %}

{{ page.summary }}

{% else %}

Complete this page's content in the admin console.

{% endif %}{% endmacro %} diff --git a/themes/trade-pro/pages/article.html b/themes/trade-pro/pages/article.html index dfca60f..6983f2d 100644 --- a/themes/trade-pro/pages/article.html +++ b/themes/trade-pro/pages/article.html @@ -1 +1 @@ -
LATEST NEWS / INSIGHTS

{{page.title}}

{{page.summary}}

{% if fields.get('publish_date') %}{% endif %}
+
LATEST NEWS / INSIGHTS

{{page.title}}

{{page.summary}}

{% if fields.get('publish_date') %}{% endif %}
diff --git a/themes/trade-pro/pages/case.html b/themes/trade-pro/pages/case.html index 7c16d86..8ae8407 100644 --- a/themes/trade-pro/pages/case.html +++ b/themes/trade-pro/pages/case.html @@ -1 +1 @@ -
SELECTED WORK / PROJECT

{{page.title}}

{{page.summary}}

{% if page.cover %}{{page.title}}{% endif %}
{% if fields.get('location') %}Location
{{fields.get('location')}}
{% endif %}{% if fields.get('industry') %}Industry
{{fields.get('industry')}}
{% endif %}
{{body_copy(content,page)}}Browse more projects →
+
SELECTED WORK / PROJECT

{{page.title}}

{{page.summary}}

{% if page.cover %}{{page.title}}{% endif %}
{% if fields.get('location') %}Location
{{fields.get('location')}}
{% endif %}{% if fields.get('industry') %}Industry
{{fields.get('industry')}}
{% endif %}
{{body_copy(content,page)}}Browse more projects {{ icon('arrow-right') }}
diff --git a/themes/trade-pro/pages/category.html b/themes/trade-pro/pages/category.html index 8b2ea73..57e1a27 100644 --- a/themes/trade-pro/pages/category.html +++ b/themes/trade-pro/pages/category.html @@ -1,2 +1,2 @@
DISCOVER / {{'CATEGORY' if kind=='category' else 'COLLECTION'}}

{{page.title}}

{{page.summary or 'Browse all published content in this section.'}}

Home/{{page.title}}
-
{{ filters() }}
{{grid(listing,'catalog')}}
{% if pagination and pagination.pages>1 %}{% endif %}
+
{{ filters() }}
{{grid(listing,'catalog')}}
{% if pagination and pagination.pages>1 %}{% endif %}
diff --git a/themes/trade-pro/pages/contact.html b/themes/trade-pro/pages/contact.html index b4ef08b..07cf572 100644 --- a/themes/trade-pro/pages/contact.html +++ b/themes/trade-pro/pages/contact.html @@ -1,6 +1,6 @@
GET IN TOUCH

{{page.title}}

{{page.summary}}

-
CONNECT / CONTACT

Let's start the conversation.

{% if footer_email %}{{footer_email}} ↗{% else %}

Set the official business email in the site admin console first.

{% endif %}
{{body_copy(content,page)}}{% if page.cover %}{{page.title}}{% endif %}
+
CONNECT / CONTACT

Let's start the conversation.

{% if footer_email %}{{footer_email}} {{ icon('arrow-right') }}{% else %}

Set the official business email in the site admin console first.

{% endif %}
{{body_copy(content,page)}}{% if page.cover %}{{page.title}}{% endif %}
{% if form_endpoint %}
CONTACT FORM

Send a business enquiry

Submissions go directly to the external form service configured by the site administrator.

-

Form submissions are handled by the external service; review its privacy and spam-filtering settings.

+

Form submissions are handled by the external service; review its privacy and spam-filtering settings.

{% endif %} diff --git a/themes/trade-pro/pages/home.html b/themes/trade-pro/pages/home.html index dc522a6..b44299c 100644 --- a/themes/trade-pro/pages/home.html +++ b/themes/trade-pro/pages/home.html @@ -2,7 +2,7 @@ industrial on purpose: the 4-family distinct-markup tripwire in test_theme_pro_v3 counts distinct pro-home-hero class strings, and the Phase 3 visual port will give trade-pro its own hero variant. --> -
GLOBAL SOURCING · MADE RELIABLE / 001

{{page.title if page.title not in (site.name,'首页','Home') else site.name}} BUILT FOR GLOBAL TRADE.

{{page.summary or site.tagline or 'Reliable products, transparent specifications and responsive export service.'}}

{% if page.cover %}{{page.title}}{% else %}
EXPORT / B2B
{% endif %}
-{% if categories %}
{{section_title('PRODUCT CATEGORIES','Shop by category','/products/')}}
{% for cat in categories[:6] %}0{{loop.index}}{{cat.name}}↗{% endfor %}
{% endif %} +
GLOBAL SOURCING · MADE RELIABLE / 001

{{page.title if page.title not in (site.name,'首页','Home') else site.name}} BUILT FOR GLOBAL TRADE.

{{page.summary or site.tagline or 'Reliable products, transparent specifications and responsive export service.'}}

{% if page.cover %}{{page.title}}{% else %}
EXPORT / B2B
{% endif %}
+{% if categories %}
{{section_title('PRODUCT CATEGORIES','Shop by category','/products/')}}
{% for cat in categories[:6] %}0{{loop.index}}{{cat.name}}{{ icon('arrow-right') }}{% endfor %}
{% endif %} -
SERVICE & SUPPORT

From inquiry to delivery.

{{site.tagline or 'Product specifications, quality control and export documentation in one place.'}}

About our company ↗
+
SERVICE & SUPPORT

From inquiry to delivery.

{{site.tagline or 'Product specifications, quality control and export documentation in one place.'}}

About our company {{ icon('arrow-right') }}
diff --git a/themes/trade-pro/pages/listing.html b/themes/trade-pro/pages/listing.html index 8b2ea73..57e1a27 100644 --- a/themes/trade-pro/pages/listing.html +++ b/themes/trade-pro/pages/listing.html @@ -1,2 +1,2 @@
DISCOVER / {{'CATEGORY' if kind=='category' else 'COLLECTION'}}

{{page.title}}

{{page.summary or 'Browse all published content in this section.'}}

Home/{{page.title}}
-
{{ filters() }}
{{grid(listing,'catalog')}}
{% if pagination and pagination.pages>1 %}{% endif %}
+
{{ filters() }}
{{grid(listing,'catalog')}}
{% if pagination and pagination.pages>1 %}{% endif %}
diff --git a/themes/trade-pro/pages/notfound.html b/themes/trade-pro/pages/notfound.html index abf2674..cf49837 100644 --- a/themes/trade-pro/pages/notfound.html +++ b/themes/trade-pro/pages/notfound.html @@ -1 +1 @@ -
ERROR / 404

404

The page you are looking for does not exist or has been moved.

Back to home ↗
+
ERROR / 404

404

The page you are looking for does not exist or has been moved.

Back to home {{ icon('arrow-right') }}
diff --git a/themes/trade-pro/pages/product.html b/themes/trade-pro/pages/product.html index d8c451a..d490316 100644 --- a/themes/trade-pro/pages/product.html +++ b/themes/trade-pro/pages/product.html @@ -1,3 +1,3 @@
Home/Products/{{page.title}}
-
PRODUCT DETAILS / {{fields.get('brand') or 'CATALOGUE'}}

{{page.title}}

{{page.summary or 'Detailed product information and specifications.'}}

{% if fields.get('brand') %}

Brand · {{fields.get('brand')}}

{% endif %}Inquire about this product ↗
+
PRODUCT DETAILS / {{fields.get('brand') or 'CATALOGUE'}}

{{page.title}}

{{page.summary or 'Detailed product information and specifications.'}}

{% if fields.get('brand') %}

Brand · {{fields.get('brand')}}

{% endif %}Inquire about this product {{ icon('arrow-right') }}
OVERVIEW

Product overview

{{body_copy(content,page)}}
SPECIFICATIONS

Specifications

{% for row in specs %}
{{row.label}}{{row.value}}
{% else %}

Specifications have not been filled in yet.

{% endfor %}
diff --git a/themes/trade-pro/pro.html b/themes/trade-pro/pro.html index dc63541..a8e6081 100644 --- a/themes/trade-pro/pro.html +++ b/themes/trade-pro/pro.html @@ -1,5 +1,5 @@ {{ seo_title }}{% if canonical %}{% endif %}{% if canonical and not noindex %}{% endif %}{% if theme_overrides %}{% endif %}Skip to content -{% from '_professional_components.html' import card,cover,grid,section_title,filters,footer_links,body_copy with context %} +{% 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 @@ -16,10 +16,10 @@ 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 %}
{{ chrome.header.announcement }}{{ site.tagline or site.name }}
{% else %}
GLOBAL SOURCING · QUALITY · DELIVERYINTERNATIONAL B2B SOLUTIONS
{% endif %} -
{% if chrome_is_custom %}{% if chrome.header.show_cta %}{{chrome.header.cta_text}} ↗{% endif %}{% else %}Request a Quote ↗{% endif %}
{% if categories %}{% endif %}
+
{% if chrome_is_custom %}{% if chrome.header.show_cta %}{{chrome.header.cta_text}} {{ icon('arrow-right') }}{% endif %}{% else %}Request a Quote {{ icon('arrow-right') }}{% endif %}
{% if categories %}{% endif %}
{% if not designer_replace %}{% include 'trade-pro/pages/' ~ kind ~ '.html' %}{% endif %} {% if has_custom and kind!='notfound' %}
{{custom_blocks}}
{% else %}
{% endif %} -{% if kind not in ('notfound','search') %}
LET'S CONNECT

Ready to source with confidence?

{{site.tagline or 'Send your specifications and get a quotation within one business day.'}}

Contact us ↗
{% endif %} +{% if kind not in ('notfound','search') %}
LET'S CONNECT

Ready to source with confidence?

{{site.tagline or 'Send your specifications and get a quotation within one business day.'}}

Contact us {{ icon('arrow-right') }}
{% endif %}
diff --git a/themes/trade-pro/site.html b/themes/trade-pro/site.html index 630bab5..48ba525 100644 --- a/themes/trade-pro/site.html +++ b/themes/trade-pro/site.html @@ -1,12 +1,19 @@ {{seo_title}}{% if canonical %}{% endif %} +{# site.html renders in environments where the trade-pro macro library cannot + be imported (theme-package previews load only the package dir), so its arrow + is a self-contained copy of the icon macro's arrow-right glyph. Same style + contract: 24×24 viewBox, currentColor stroke, decorative aria-hidden. #} +{% macro tp_arrow() -%} + +{%- endmacro %}
ENGINEERING · MANUFACTURING · SUPPLYGLOBAL INDUSTRIAL SOLUTIONS
-
▰{{site.name}}INDUSTRIAL SOLUTIONSREQUEST A QUOTE ↗
-{% if blueprint_mode %}{% elif mode=='home' %}
STRONGER CONNECTIONS. SMARTER SOLUTIONS.

{{page.title if page.title!=site.name else 'BUILT TO LAST.'}}

{{page.summary or site.tagline or 'Industrial excellence, quality products and dependable worldwide delivery.'}}

EXPLORE PRODUCTS ↗
01 / MATERIALS02 / ENGINEERING03 / SERVICE
{% if page.cover %}{{page.title}}{% else %}
INDUSTRY
IN MOTION
{% endif %}
+
▰{{site.name}}INDUSTRIAL SOLUTIONSREQUEST A QUOTE {{ tp_arrow() }}
+{% if blueprint_mode %}{% elif mode=='home' %}
STRONGER CONNECTIONS. SMARTER SOLUTIONS.

{{page.title if page.title!=site.name else 'BUILT TO LAST.'}}

{{page.summary or site.tagline or 'Industrial excellence, quality products and dependable worldwide delivery.'}}

EXPLORE PRODUCTS {{ tp_arrow() }}
01 / MATERIALS02 / ENGINEERING03 / SERVICE
{% if page.cover %}{{page.title}}{% else %}
INDUSTRY
IN MOTION
{% endif %}
{% elif mode=='notfound' %}
ERROR

404 — Page not found

{% else %}
{{type_names.get(page.type,'COMPANY')|upper}} / {{'PRODUCT CATALOG' if mode=='listing' else 'OUR EXPERTISE'}}

{{page.title}}

{{page.summary}}

{% endif %} {% if blueprint_mode %}
{{blocks}}
{% else %}
{% if mode=='home' %}
01PRODUCT ENGINEERING
02GLOBAL SOURCING
03QUALITY ASSURANCE
{% endif %} {% if mode in ['home','detail'] and (content or blocks) %}
{{blocks}}{{content}}
{% endif %} {% if mode=='detail' and page.cover %}
{{page.title}}
{% endif %} {% set cards=listing_items if mode=='listing' else entries|selectattr('type','equalto','product')|list if mode=='home' else [] %} -{% if cards %}
OUR PORTFOLIO

{{'Product Catalogue' if mode=='listing' else 'Explore Our Products'}}

{% if mode=='home' %}ALL PRODUCTS ↗{% endif %}
{% endif %}
+{% if cards %}
OUR PORTFOLIO

{{'Product Catalogue' if mode=='listing' else 'Explore Our Products'}}

{% if mode=='home' %}ALL PRODUCTS {{ tp_arrow() }}{% endif %}
{% endif %} {% endif %}
© {{site.name}}DESIGNED TO DELIVER. BUILT WITH SITEFORGE.
diff --git a/themes/trade-pro/theme.css b/themes/trade-pro/theme.css index 373dc39..d2631a4 100644 --- a/themes/trade-pro/theme.css +++ b/themes/trade-pro/theme.css @@ -585,6 +585,47 @@ body.sf-theme-trade-pro { margin: 0; } +/* ── 图标(Task 9 SVG 宏库:内联线条图标的对齐与间距)── */ + +/* 基线对齐:inline 上下文垂直居中,flex 上下文不被压缩换行 */ +.sf-tp-icon { + display: inline-block; + vertical-align: middle; + flex-shrink: 0; +} + +/* 按钮内紧跟文字的图标留出呼吸空间(flex gap 之外的显式间距, + 兼容图标包在 里或直接暴露两种写法) */ +.sf-tp-btn svg { + margin-inline-start: var(--sf-tp-space-1); +} + +/* 非按钮内联上下文:图标贴着文字时补一点间距 */ +.pro-mega a .sf-tp-icon, +.pro-contact-email .sf-tp-icon, +.pro-footer-links a .sf-tp-icon { + margin-inline-start: var(--sf-tp-space-1); +} + +/* filters 宏搜索框:放大镜占位在输入框内左侧(不抢焦点) */ +.sf-theme-trade-pro .pro-filter-search-box { + position: relative; + display: block; + margin: 0; /* 抵消 pro-base 的 label>span 说明文字样式 */ +} + +.sf-theme-trade-pro .pro-filter-search-box .sf-tp-icon { + position: absolute; + inset-inline-start: var(--sf-tp-space-3); + inset-block-end: var(--sf-tp-space-3); + pointer-events: none; + color: var(--sf-tp-color-text-muted); +} + +.sf-theme-trade-pro .pro-filter-search-box input { + padding-inline-start: calc(var(--sf-tp-icon-size-md, 20px) + var(--sf-tp-space-6)); +} + /* ── Phase 2 桥接:骨架期 .pro-* 强调样式改由 cms 令牌驱动(Task 10 布局移植后由组件样式接管)── */ .pro-utility {