From e35011f2c01a3add62ccd36040d82aef08851452 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Jul 2026 10:30:22 +0800 Subject: [PATCH] fix(header): 1:1 zibll overlay icon cards for top multifunc component Rebuild header-slider card DOM/CSS against zib_header_slide + zib_icon_cover_card so shared/individual backgrounds, flex gutters, and badge icons match the reference theme. --- .../frontend/header-components/dark-theme.css | 369 ++++++------------ .../header-components/search-keywords-row.css | 48 +-- assets/css/frontend/widget-batch5.css | 27 +- assets/css/frontend/zibll-utilities.css | 23 ++ inc/helpers/aksrt-icon-cards.php | 244 +++++++----- inc/helpers/aksrt-template-tags.php | 67 ++-- templates/parts/home-modules/module-hero.php | 69 ++-- 7 files changed, 384 insertions(+), 463 deletions(-) diff --git a/assets/css/frontend/header-components/dark-theme.css b/assets/css/frontend/header-components/dark-theme.css index aefc4cc1..3e90a6bc 100644 --- a/assets/css/frontend/header-components/dark-theme.css +++ b/assets/css/frontend/header-components/dark-theme.css @@ -12,292 +12,149 @@ body.dark-theme .header-components-search__input { } /* ============================================================ - * Layer 3: 图标卡片(模仿 zibll — 骑在 hero 底部边界线上) - * - * 关键技术:卡片在 DOM 中位于轮播之后, - * 使用 margin-top: -40px 向上拉,使其骑在 hero 底部边界线上 + * Layer 3: Icon cards — 1:1 zibll header-slider-card + * DOM: .header-slider-card.container > (.zib-widget.padding-10|div) + * > .flex.jse.flex-row.gutters-5.flex-col-sm-2 + * > (.flex1|.relative-h) > [.main-color >] .icon-cover-card.flex.ac.(zib-widget|padding-10) * ============================================================ */ +.header-slider-card, .header-components-card { position: relative; - z-index: 1; margin-top: -40px; + z-index: 1; } -.header-components-card__container { - max-width: var(--container, 1280px); - margin: 0 auto; - padding: 0 16px; +.header-slider-card .zib-widget, +.header-components-card .zib-widget { + margin-bottom: 0; } -/* 卡片行:flex 水平排列 */ -.header-components-card__row { - display: flex; - gap: 10px; - padding-top: 4px; /* 为 hover 上浮留出空间 */ +/* Individual card surface = .zib-widget (from card-primitives); shared mode uses outer .zib-widget.padding-10 */ +.header-slider-card .icon-cover-card.zib-widget, +.header-components-card .icon-cover-card.zib-widget { + margin-bottom: 0; } -/* 每个卡片项等宽 */ -.header-components-card__item { - flex: 1; - min-width: 0; +/* zibll filter-blur glass on card surfaces + search form */ +.filter-blur .header-slider-search .line-form, +.filter-blur .header-components-search .line-form, +.filter-blur .header-slider-card .zib-widget, +.filter-blur .header-components-card .zib-widget { + -webkit-backdrop-filter: saturate(5) blur(20px); + backdrop-filter: saturate(5) blur(20px); } -/* 共享背景模式:一个大卡片包裹 */ -.header-components-card__wrap--shared { - background: var(--surface-page); - border-radius: 8px; - padding: 10px; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); - border: 1px solid var(--color-line); +.filter-blur .header-slider-card .zib-widget, +.filter-blur .header-components-card .zib-widget { + background: var(--blur-bg, rgba(255, 255, 255, 0.75)); } -/* 独立背景模式:每个卡片有自己的背景 */ -.header-components-card__item-bg { - background: var(--surface-page); - border-radius: 8px; - padding: 12px 14px; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); - border: 1px solid var(--color-line); - height: 100%; - transition: transform 0.2s ease, box-shadow 0.2s ease; +.theme-dark .filter-blur .header-slider-card .zib-widget, +html.dark .filter-blur .header-slider-card .zib-widget, +body.dark-theme .filter-blur .header-slider-card .zib-widget, +.theme-dark .filter-blur .header-components-card .zib-widget, +html.dark .filter-blur .header-components-card .zib-widget, +body.dark-theme .filter-blur .header-components-card .zib-widget { + background: var(--blur-bg, rgba(30, 30, 30, 0.75)); } -.header-components-card__item-bg:hover { - transform: none; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); -} - -/* 毛玻璃效果 */ -.filter-blur .header-components-card__wrap--shared, -.filter-blur .header-components-card__item-bg { - background: rgba(255, 255, 255, 0.75); - backdrop-filter: saturate(3) blur(20px); - -webkit-backdrop-filter: saturate(3) blur(20px); - border-color: rgba(255, 255, 255, 0.3); -} - -.theme-dark .filter-blur .header-components-card__wrap--shared, -html.dark .filter-blur .header-components-card__wrap--shared, -body.dark-theme .filter-blur .header-components-card__wrap--shared, -.theme-dark .filter-blur .header-components-card__item-bg, -html.dark .filter-blur .header-components-card__item-bg, -body.dark-theme .filter-blur .header-components-card__item-bg { - background: rgba(30, 30, 30, 0.75); - border-color: rgba(255, 255, 255, 0.1); -} - -/* ============================================================ - * 单个图标卡片样式(模仿 zibll icon-cover-card) - * 结构:左侧圆形图标 + 右侧标题/描述 - * ============================================================ */ -.icon-cover-card { - display: flex; - align-items: center; - gap: 10px; - padding: 8px; - border-radius: 6px; - text-decoration: none; - color: var(--color-ink); - transition: background 0.15s ease; -} - -a.icon-cover-card:hover { - background: var(--surface-3); - text-decoration: none; - color: var(--color-ink); -} - -/* 图标容器 — 圆形 */ -.icon-cover-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - min-width: 40px; - border-radius: 50%; - font-size: 18px; - flex-shrink: 0; -} - -/* 方形图标 */ -.icon-cover-icon.radius4 { - border-radius: 6px; -} - -/* 图标颜色类(模仿 zibll 的调色板) */ -.icon-cover-icon.c-blue { - background: rgba(66, 133, 244, 0.12); - color: #4285f4; -} -.icon-cover-icon.c-yellow { - background: rgba(251, 188, 4, 0.12); - color: #fbbc04; -} -.icon-cover-icon.c-green { - background: rgba(52, 168, 83, 0.12); - color: #34a853; -} -.icon-cover-icon.c-red { - background: rgba(234, 67, 53, 0.12); - color: #ea4335; -} -.icon-cover-icon.c-orange { - background: rgba(255, 152, 0, 0.12); - color: #ff9800; -} -.icon-cover-icon.c-cyan { - background: rgba(0, 188, 212, 0.12); - color: #00bcd4; -} -.icon-cover-icon.c-pink { - background: rgba(233, 30, 99, 0.12); - color: #e91e63; -} - -/* 渐变图标背景 */ -.icon-cover-icon.jb-cyan { - background: linear-gradient(135deg, #4facfe, #00f2fe); - color: #fff; -} -.icon-cover-icon.jb-green { - background: linear-gradient(135deg, #43e97b, #38f9d7); - color: #fff; -} -.icon-cover-icon.jb-orange { - background: linear-gradient(135deg, #fa709a, #fee140); - color: #fff; -} - -/* 文字区 */ -.icon-cover-desc { - flex: 1; - min-width: 0; - overflow: hidden; -} - -.icon-cover-title { - font-size: 13px; - font-weight: 600; - line-height: 1.3; - color: var(--color-ink); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.icon-cover-subtitle { - font-size: 11px; - color: var(--color-ink-4); - margin-top: 2px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - line-height: 1.3; -} - -/* ============================================================ - * 响应式 - * ============================================================ */ -@media (max-width: 767px) { - .header-components-card { - margin-top: -35px; - } - - .header-components-card__row { - flex-wrap: wrap; - gap: 6px; - } - - /* 移动端 2 列 */ - .header-components-card__item { - flex: 0 0 calc(50% - 3px); - } - - .header-components-card__item-bg { - padding: 10px; - } - - .header-components-card__wrap--shared { - padding: 6px; - } - - .icon-cover-card { - gap: 6px; - padding: 6px; - } - - .icon-cover-icon { - width: 32px; - height: 32px; - min-width: 32px; - font-size: 14px; - } - - .icon-cover-title { - font-size: 12px; - } - - .icon-cover-subtitle { - display: none; - } -} - -@media (max-width: 480px) { - .icon-cover-card { - gap: 4px; - padding: 2px; - } - - .icon-cover-icon { - width: 28px; - height: 28px; - min-width: 28px; - font-size: 12px; - } -} - -/* ============================================================ - * Container-level blur: PC/Mobile split (colors only; no top offsets) - * ============================================================ */ +/* Device-split blur (AKSRT extension of zibll filter-blur) */ @media (min-width: 768px) { - .has-blur-pc .header-components-card__wrap--shared, - .has-blur-pc .header-components-card__item-bg { - background: rgba(255, 255, 255, 0.75); - backdrop-filter: saturate(3) blur(20px); - -webkit-backdrop-filter: saturate(3) blur(20px); - border-color: rgba(255, 255, 255, 0.3); + .has-blur-pc .header-slider-card .zib-widget, + .has-blur-pc .header-components-card .zib-widget { + -webkit-backdrop-filter: saturate(5) blur(20px); + backdrop-filter: saturate(5) blur(20px); + background: var(--blur-bg, rgba(255, 255, 255, 0.75)); } .has-blur-pc .header-components-search .line-form, .has-blur-pc .header-slider-search .line-form { background: rgba(255, 255, 255, 0.75); } - .theme-dark .has-blur-pc .header-components-card__wrap--shared, - html.dark .has-blur-pc .header-components-card__wrap--shared, - .theme-dark .has-blur-pc .header-components-card__item-bg, - html.dark .has-blur-pc .header-components-card__item-bg { - background: rgba(30, 30, 30, 0.75); - border-color: rgba(255, 255, 255, 0.1); + .theme-dark .has-blur-pc .header-slider-card .zib-widget, + html.dark .has-blur-pc .header-slider-card .zib-widget, + .theme-dark .has-blur-pc .header-components-card .zib-widget, + html.dark .has-blur-pc .header-components-card .zib-widget { + background: var(--blur-bg, rgba(30, 30, 30, 0.75)); } } @media (max-width: 767px) { - .has-blur-mobile .header-components-card__wrap--shared, - .has-blur-mobile .header-components-card__item-bg { - background: rgba(255, 255, 255, 0.75); - backdrop-filter: saturate(3) blur(20px); - -webkit-backdrop-filter: saturate(3) blur(20px); - border-color: rgba(255, 255, 255, 0.3); + .has-blur-mobile .header-slider-card .zib-widget, + .has-blur-mobile .header-components-card .zib-widget { + -webkit-backdrop-filter: saturate(5) blur(20px); + backdrop-filter: saturate(5) blur(20px); + background: var(--blur-bg, rgba(255, 255, 255, 0.75)); } .has-blur-mobile .header-components-search .line-form, .has-blur-mobile .header-slider-search .line-form { background: rgba(255, 255, 255, 0.75); } - .theme-dark .has-blur-mobile .header-components-card__wrap--shared, - html.dark .has-blur-mobile .header-components-card__wrap--shared, - .theme-dark .has-blur-mobile .header-components-card__item-bg, - html.dark .has-blur-mobile .header-components-card__item-bg { - background: rgba(30, 30, 30, 0.75); - border-color: rgba(255, 255, 255, 0.1); + .theme-dark .has-blur-mobile .header-slider-card .zib-widget, + html.dark .has-blur-mobile .header-slider-card .zib-widget, + .theme-dark .has-blur-mobile .header-components-card .zib-widget, + html.dark .has-blur-mobile .header-components-card .zib-widget { + background: var(--blur-bg, rgba(30, 30, 30, 0.75)); + } + + .header-slider-card .icon-cover-card.zib-widget, + .header-components-card .icon-cover-card.zib-widget { + padding: 10px; + } + .header-slider-card .icon-cover-card.padding-10, + .header-components-card .icon-cover-card.padding-10 { + padding: 2px; + } + .header-slider-card, + .header-components-card { + margin-top: -35px; } } +/* icon-cover-card itself is a flex row — do NOT re-skin as a bordered box here. + * Surface comes from .zib-widget / .padding-10; icon badge from .badg.cir + c-*/jb-*. */ +.header-slider-card .icon-cover-card, +.header-components-card .icon-cover-card { + display: flex; + align-items: center; + gap: 0; + /* padding comes from .zib-widget or .padding-10 class on the same node */ +} + +.header-slider-card a.main-color, +.header-components-card a.main-color { + color: inherit; + text-decoration: none; + display: block; +} + +.header-slider-card a.main-color:hover, +.header-components-card a.main-color:hover { + color: inherit; + text-decoration: none; +} + +/* Let .badg.cir + color modifiers drive icon look; only size/image tweaks here */ +.header-slider-card .icon-cover-icon.badg.cir, +.header-components-card .icon-cover-icon.badg.cir { + display: inline-flex; + justify-content: center; + align-items: center; + flex-shrink: 0; +} + +.header-slider-card .icon-cover-icon img, +.header-components-card .icon-cover-icon img { + width: 70%; + height: 70%; + object-fit: contain; +} + +.header-slider-card .icon-cover-desc, +.header-components-card .icon-cover-desc { + min-width: 0; + overflow: hidden; +} + +.header-slider-card .icon-cover-desc .font-bold, +.header-components-card .icon-cover-desc .font-bold { + font-weight: bold; +} diff --git a/assets/css/frontend/header-components/search-keywords-row.css b/assets/css/frontend/header-components/search-keywords-row.css index 89ffb595..9cdae06a 100644 --- a/assets/css/frontend/header-components/search-keywords-row.css +++ b/assets/css/frontend/header-components/search-keywords-row.css @@ -100,48 +100,16 @@ margin-top: -35px; } - .header-components-card__container { - padding: 0 10px; + + .header-slider-card .icon-cover-card.zib-widget, + .header-components-card .icon-cover-card.zib-widget { + padding: 10px; + } + .header-slider-card .icon-cover-card.padding-10, + .header-components-card .icon-cover-card.padding-10 { + padding: 2px; } - .header-components-card__row { - flex-wrap: wrap; - gap: 6px; - } - - .header-components-card__item { - flex: 0 0 calc(50% - 3px); - } - - .header-components-card__item-bg { - padding: 8px 10px; - } - - .header-components-card__wrap--shared { - padding: 6px; - } - - .icon-cover-card { - gap: 6px; - padding: 4px; - min-height: 52px; - border-radius: 6px; - } - - .icon-cover-icon { - width: 30px; - height: 30px; - min-width: 30px; - font-size: 14px; - } - - .icon-cover-title { - font-size: 12px; - } - - .icon-cover-subtitle { - display: none; - } /* zibll ~640px: slide-header text padding 52px */ .header-components.slide-header .slide-text, diff --git a/assets/css/frontend/widget-batch5.css b/assets/css/frontend/widget-batch5.css index eb500440..c65ef30a 100644 --- a/assets/css/frontend/widget-batch5.css +++ b/assets/css/frontend/widget-batch5.css @@ -1862,31 +1862,21 @@ display: flex; align-items: center; gap: 0; - padding: 12px; - border-radius: 8px; - background: var(--surface-1, #fff); - border: 1px solid var(--color-line, #e5e7eb); - transition: box-shadow .2s ease, transform .2s ease; + /* Surface: .zib-widget / .padding-10 on same node (zibll). Do not re-box. */ } -a.main-color > .icon-cover-card:hover, -.icon-cover-card:hover { - box-shadow: 0 6px 18px rgba(0,0,0,.06); - transform: translateY(-1px); +a.main-color > .icon-cover-card:hover { + opacity: 0.92; } +/* Icon badge uses .badg.cir (+ c-* / jb-*); only ensure flex centering. */ .icon-cover-icon.badg.cir { display: inline-flex; align-items: center; justify-content: center; - width: 2.4em; - height: 2.4em; - min-width: 2.4em; - border-radius: 50%; - background: rgba(59, 130, 246, 0.12); - color: #2563eb; flex-shrink: 0; } -.icon-cover-icon.radius4 { - border-radius: 10px !important; +.icon-cover-icon.radius4, +.icon-cover-icon.badg.cir.radius4 { + border-radius: 4px !important; } .icon-cover-icon img { width: 70%; @@ -1895,9 +1885,10 @@ a.main-color > .icon-cover-card:hover, } .icon-cover-desc { min-width: 0; + overflow: hidden; } .icon-cover-desc .font-bold { - font-weight: 700; + font-weight: bold; } /* Buttons wrap (zibll flex hh) */ diff --git a/assets/css/frontend/zibll-utilities.css b/assets/css/frontend/zibll-utilities.css index 531c115e..280abc71 100644 --- a/assets/css/frontend/zibll-utilities.css +++ b/assets/css/frontend/zibll-utilities.css @@ -25,6 +25,29 @@ .flex1 { flex: 1 1 auto; min-width: 0; } .flex.column { flex-direction: column; } .flex.wrap { flex-wrap: wrap; } +.flex.flex-row, +.flex-row { flex-direction: row; } +.flex.jse, +.jse { justify-content: space-evenly; } +.flex.jsa, +.jsa { justify-content: space-around; } +/* Nested flex1 (zibll .flex .flex1) — allow both direct and descendant */ +.flex > .flex1, +.flex .flex1 { flex: 1; overflow: hidden; min-width: 0; } +.relative-h { position: relative; height: 100%; } +.padding-10 { padding: 10px; } +.font-bold { font-weight: bold; } +.mt6 { margin-top: 6px; } +.gutters-5 { margin: -5px; } +.gutters-5 > * { padding: 5px; } +@media (max-width: 767px) { + .flex.flex-row.flex-col-sm-2 { + flex-wrap: wrap; + } + .flex.flex-row.flex-col-sm-2 > * { + flex-basis: 50%; + } +} .clearfix::before, .clearfix::after { diff --git a/inc/helpers/aksrt-icon-cards.php b/inc/helpers/aksrt-icon-cards.php index 79ff2824..3d72fadd 100644 --- a/inc/helpers/aksrt-icon-cards.php +++ b/inc/helpers/aksrt-icon-cards.php @@ -135,103 +135,165 @@ if (!function_exists('aksrt_icon_cover_card')) : * @param bool $echo */ function aksrt_icon_cover_card(array $args = [], bool $echo = false): string { - $defaults = [ - 'class' => 'zib-widget', - 'icon' => '', - 'icon_size' => 25, - 'customize_icon' => '', - 'icon_image' => '', - 'link' => [ - 'url' => '', - 'target' => '', - ], - 'icon_radius' => false, - 'icon_color' => '', - 'icon_custom_color' => '', - 'icon_class' => '', - 'title' => '', - 'desc' => '', - ]; - $args = wp_parse_args($args, $defaults); + $defaults = [ + 'class' => 'zib-widget', + 'icon' => '', + 'icon_size' => 25, + 'customize_icon' => '', + 'custom_icon' => '', + 'icon_image' => '', + 'link' => [ + 'url' => '', + 'target' => '', + ], + // Flat keys used by header_components_icon_cards admin schema. + 'url' => '', + 'target' => '', + 'icon_radius' => false, + 'icon_color' => '', + 'icon_custom_color' => '', + 'icon_class' => '', + 'title' => '', + 'desc' => '', + ]; + $args = wp_parse_args($args, $defaults); - $icon_fa = sanitize_text_field((string) $args['icon']); - $icon_img = esc_url_raw(trim((string) ($args['icon_image'] !== '' ? $args['icon_image'] : ''))); - $custom_html = ''; - if ($icon_img === '' && is_string($args['customize_icon']) && str_contains((string) $args['customize_icon'], '<')) { - $custom_html = wp_kses((string) $args['customize_icon'], [ - 'i' => ['class' => true, 'aria-hidden' => true], - 'img' => ['src' => true, 'alt' => true, 'class' => true, 'loading' => true], - 'span'=> ['class' => true], - ]); - } elseif ($icon_img === '' && is_string($args['customize_icon']) && filter_var((string) $args['customize_icon'], FILTER_VALIDATE_URL)) { - $icon_img = esc_url_raw((string) $args['customize_icon']); - } - if ($icon_fa === '' && $icon_img === '' && $custom_html === '') { - return ''; - } - - $link = $args['link']; - if (is_string($link)) { - $link = ['url' => $link, 'target' => '']; - } - if (!is_array($link)) { - $link = ['url' => '', 'target' => '']; - } - $url = esc_url(trim((string) ($link['url'] ?? ''))); - $target = sanitize_text_field((string) ($link['target'] ?? '')); - $tgt = $target !== '' ? ' target="' . esc_attr($target) . '" rel="noopener noreferrer"' : ''; - - $icon_class = trim('icon-cover-icon badg cir ' . sanitize_html_class((string) $args['icon_class']) . ' ' . sanitize_html_class((string) $args['icon_color'])); - if (!empty($args['icon_radius'])) { - $icon_class .= ' radius4'; - } - $icon_size = max(14, min(64, (int) $args['icon_size'])); - $style = 'font-size:' . $icon_size . 'px;'; - $custom_color = sanitize_hex_color((string) $args['icon_custom_color']); - if ($custom_color) { - $style .= 'color:' . $custom_color . ';'; - } - - if ($custom_html !== '') { - $icon_inner = $custom_html; - } elseif ($icon_img !== '') { - $icon_inner = ''; - } else { - if ($icon_fa !== '' && !str_contains($icon_fa, 'fa-') && !str_contains($icon_fa, ' ')) { - $icon_fa = 'fa fa-' . $icon_fa; + // Normalize link: accept nested link[] or flat url/target (SPA schema). + $link = $args['link']; + if (is_string($link)) { + $link = ['url' => $link, 'target' => (string) $args['target']]; + } + if (!is_array($link)) { + $link = ['url' => '', 'target' => '']; + } + if (trim((string) ($link['url'] ?? '')) === '' && trim((string) $args['url']) !== '') { + $link['url'] = (string) $args['url']; + } + if (trim((string) ($link['target'] ?? '')) === '' && trim((string) $args['target']) !== '') { + $link['target'] = (string) $args['target']; } - $icon_inner = ''; - } - $icon = '
' . $icon_inner . '
'; - $title = trim((string) $args['title']); - $desc = trim((string) $args['desc']); - $text = ''; - if ($title !== '') { - $text .= '
' . esc_html($title) . '
'; - } - if ($desc !== '') { - $text .= '
' . esc_html($desc) . '
'; - } + $icon_fa = trim((string) $args['icon']); + // custom_icon (SPA) + customize_icon (zibll) + icon_image URL. + $custom_src = (string) ($args['custom_icon'] !== '' ? $args['custom_icon'] : $args['customize_icon']); + $icon_img = esc_url_raw(trim((string) ($args['icon_image'] !== '' ? $args['icon_image'] : ''))); + $custom_html = ''; + if ($icon_img === '' && $custom_src !== '' && str_contains($custom_src, '<')) { + $custom_html = wp_kses($custom_src, [ + 'i' => ['class' => true, 'aria-hidden' => true], + 'img' => ['src' => true, 'alt' => true, 'class' => true, 'loading' => true, 'width' => true, 'height' => true], + 'span' => ['class' => true], + 'svg' => ['class' => true, 'viewbox' => true, 'viewBox' => true, 'width' => true, 'height' => true, 'fill' => true, 'xmlns' => true, 'aria-hidden' => true], + 'path' => ['d' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true], + 'use' => ['href' => true, 'xlink:href' => true], + ]); + } elseif ($icon_img === '' && $custom_src !== '' && filter_var($custom_src, FILTER_VALIDATE_URL)) { + $icon_img = esc_url_raw($custom_src); + } + if ($icon_fa === '' && $icon_img === '' && $custom_html === '') { + return ''; + } - $html = ''; - if ($url !== '') { - $html .= ''; - } - $html .= '
'; - $html .= $icon; - $html .= $text !== '' ? '
' . $text . '
' : ''; - $html .= '
'; - if ($url !== '') { - $html .= '
'; - } + $url = esc_url(trim((string) ($link['url'] ?? ''))); + $target = sanitize_text_field((string) ($link['target'] ?? '')); + // zibll only adds target= when non-empty; keep same for 1:1. + $tgt = ($target !== '' && $target !== '_self') ? ' target="' . esc_attr($target) . '"' : ''; - if ($echo) { - echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - return ''; + // zibll: class = "icon-cover-icon badg cir" + optional icon_class (may include radius4 / c-* / jb-*). + // Do NOT sanitize_html_class on the whole string — it collapses multi-token classes. + $icon_class_extra = trim(preg_replace('/\s+/', ' ', (string) $args['icon_class'])); + $icon_class = 'icon-cover-icon badg cir'; + if ($icon_class_extra !== '') { + $tokens = preg_split('/\s+/', $icon_class_extra) ?: []; + $safe = []; + foreach ($tokens as $tok) { + $tok = preg_replace('/[^a-zA-Z0-9_-]/', '', $tok); + if ($tok !== '') { + $safe[] = $tok; + } + } + if ($safe) { + $icon_class .= ' ' . implode(' ', $safe); + } + } + if (!empty($args['icon_radius']) && !str_contains($icon_class, 'radius4')) { + $icon_class .= ' radius4'; + } + $icon_color = trim((string) $args['icon_color']); + if ($icon_color !== '' && !str_contains($icon_class, $icon_color)) { + $icon_class .= ' ' . preg_replace('/[^a-zA-Z0-9_-]/', '', $icon_color); + } + + $icon_size = max(14, min(64, (int) $args['icon_size'])); + // zibll always sets font-size on the icon badge. + $style = 'font-size: ' . $icon_size . 'px;'; + $custom_color = sanitize_hex_color((string) $args['icon_custom_color']); + if ($custom_color) { + $style .= 'color:' . $custom_color . ';'; + } + + if ($custom_html !== '') { + $icon_inner = $custom_html; + } elseif ($icon_img !== '') { + $icon_inner = ''; + } else { + // zibll: zib_get_cfs_icon($args['icon'], 'em09') — keep FA class + em09. + if ($icon_fa !== '' && !str_contains($icon_fa, 'fa-') && !str_contains($icon_fa, ' ') && !str_starts_with($icon_fa, 'zibsvg-')) { + $icon_fa = 'fa fa-' . $icon_fa; + } + if (str_contains($icon_fa, '<')) { + $icon_inner = wp_kses($icon_fa, [ + 'i' => ['class' => true, 'aria-hidden' => true], + 'svg' => ['class' => true, 'viewbox' => true, 'viewBox' => true, 'width' => true, 'height' => true, 'fill' => true, 'xmlns' => true, 'aria-hidden' => true], + 'path' => ['d' => true, 'fill' => true], + 'use' => ['href' => true, 'xlink:href' => true], + ]); + } else { + $icon_inner = ''; + } + } + $icon = '
' . $icon_inner . '
'; + + // zibll allows HTML in title/desc (admin stores badges etc.). + $title = (string) $args['title']; + $desc = (string) $args['desc']; + $allow = [ + 'badge' => ['class' => true], + 'span' => ['class' => true], + 'i' => ['class' => true, 'aria-hidden' => true], + 'em' => ['class' => true], + 'strong' => ['class' => true], + 'b' => [], + 'small' => ['class' => true], + 'br' => [], + ]; + $text = ''; + if (trim(wp_strip_all_tags($title)) !== '' || str_contains($title, '<')) { + $text .= '
' . wp_kses($title, $allow) . '
'; + } + if (trim(wp_strip_all_tags($desc)) !== '' || str_contains($desc, '<')) { + $text .= '
' . wp_kses($desc, $allow) . '
'; + } + + $html = ''; + if ($url !== '') { + // zibll: + $html .= ''; + } + $html .= '
'; + $html .= $icon; + $html .= $text !== '' ? '
' . $text . '
' : ''; + $html .= '
'; + if ($url !== '') { + $html .= '
'; + } + + if ($echo) { + echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return ''; + } + return $html; } - return $html; - } endif; if (!function_exists('aksrt_obs_animate_attrs')) : diff --git a/inc/helpers/aksrt-template-tags.php b/inc/helpers/aksrt-template-tags.php index 5700ed06..307529e0 100644 --- a/inc/helpers/aksrt-template-tags.php +++ b/inc/helpers/aksrt-template-tags.php @@ -495,45 +495,46 @@ if (!function_exists('aksrt_render_icon_cover_card')): * @return string HTML */ function aksrt_render_icon_cover_card(array $card): string { - $icon = $card['icon'] ?? 'fas fa-star'; - $custom_icon = $card['custom_icon'] ?? ''; - $icon_class = $card['icon_class'] ?? 'c-blue'; - $icon_shape = ($card['icon_shape'] ?? 'circle') === 'square' ? 'radius4' : ''; - $title = $card['title'] ?? ''; - $desc = $card['desc'] ?? ''; - $url = $card['url'] ?? ''; - $target = ($card['target'] ?? '_self') === '_blank' ? ' target="_blank" rel="noopener"' : ''; + // Prefer canonical zibll-aligned renderer. + if (function_exists('aksrt_icon_cover_card')) { + return aksrt_icon_cover_card($card); + } - // 图标 HTML - $icon_html = $custom_icon - ? $custom_icon - : ''; + $icon = $card['icon'] ?? 'fas fa-star'; + $custom_icon = $card['custom_icon'] ?? ($card['customize_icon'] ?? ''); + $icon_class = trim((string) ($card['icon_class'] ?? 'c-blue')); + if (($card['icon_shape'] ?? '') === 'square' && !str_contains($icon_class, 'radius4')) { + $icon_class .= ' radius4'; + } + $title = $card['title'] ?? ''; + $desc = $card['desc'] ?? ''; + $url = $card['url'] ?? ($card['link']['url'] ?? ''); + $target = $card['target'] ?? ($card['link']['target'] ?? ''); + $tgt = ($target !== '' && $target !== '_self') ? ' target="' . esc_attr((string) $target) . '"' : ''; - // 图标容器 - $icon_container = '
' . $icon_html . '
'; + $icon_html = $custom_icon !== '' + ? wp_kses_post((string) $custom_icon) + : ''; - // 文字区 - $text_html = '
'; - if ($title) { - $text_html .= '
' . wp_kses_post($title) . '
'; - } - if ($desc) { - $text_html .= '
' . wp_kses_post($desc) . '
'; - } - $text_html .= '
'; + $icon_container = '
' . $icon_html . '
'; - // 卡片内容 - $card_inner = $icon_container . $text_html; + $text_html = '
'; + if ($title !== '') { + $text_html .= '
' . wp_kses_post((string) $title) . '
'; + } + if ($desc !== '') { + $text_html .= '
' . wp_kses_post((string) $desc) . '
'; + } + $text_html .= '
'; - // 包裹标签 - if ($url) { - $html = '' . $card_inner . ''; - } else { - $html = '
' . $card_inner . '
'; - } + $card_inner = $icon_container . $text_html; + $cls = 'icon-cover-card flex ac ' . esc_attr((string) ($card['class'] ?? 'zib-widget')); - return $html; -} + if ($url !== '') { + return '
' . $card_inner . '
'; + } + return '
' . $card_inner . '
'; + } endif; // ============================================================ diff --git a/templates/parts/home-modules/module-hero.php b/templates/parts/home-modules/module-hero.php index 46822d59..1b8e1806 100644 --- a/templates/parts/home-modules/module-hero.php +++ b/templates/parts/home-modules/module-hero.php @@ -152,33 +152,52 @@ $render_icon_cards = static function () use ($show_cards, $icon_cards, $card_bg_ if (!$show_cards || empty($icon_cards)) { return; } + /** + * Overlay icon cards — 1:1 zibll zib_header_slide() card module. + * + * show_widget_bg (shared) → outer .zib-widget.padding-10 + each card class=padding-10 in .relative-h + * individual (default) → outer empty div + each card class=zib-widget in .flex1 + * Row: .flex.jse.flex-row.gutters-5.flex-col-sm-2 + */ + $show_widget_bg = ($card_bg_mode === 'shared'); + $icon_radius4 = ($card_icon_shape === 'square'); + $slider_card_html = ''; + foreach ($icon_cards as $raw) { + if (!is_array($raw)) { + continue; + } + $card = $raw; + $card['class'] = $show_widget_bg ? 'padding-10' : 'zib-widget'; + $extra = trim((string) ($card['icon_class'] ?? '')); + if ($icon_radius4 && !str_contains($extra, 'radius4')) { + $extra = trim($extra . ' radius4'); + } + $card['icon_class'] = $extra; + unset($card['icon_shape']); + + $wrap_class = $show_widget_bg ? 'relative-h' : 'flex1'; + $inner = function_exists('aksrt_icon_cover_card') + ? aksrt_icon_cover_card($card) + : (function_exists('aksrt_render_icon_cover_card') ? aksrt_render_icon_cover_card($card) : ''); + if ($inner === '') { + continue; + } + $slider_card_html .= '
' . $inner . '
'; + } + if ($slider_card_html === '') { + return; + } ?>
-
- -
-
- - -
- -
- -
-
- -
- - -
-
- -
-
- -
- -
+ +
+ +
+ +
+ +
+