diff --git a/.gitignore b/.gitignore index 8d8a5dbf..d7c25d50 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ !/tests/widget-system/lazyload-contract.test.cjs !/tests/widget-system/toast-contract.test.cjs !/tests/widget-system/page-display-contract.test.cjs +!/tests/widget-system/icon-card-contract.test.cjs !/tests/widget-system/page-display-save-harness.php !/tests/widget-system/admin-ui-contract.test.cjs !/tests/visual/docker-compose.zibll.yml diff --git a/assets/css/frontend/header-components.css b/assets/css/frontend/header-components.css index 63512091..a779178c 100644 --- a/assets/css/frontend/header-components.css +++ b/assets/css/frontend/header-components.css @@ -56,16 +56,6 @@ padding-right: 15px; box-sizing: border-box; } -.header-slider-card .icon-cover-card.zib-widget, -.header-components-card .icon-cover-card.zib-widget { - margin-bottom: 0; - padding: var(--card-padding, 15px); - box-shadow: 0 0 10px var(--main-shadow, var(--shadow-sm, rgba(116, 116, 116, 0.08))); - border-radius: var(--main-radius, var(--radius, 8px)); - background-color: var(--main-bg-color, var(--surface-1, #fff)); - height: 100%; - box-sizing: border-box; -} .filter-blur .header-slider-card .zib-widget, .filter-blur .header-components-card .zib-widget { -webkit-backdrop-filter: saturate(5) blur(20px); diff --git a/assets/css/frontend/header-components/dark-theme.css b/assets/css/frontend/header-components/dark-theme.css index 8255c75f..24ac644b 100644 --- a/assets/css/frontend/header-components/dark-theme.css +++ b/assets/css/frontend/header-components/dark-theme.css @@ -5,6 +5,7 @@ body.dark-theme .header-components-search__form { background: rgba(30, 30, 30, 0.9); } + .theme-dark .header-components-search__input, html.dark .header-components-search__input, body.dark-theme .header-components-search__input { @@ -100,18 +101,6 @@ body.dark-theme .header-components-search__input { margin-bottom: 0; } -/* Match zibll widget surface so homepage and other page types look identical */ -.header-slider-card .icon-cover-card.zib-widget, -.header-components-card .icon-cover-card.zib-widget { - margin-bottom: 0; - padding: var(--card-padding, 15px); - box-shadow: 0 0 10px var(--main-shadow, var(--shadow-sm, rgba(116, 116, 116, 0.08))); - border-radius: var(--main-radius, var(--radius, 8px)); - background-color: var(--main-bg-color, var(--surface-1, #fff)); - height: 100%; - box-sizing: border-box; -} - /* zibll filter-blur glass on card surfaces + search form */ .filter-blur .header-slider-search .line-form, .filter-blur .header-components-search .line-form, @@ -172,12 +161,10 @@ body.dark-theme .filter-blur .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 { + .header-slider-card .icon-cover-card.zib-widget { padding: 10px; } - .header-slider-card .icon-cover-card.padding-10, - .header-components-card .icon-cover-card.padding-10 { + .header-slider-card .icon-cover-card.padding-10 { padding: 2px; } .header-slider-card, @@ -185,53 +172,3 @@ body.dark-theme .filter-blur .header-components-card .zib-widget { 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 6f5d3279..86ee186c 100644 --- a/assets/css/frontend/header-components/search-keywords-row.css +++ b/assets/css/frontend/header-components/search-keywords-row.css @@ -128,12 +128,10 @@ } - .header-slider-card .icon-cover-card.zib-widget, - .header-components-card .icon-cover-card.zib-widget { + .header-slider-card .icon-cover-card.zib-widget { padding: 10px; } - .header-slider-card .icon-cover-card.padding-10, - .header-components-card .icon-cover-card.padding-10 { + .header-slider-card .icon-cover-card.padding-10 { padding: 2px; } diff --git a/assets/widgets/frontend.css b/assets/widgets/frontend.css index 63e3fbba..2388b851 100644 --- a/assets/widgets/frontend.css +++ b/assets/widgets/frontend.css @@ -63,8 +63,8 @@ .aksrt-widget-quote blockquote { margin: 12px 0 0; font-size: 16px; line-height: 1.8; } .aksrt-widget-quote button { position: absolute; right: 13px; bottom: 13px; width: 30px; height: 30px; border: 0; border-radius: 50%; background: rgba(255,255,255,.16); color: #fff; cursor: pointer; } -.aksrt-widget-icon-card, .aksrt-widget-icon-cards .icon-card, .aksrt-widget-icon-cards .icon-cover-card { min-height: 130px; max-width:280px; margin-right:auto; margin-left:auto; padding: 20px; border-radius: var(--aksrt-widget-radius); background: var(--main-bg-color, #fff); box-shadow: 0 0 10px var(--main-shadow,rgba(116,116,116,.08)); transition: box-shadow .2s; } -.aksrt-widget-icon-card:hover, .aksrt-widget-icon-cards .icon-card:hover, .aksrt-widget-icon-cards .icon-cover-card:hover { box-shadow: 0 0 10px rgba(116,116,116,.14); } +.aksrt-widget-icon-card { min-height: 130px; max-width:280px; margin-right:auto; margin-left:auto; padding: 20px; border-radius: var(--aksrt-widget-radius); background: var(--main-bg-color, #fff); box-shadow: 0 0 10px var(--main-shadow,rgba(116,116,116,.08)); transition: box-shadow .2s; } +.aksrt-widget-icon-card:hover { box-shadow: 0 0 10px rgba(116,116,116,.14); } .aksrt-widget-icon-card { display: flex; flex-direction: column; gap: 12px; text-align: center; } .aksrt-widget-icon-cards--cover .aksrt-widget-icon-card { flex-direction: row; align-items: center; text-align: left; } .aksrt-widget-icon-card > i { font-size: 34px; color: var(--focus-color, #ff5473); transition:transform .3s; } diff --git a/inc/helpers/homepage/header-components.php b/inc/helpers/homepage/header-components.php index c7e73575..12875c7f 100644 --- a/inc/helpers/homepage/header-components.php +++ b/inc/helpers/homepage/header-components.php @@ -419,7 +419,7 @@ if (!function_exists('aksrt_header_slide')) { ]); $items = ''; foreach ((array) $card_options['cards'] as $card) { - if (!is_array($card) || (empty($card['icon']) && empty($card['customize_icon']))) { + if (!is_array($card) || (empty($card['icon']) && empty($card['customize_icon']) && empty($card['custom_icon']) && empty($card['icon_image']))) { continue; } $card['class'] = !empty($card_options['show_widget_bg']) ? 'padding-10' : 'zib-widget'; diff --git a/src/WidgetSystem/Renderers/GeneralRenderer.php b/src/WidgetSystem/Renderers/GeneralRenderer.php index 4d328b00..f58b088f 100644 --- a/src/WidgetSystem/Renderers/GeneralRenderer.php +++ b/src/WidgetSystem/Renderers/GeneralRenderer.php @@ -47,7 +47,17 @@ final class GeneralRenderer { if (!is_array($card)) { continue; } - $args = ['icon' => $card['icon'] ?? '', 'customize_icon' => $card['custom_icon'] ?? '', 'icon_image' => $card['custom_icon'] ?? '', 'icon_color' => $card['color'] ?? '', 'icon_class' => $card['color'] ?? '', 'title' => $card['title'] ?? '', 'desc' => $card['description'] ?? '', 'link' => $card['link'] ?? []]; + $args = [ + 'icon' => $card['icon'] ?? '', + 'customize_icon' => $card['customize_icon'] ?? '', + 'custom_icon' => $card['custom_icon'] ?? '', + 'icon_image' => $card['icon_image'] ?? '', + 'icon_color' => $card['color'] ?? '', + 'icon_class' => $card['color'] ?? '', + 'title' => $card['title'] ?? '', + 'desc' => $card['description'] ?? '', + 'link' => $card['link'] ?? [], + ]; if ($cover && function_exists('aksrt_icon_cover_card')) { $html .= aksrt_icon_cover_card($args); } elseif (!$cover && function_exists('aksrt_icon_card')) { diff --git a/tests/widget-system/icon-card-contract.test.cjs b/tests/widget-system/icon-card-contract.test.cjs new file mode 100644 index 00000000..108850ab --- /dev/null +++ b/tests/widget-system/icon-card-contract.test.cjs @@ -0,0 +1,36 @@ +'use strict'; + +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const test = require('node:test'); + +const root = path.resolve(__dirname, '..', '..'); +const read = (file) => fs.readFileSync(path.join(root, file), 'utf8'); + +test('icon card renderers keep vertical and horizontal Zibll DOM contracts separate', () => { + const helper = read('inc/helpers/aksrt-icon-cards.php'); + const widgets = read('src/WidgetSystem/Renderers/GeneralRenderer.php'); + const header = read('inc/helpers/homepage/header-components.php'); + + assert.match(helper, /
\$card\['customize_icon'\] \?\? ''/); + assert.match(widgets, /'custom_icon'\s+=> \$card\['custom_icon'\] \?\? ''/); + assert.match(widgets, /'icon_image'\s+=> \$card\['icon_image'\] \?\? ''/); + assert.match(header, /empty\(\$card\['custom_icon'\]\)/); +}); + +test('homepage horizontal cards only use Zibll surface and responsive padding rules', () => { + const css = read('assets/css/frontend/header-components/dark-theme.css'); + const widgetCss = read('assets/widgets/frontend.css'); + + assert.match(css, /\.header-slider-card \.icon-cover-card\.zib-widget \{\s*padding: 10px;/); + assert.match(css, /\.header-slider-card \.icon-cover-card\.padding-10 \{\s*padding: 2px;/); + assert.doesNotMatch(css, /\.header-slider-card \.icon-cover-icon\.badg\.cir/); + assert.doesNotMatch(css, /\.header-slider-card \.icon-cover-icon img/); + assert.doesNotMatch(widgetCss, /aksrt-widget-icon-cards \.icon-cover-card/); +});