Merge pull request 'fix(ui): render user level icon inline beside the name' (#328) from fix/level-icon-inline into main

Reviewed-on: CYL/wordpress-theme-aksrt#328
This commit is contained in:
kate522 committed 2026-07-30 18:20:16 +08:00
commit 59e550fd27
13 files changed
+134 -34

No files matched your search

+47
View File
@@ -162,6 +162,53 @@ main.container,
} }
} }
/* ========== User identity inline icons ==========
* Level / medal icons ride inline to the right of a username. Centralized here
* so every route (header panel, user center, author hero, single, comments,
* widgets) renders the same compact badge instead of the raw intrinsic PNG size.
*
* Mirrors zibll `.img-icon { height: 1.1em; display: inline-block; vertical-align: -.15em }`
* — sizes to the surrounding line height, never dominates the row. Localized
* exceptions (UC level hero, admin column) override `height` per context.
*/
.aksrt-user-level-icon,
.aksrt-identity-medal {
display: inline-block;
width: auto;
height: 1.1em;
max-width: 6em;
object-fit: contain;
vertical-align: -.15em;
flex: 0 0 auto;
}
/* Comment context: a touch smaller to fit the 14px author line. */
.aksrt-identity-medal--comment,
.aksrt-user-level-icon--comment {
height: 1em;
vertical-align: -.12em;
}
/* No-wrap inline name rows — keep the level icon on the same line as the name.
* The non-standard <name> element (zibll compat) plus .user-name / author-hero /
* uc-header name rows must not flex-wrap, or the badge drops to a new line.
* The comment header may still wrap (avatar + name + like can overflow on
* narrow viewports), so it keeps flex-wrap but the inner <name> never does. */
.article-avatar name,
.article-avatar .name,
.user-name,
.aksrt-author-hero__name,
.uc-header__name,
.aksrt-header-badges {
flex-wrap: nowrap;
white-space: nowrap;
}
.aksrt-author-hero__name,
.uc-header__name {
display: inline-flex;
align-items: center;
gap: 4px;
}
/* ========== 通用空状态 ========== */ /* ========== 通用空状态 ========== */
.no-results { .no-results {
padding: 48px 20px; padding: 48px 20px;
@@ -30,6 +30,9 @@
.comment-header .author-box { display: flex; align-items: center; gap: 8px; width: 100%; } .comment-header .author-box { display: flex; align-items: center; gap: 8px; width: 100%; }
.comment-header .author-box name, .comment-header .author-box name,
.comment-header .author-box .flex1 { flex: 1; min-width: 0; } .comment-header .author-box .flex1 { flex: 1; min-width: 0; }
/* Keep name + level badge on one line; clamp the name so the badge never wraps. */
.comment-header .author-box name { flex-wrap: nowrap; }
.comment-header .author-box .aksrt-comment__author { max-width: 14em; }
.comment-header .aksrt-comment__like, .comment-header .aksrt-comment__like,
.comment-header .action-comment-like { .comment-header .action-comment-like {
margin-left: auto; display: inline-flex; align-items: center; gap: 4px; margin-left: auto; display: inline-flex; align-items: center; gap: 4px;
+12 -5
View File
@@ -754,15 +754,16 @@ body.dark-theme .comment-login-prompt__inner {
.medal-icon.img-icon, .medal-icon.img-icon,
.aksrt-identity-medal { .aksrt-identity-medal {
display: inline-block; display: inline-block;
width: 1.35em; width: auto;
height: 1.35em; height: 1.1em;
max-width: 6em;
object-fit: contain; object-fit: contain;
vertical-align: middle; vertical-align: -.15em;
margin-left: 4px; margin-left: 4px;
} }
.aksrt-identity-medal--comment { .aksrt-identity-medal--comment {
width: 1.15em; width: auto;
height: 1.15em; height: 1em;
} }
.aksrt-identity-medal.is-wear { .aksrt-identity-medal.is-wear {
outline: 1px solid rgba(245, 158, 11, 0.45); outline: 1px solid rgba(245, 158, 11, 0.45);
@@ -782,3 +783,9 @@ body.dark-theme .comment-login-prompt__inner {
align-items: center; align-items: center;
gap: 4px; gap: 4px;
} }
/* Inline name + level badge stay on one line; only the outer author-box wraps. */
.aksrt-comment__header name,
.aksrt-comment__header .author-box > name {
flex-wrap: nowrap;
white-space: nowrap;
}
@@ -210,6 +210,32 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: 4px; gap: 4px;
} }
/* Inline name + level badge in the dropdown profile row — one line, no wrap. */
.sub-user-box .user-right > .flex1 {
min-width: 0;
}
.sub-user-box .user-right b {
display: inline-flex;
align-items: center;
gap: 4px;
flex-wrap: nowrap;
white-space: nowrap;
max-width: 100%;
overflow: hidden;
}
.sub-user-box .user-right b .display-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
max-width: 100%;
}
.sub-user-box .user-right .aksrt-user-level-icon,
.sub-user-box .user-right .aksrt-identity-medal {
height: 1em;
vertical-align: -.12em;
flex: 0 0 auto;
}
/* User dropdown light-theme text contrast */ /* User dropdown light-theme text contrast */
.user-dropdown.sub-user-box, .user-dropdown.sub-user-box,
@@ -640,7 +640,7 @@ html.dark .post-drop-meta__item:hover {
.user-card.author .user-avatar { display: flex; justify-content: center; } .user-card.author .user-avatar { display: flex; justify-content: center; }
.user-card.author .user-avatar .avatar, .user-card.author .user-avatar .avatar,
.user-card.author .user-avatar img { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; } .user-card.author .user-avatar img { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; }
.user-card.author .user-name { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px; } .user-card.author .user-name { display: flex; flex-wrap: nowrap; align-items: center; justify-content: center; gap: 4px; }
.user-card.author .user-name .display-name { font-weight: 700; color: var(--key-color, inherit); } .user-card.author .user-name .display-name { font-weight: 700; color: var(--key-color, inherit); }
.user-card.author .user-desc { text-align: center; } .user-card.author .user-desc { text-align: center; }
.user-card.author .user-btns { text-align: center; } .user-card.author .user-btns { text-align: center; }
+7 -5
View File
@@ -277,7 +277,7 @@
} }
.article-avatar name.flex { .article-avatar name.flex {
gap: 4px; gap: 4px;
flex-wrap: wrap; flex-wrap: nowrap;
} }
.article-avatar .aksrt-vip-badge, .article-avatar .aksrt-vip-badge,
.article-avatar .aksrt-identity-chip { .article-avatar .aksrt-identity-chip {
@@ -286,11 +286,13 @@
vertical-align: middle; vertical-align: middle;
} }
.article-avatar .medal-icon.img-icon, .article-avatar .medal-icon.img-icon,
.article-avatar .aksrt-identity-medal { .article-avatar .aksrt-identity-medal,
width: 1.4em; .article-avatar .aksrt-user-level-icon {
height: 1.4em; width: auto;
height: 1.2em;
max-width: 6em;
object-fit: contain; object-fit: contain;
vertical-align: middle; vertical-align: -.15em;
margin-left: 4px; margin-left: 4px;
} }
.article-avatar .display-name { .article-avatar .display-name {
+1 -8
View File
@@ -535,14 +535,7 @@
color: #fff; color: #fff;
} }
.aksrt-user-level-icon { /* Level icon base sizing is centralized in assets/css/frontend/base.css. */
display: inline-block;
width: auto;
height: 1.15em;
max-width: 6em;
object-fit: contain;
vertical-align: -.18em;
}
/* 用户中心 layout 间距;双栏 / sticky 见 user-center/shell.css(body.aksrt-user-center)。 */ /* 用户中心 layout 间距;双栏 / sticky 见 user-center/shell.css(body.aksrt-user-center)。 */
.aksrt-profile__layout { .aksrt-profile__layout {
+3 -9
View File
@@ -1,12 +1,6 @@
/* User level — dedicated center panel */ /* User level — dedicated center panel.
.aksrt-user-level-icon { * Base level icon sizing lives in assets/css/frontend/base.css (1.1em, inline).
display: inline-block; * This file keeps only the UC-level-specific hero/road overrides + admin column. */
width: auto;
height: 1.35em;
max-width: 6em;
object-fit: contain;
vertical-align: -.2em;
}
.aksrt-user-level-icon--admin { height: 22px; } .aksrt-user-level-icon--admin { height: 22px; }
.uc-level-page { display: grid; gap: 16px; } .uc-level-page { display: grid; gap: 16px; }
+2 -2
View File
@@ -91,8 +91,8 @@ if (!function_exists('aksrt_header_user_badges_row')) :
if ($user_id <= 0 || !function_exists('aksrt_render_user_identity_chips')) { if ($user_id <= 0 || !function_exists('aksrt_render_user_identity_chips')) {
return ''; return '';
} }
// VIP already shown next to display name in the header profile row. // VIP + level already shown next to display name in the header profile row.
$chips = aksrt_render_user_identity_chips($user_id, 'card', ['include_vip' => false]); $chips = aksrt_render_user_identity_chips($user_id, 'card', ['include_vip' => false, 'skip_level' => true]);
if ($chips === '') { if ($chips === '') {
return ''; return '';
} }
+9 -2
View File
@@ -35,6 +35,7 @@ if (!function_exists('aksrt_render_user_identity_chips')) {
* Optional. Additional options. * Optional. Additional options.
* *
* @type bool $include_vip Whether to prepend VIP badge. Default true. * @type bool $include_vip Whether to prepend VIP badge. Default true.
* @type bool $skip_level Whether to omit the level chip (rendered inline elsewhere). Default false.
* } * }
*/ */
function aksrt_render_user_identity_chips(int $user_id, string $context = 'author', array $args = []): string { function aksrt_render_user_identity_chips(int $user_id, string $context = 'author', array $args = []): string {
@@ -43,6 +44,7 @@ if (!function_exists('aksrt_render_user_identity_chips')) {
} }
$args = wp_parse_args($args, [ $args = wp_parse_args($args, [
'include_vip' => true, 'include_vip' => true,
'skip_level' => false,
]); ]);
$html = ''; $html = '';
if (!empty($args['include_vip']) && function_exists('aksrt_render_vip_badge')) { if (!empty($args['include_vip']) && function_exists('aksrt_render_vip_badge')) {
@@ -63,12 +65,16 @@ if (!function_exists('aksrt_render_user_identity_chips')) {
if ($label === '') { if ($label === '') {
continue; continue;
} }
$type = sanitize_html_class((string) ($signal['type'] ?? ''));
// Level is rendered inline next to the name by the caller; skip it here.
if (!empty($args['skip_level']) && $type === 'level') {
continue;
}
$tone = sanitize_html_class((string) ($signal['tone'] ?? 'info')); $tone = sanitize_html_class((string) ($signal['tone'] ?? 'info'));
$icon = sanitize_html_class((string) ($signal['icon'] ?? 'check')); $icon = sanitize_html_class((string) ($signal['icon'] ?? 'check'));
$icon_url = esc_url((string) ($signal['icon_url'] ?? '')); $icon_url = esc_url((string) ($signal['icon_url'] ?? ''));
$title = (string) ($signal['title'] ?? $label); $title = (string) ($signal['title'] ?? $label);
$is_wear = !empty($signal['wear']); $is_wear = !empty($signal['wear']);
$type = sanitize_html_class((string) ($signal['type'] ?? ''));
// Image medal when dictionary provides icon_url. // Image medal when dictionary provides icon_url.
if ($icon_url !== '') { if ($icon_url !== '') {
@@ -162,7 +168,8 @@ if (!function_exists('aksrt_get_user_name')) {
if ($icon_url !== '') { if ($icon_url !== '') {
$level_html = '<img class="aksrt-user-level-icon img-icon ml3" src="' . $icon_url . '" alt="' . esc_attr($label) . '" title="' . esc_attr($title) . '" loading="lazy" decoding="async" />'; $level_html = '<img class="aksrt-user-level-icon img-icon ml3" src="' . $icon_url . '" alt="' . esc_attr($label) . '" title="' . esc_attr($title) . '" loading="lazy" decoding="async" />';
} else { } else {
$level_html = '<span class="badg badg-sm ml3 aksrt-identity-chip aksrt-identity-chip--info" title="' . esc_attr($title) . '"><i class="fa fa-layer-group" aria-hidden="true"></i> ' . esc_html($label) . '</span>'; // Text fallback mirrors zibll LV badge: small pill, never oversized.
$level_html = '<span class="badg badg-sm ml3 aksrt-user-level-badge" title="' . esc_attr($title) . '">' . esc_html($label) . '</span>';
} }
} elseif ($type === 'badge' && !empty($args['medal']) && $medal_html === '' && !empty($signal['wear'])) { } elseif ($type === 'badge' && !empty($args['medal']) && $medal_html === '' && !empty($signal['wear'])) {
if ($icon_url !== '') { if ($icon_url !== '') {
+5 -1
View File
@@ -151,6 +151,10 @@ if (!function_exists('aksrt_header_user_box_html')) :
$avatar = get_avatar($user_id, 44, '', esc_attr($display_name), ['loading' => 'lazy']); $avatar = get_avatar($user_id, 44, '', esc_attr($display_name), ['loading' => 'lazy']);
$desc = (string) ($ctx['user_desc'] ?? ''); $desc = (string) ($ctx['user_desc'] ?? '');
$vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($user_id, 'sm') : ''; $vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($user_id, 'sm') : '';
// Level icon rides inline to the right of the display name (zibll parity).
$level_badge = (class_exists('\AKSRT\User\Level') && method_exists('\AKSRT\User\Level', 'get_badge_html'))
? \AKSRT\User\Level::get_badge_html($user_id, 'aksrt-user-level-icon--header')
: '';
$payvip = function_exists('aksrt_header_payvip_bar') ? aksrt_header_payvip_bar($user_id) : ''; $payvip = function_exists('aksrt_header_payvip_bar') ? aksrt_header_payvip_bar($user_id) : '';
// nav_user_pay_vip gate: empty payvip when switch off for non-VIP CTA only — helper already handles VIP status. // nav_user_pay_vip gate: empty payvip when switch off for non-VIP CTA only — helper already handles VIP status.
if (empty($ctx['nav_user_pay_vip']) && $payvip !== '' && strpos($payvip, 'is-vip') === false) { if (empty($ctx['nav_user_pay_vip']) && $payvip !== '' && strpos($payvip, 'is-vip') === false) {
@@ -239,7 +243,7 @@ if (!function_exists('aksrt_header_user_box_html')) :
$con .= $avatar; $con .= $avatar;
$con .= '<div class="user-right flex flex1 ac jsb ml10">'; $con .= '<div class="user-right flex flex1 ac jsb ml10">';
$con .= '<div class="flex1" style="max-width: calc(100% - 40px);"><b>' $con .= '<div class="flex1" style="max-width: calc(100% - 40px);"><b>'
. esc_html($display_name) . $vip_badge . '<span class="display-name text-ellipsis">' . esc_html($display_name) . '</span>' . $vip_badge . $level_badge
. '</b><div class="px12 muted-2-color text-ellipsis">' . esc_html($desc) . '</div></div>'; . '</b><div class="px12 muted-2-color text-ellipsis">' . esc_html($desc) . '</div></div>';
$con .= '</div>'; $con .= '</div>';
$con .= $msg_icon; $con .= $msg_icon;
@@ -41,6 +41,10 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
if ($vip_badge) { if ($vip_badge) {
echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} }
// Level icon inline to the right of the name (zibll parity).
if (class_exists('\AKSRT\User\Level') && method_exists('\AKSRT\User\Level', 'get_badge_html')) {
echo \AKSRT\User\Level::get_badge_html((int) $user_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?> ?>
</h1> </h1>
<?php if ($target_user->description) : ?> <?php if ($target_user->description) : ?>
@@ -49,6 +53,12 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
<?php if (!empty($profile_signals)) : ?> <?php if (!empty($profile_signals)) : ?>
<div class="aksrt-identity-chips"> <div class="aksrt-identity-chips">
<?php foreach ($profile_signals as $signal) : ?> <?php foreach ($profile_signals as $signal) : ?>
<?php
// Level is rendered inline next to the name above; skip it here.
if ((string) ($signal['type'] ?? '') === 'level') {
continue;
}
?>
<span class="aksrt-identity-chip aksrt-identity-chip--<?php echo esc_attr($signal['tone']); ?>"> <span class="aksrt-identity-chip aksrt-identity-chip--<?php echo esc_attr($signal['tone']); ?>">
<i class="fas fa-<?php echo esc_attr($signal['icon']); ?>" aria-hidden="true"></i> <i class="fas fa-<?php echo esc_attr($signal['icon']); ?>" aria-hidden="true"></i>
<span><?php echo esc_html($signal['label']); ?></span> <span><?php echo esc_html($signal['label']); ?></span>
+8 -1
View File
@@ -98,6 +98,10 @@ $pm_url = ($pm_enabled && $messages_url !== '')
if ($vip_badge) { if ($vip_badge) {
echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} }
// Level icon inline to the right of the name (zibll parity).
if (class_exists('\AKSRT\User\Level') && method_exists('\AKSRT\User\Level', 'get_badge_html')) {
echo \AKSRT\User\Level::get_badge_html($user_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?> ?>
</h1> </h1>
<?php endif; ?> <?php endif; ?>
@@ -124,7 +128,10 @@ $pm_url = ($pm_enabled && $messages_url !== '')
<?php echo $city_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> <?php echo $city_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?> <?php endif; ?>
<?php if (function_exists('aksrt_render_user_identity_chips')) : ?> <?php if (function_exists('aksrt_render_user_identity_chips')) : ?>
<?php echo aksrt_render_user_identity_chips($user_id, 'author', ['include_vip' => false]); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> <?php
// Level already rendered inline next to the name above; skip it here.
echo aksrt_render_user_identity_chips($user_id, 'author', ['include_vip' => false, 'skip_level' => true]); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>