diff --git a/assets/css/frontend/vip-modal.css b/assets/css/frontend/vip-modal.css
index c9cb0f0c..261e7983 100644
--- a/assets/css/frontend/vip-modal.css
+++ b/assets/css/frontend/vip-modal.css
@@ -278,72 +278,6 @@
color: #e74c3c;
}
-/* Header hover VIP cards
- * Markup: .navbar-vip > .navbar-vip-hover.hover-show > a + .hover-show-con.sub-vip-card
- * (hover-show is on the INNER span, not on .navbar-vip itself)
- */
-.navbar-vip-hover.hover-show,
-.navbar-vip .hover-show {
- position: relative;
-}
-
-.sub-vip-card {
- display: none;
- position: absolute;
- top: calc(100% + 0px);
- right: 0;
- z-index: 60;
- width: max-content;
- max-width: min(520px, 92vw);
- padding: 10px;
- padding-top: 12px; /* hover bridge */
- border-radius: 12px;
- background: transparent;
- box-shadow: none;
- gap: 10px;
- box-sizing: border-box;
-}
-
-.navbar-vip-hover.hover-show:hover .sub-vip-card,
-.navbar-vip-hover.hover-show:focus-within .sub-vip-card,
-.navbar-vip .hover-show:hover .sub-vip-card,
-.navbar-vip .hover-show:focus-within .sub-vip-card {
- display: flex;
- flex-wrap: wrap;
-}
-
-.vip-card {
- position: relative;
- flex: 1 1 160px;
- min-width: 150px;
- padding: 14px 12px;
- border-radius: 10px;
- overflow: hidden;
- background: linear-gradient(135deg, #2b2f3a, #1b1e27);
- color: #fff;
-}
-
-.vip-card.vip-theme1 {
- background: linear-gradient(135deg, #f6c453, #f39c12);
- color: #3b2a00;
-}
-
-.vip-card.vip-theme2 {
- background: linear-gradient(135deg, #8e44ad, #3f51b5);
- color: #fff;
-}
-
-.vip-card .payvip-button {
- display: inline-block;
- margin-top: 6px;
- padding: 6px 12px;
- border-radius: 999px;
- background: rgba(255, 255, 255, 0.92);
- color: #333;
- text-decoration: none;
- font-size: 12px;
-}
-
.vipbg-v2 {
display: flex;
align-items: center;
diff --git a/src/Content/VipServiceModal.php b/src/Content/VipServiceModal.php
index 55206e8c..a5331c51 100644
--- a/src/Content/VipServiceModal.php
+++ b/src/Content/VipServiceModal.php
@@ -153,31 +153,26 @@ trait VipServiceModal {
return '';
}
$name = (string) ($levels[$level]['name'] ?? ('VIP' . $level));
- $desc = (string) ($levels[$level]['desc'] ?? '');
$equity = (string) ($levels[$level]['equity'] ?? '');
$cls = $user_id > 0 ? ' pay-vip' : ' signin-loader';
- // Preserve admin equity HTML as-is (sanitize/wrap only; never re-split).
- $equity_html = function_exists('aksrt_format_vip_equity_html')
- ? aksrt_format_vip_equity_html($equity, 'mb10 relative')
+ $equity_html = $equity !== ''
+ ? '
' . wp_kses_post($equity) . '
'
: '';
$icon_url = esc_url((string) ($levels[$level]['img_icon'] ?? ''));
$icon = $icon_url !== ''
- ? '
'
+ ? '
'
: '';
$html = '';
- $html .= '
' . $icon . '
';
+ $html .= '
' . $icon . '
';
$html .= '
' . $icon . '
';
$html .= '
';
- $html .= '
' . $icon . '' . esc_html($name) . '
';
- if ($desc !== '') {
- $html .= '
' . esc_html($desc) . '
';
- }
+ $html .= '
' . $icon . '' . esc_html($name) . '
';
$html .= $equity_html;
$html .= '
'
. esc_html(sprintf(__('开通%s', 'aksrt'), $name))
- . '';
+ . '
';
$html .= '
';
return $html;
}
diff --git a/tests/widget-system/page-display-contract.test.cjs b/tests/widget-system/page-display-contract.test.cjs
index 15e9acbe..a6b1e033 100644
--- a/tests/widget-system/page-display-contract.test.cjs
+++ b/tests/widget-system/page-display-contract.test.cjs
@@ -25,6 +25,9 @@ const headerNavbar = read('inc/helpers/header/aksrt-header-navbar.php')
const headerUserbox = read('inc/helpers/aksrt-header-userbox.php')
const headerSearch = read('inc/helpers/header/aksrt-header-menu-search.php')
const headerCss = read('assets/css/frontend/header-userbox.css')
+const vipCardRenderer = read('src/Content/VipServiceModal.php')
+const vipModalCss = read('assets/css/frontend/vip-modal.css')
+const zibllMainCss = read('assets/vendor/zibll/css/main.css')
function activeRepositorySource() {
const roots = ['assets/admin/src', 'assets/js', 'inc', 'src', 'templates']
@@ -126,6 +129,25 @@ test('VIP navigation purchase switches are saved and consumed end to end', () =>
assert.match(headerNavbar, /!empty\(\$vs\['enabled'\]\) && !empty\(\$vs\['nav_user_pay_vip'\]\)/)
})
+test('header VIP hover cards match the Zibll markup and style contract', () => {
+ assert.match(vipCardRenderer, /aksrt_get_lazy_attr\('lazy_other', \$icon_url, 'img-icon'\)/)
+ assert.match(vipCardRenderer, /]*>/)
+ assert.match(vipCardRenderer, /
/)
+ assert.match(vipCardRenderer, /
/)
+ assert.match(vipCardRenderer, //)
+ assert.doesNotMatch(vipCardRenderer, /vip-name__(?:icon|label)/)
+ assert.doesNotMatch(vipCardRenderer, /aksrt_format_vip_equity_html\(\$equity, 'mb10 relative'\)/)
+
+ assert.doesNotMatch(vipModalCss, /Header hover VIP cards/)
+ assert.doesNotMatch(vipModalCss, /\.navbar-vip-hover\.hover-show/)
+ assert.doesNotMatch(vipModalCss, /\.sub-vip-card/)
+ assert.doesNotMatch(vipModalCss, /\.vip-card\.vip-theme[12]/)
+ assert.match(zibllMainCss, /\.sub-vip-card\s*\{[^}]*font-size:\s*12px;[^}]*padding:\s*0\s*!important;/s)
+ assert.match(zibllMainCss, /\.sub-vip-card \.vip-card\s*\{[^}]*display:\s*inline-block;[^}]*width:\s*240px;[^}]*margin:\s*5px;/s)
+ assert.match(zibllMainCss, /\.sub-vip-card \.vip-baicon\.abs-center\s*\{[^}]*font-size:\s*10em;[^}]*left:\s*55%;/s)
+ assert.match(zibllMainCss, /\.sub-vip-card \.vip-img\s*\{[^}]*display:\s*none;/s)
+})
+
test('schema REST saves are privileged, section-whitelisted, and deeply sanitized', () => {
assert.match(controllerBase, /current_user_can\('manage_options'\)/)
assert.match(controllerBase, /sanitize_payload\(\$domain, \$payload, \$section\)/)