fix(ui): render user level icon inline beside the name
The level (等级) badge was wrapping to its own line below the username and rendering at the intrinsic PNG size in most surfaces. Centralize the badge sizing after zibll's `.img-icon` (height: 1.1em, inline-block) in base.css so every route gets the same compact inline badge, and keep the name row from flex-wrapping. - Add shared `.aksrt-user-level-icon / .aksrt-identity-medal` rule + nowrap name rows in base.css (loaded on all routes). - Header dropdown: append the level icon next to the display name (was a separate badges row); constrain the name to ellipsis so the badge never wraps. - User center profile header & author hero: render the level icon inside the <h1> name row; drop the duplicate level chip from the signals row. - Single article author bar / after-box author card: nowrap the name row and size the level icon to 1.2em (was unstyled → intrinsic PNG size). - Comments: inner <name> nowrap with a name clamp; medal sized to 1em. - Consolidate the per-route `.aksrt-user-level-icon` duplicates in user-center/level.css and profile/layout.css onto the shared rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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 {
|
||||
padding: 48px 20px;
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
.comment-header .author-box { display: flex; align-items: center; gap: 8px; width: 100%; }
|
||||
.comment-header .author-box name,
|
||||
.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 .action-comment-like {
|
||||
margin-left: auto; display: inline-flex; align-items: center; gap: 4px;
|
||||
|
||||
@@ -754,15 +754,16 @@ body.dark-theme .comment-login-prompt__inner {
|
||||
.medal-icon.img-icon,
|
||||
.aksrt-identity-medal {
|
||||
display: inline-block;
|
||||
width: 1.35em;
|
||||
height: 1.35em;
|
||||
width: auto;
|
||||
height: 1.1em;
|
||||
max-width: 6em;
|
||||
object-fit: contain;
|
||||
vertical-align: middle;
|
||||
vertical-align: -.15em;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.aksrt-identity-medal--comment {
|
||||
width: 1.15em;
|
||||
height: 1.15em;
|
||||
width: auto;
|
||||
height: 1em;
|
||||
}
|
||||
.aksrt-identity-medal.is-wear {
|
||||
outline: 1px solid rgba(245, 158, 11, 0.45);
|
||||
@@ -782,3 +783,9 @@ body.dark-theme .comment-login-prompt__inner {
|
||||
align-items: center;
|
||||
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;
|
||||
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.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 .avatar,
|
||||
.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-desc { text-align: center; }
|
||||
.user-card.author .user-btns { text-align: center; }
|
||||
|
||||
@@ -277,7 +277,7 @@
|
||||
}
|
||||
.article-avatar name.flex {
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.article-avatar .aksrt-vip-badge,
|
||||
.article-avatar .aksrt-identity-chip {
|
||||
@@ -286,11 +286,13 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
.article-avatar .medal-icon.img-icon,
|
||||
.article-avatar .aksrt-identity-medal {
|
||||
width: 1.4em;
|
||||
height: 1.4em;
|
||||
.article-avatar .aksrt-identity-medal,
|
||||
.article-avatar .aksrt-user-level-icon {
|
||||
width: auto;
|
||||
height: 1.2em;
|
||||
max-width: 6em;
|
||||
object-fit: contain;
|
||||
vertical-align: middle;
|
||||
vertical-align: -.15em;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.article-avatar .display-name {
|
||||
|
||||
@@ -535,14 +535,7 @@
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.aksrt-user-level-icon {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 1.15em;
|
||||
max-width: 6em;
|
||||
object-fit: contain;
|
||||
vertical-align: -.18em;
|
||||
}
|
||||
/* Level icon base sizing is centralized in assets/css/frontend/base.css. */
|
||||
|
||||
/* 用户中心 layout 间距;双栏 / sticky 见 user-center/shell.css(body.aksrt-user-center)。 */
|
||||
.aksrt-profile__layout {
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
/* User level — dedicated center panel */
|
||||
.aksrt-user-level-icon {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 1.35em;
|
||||
max-width: 6em;
|
||||
object-fit: contain;
|
||||
vertical-align: -.2em;
|
||||
}
|
||||
/* User level — dedicated center panel.
|
||||
* Base level icon sizing lives in assets/css/frontend/base.css (1.1em, inline).
|
||||
* This file keeps only the UC-level-specific hero/road overrides + admin column. */
|
||||
.aksrt-user-level-icon--admin { height: 22px; }
|
||||
|
||||
.uc-level-page { display: grid; gap: 16px; }
|
||||
|
||||
@@ -91,8 +91,8 @@ if (!function_exists('aksrt_header_user_badges_row')) :
|
||||
if ($user_id <= 0 || !function_exists('aksrt_render_user_identity_chips')) {
|
||||
return '';
|
||||
}
|
||||
// VIP already shown next to display name in the header profile row.
|
||||
$chips = aksrt_render_user_identity_chips($user_id, 'card', ['include_vip' => false]);
|
||||
// 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, 'skip_level' => true]);
|
||||
if ($chips === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ if (!function_exists('aksrt_render_user_identity_chips')) {
|
||||
* Optional. Additional options.
|
||||
*
|
||||
* @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 {
|
||||
@@ -43,6 +44,7 @@ if (!function_exists('aksrt_render_user_identity_chips')) {
|
||||
}
|
||||
$args = wp_parse_args($args, [
|
||||
'include_vip' => true,
|
||||
'skip_level' => false,
|
||||
]);
|
||||
$html = '';
|
||||
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 === '') {
|
||||
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'));
|
||||
$icon = sanitize_html_class((string) ($signal['icon'] ?? 'check'));
|
||||
$icon_url = esc_url((string) ($signal['icon_url'] ?? ''));
|
||||
$title = (string) ($signal['title'] ?? $label);
|
||||
$is_wear = !empty($signal['wear']);
|
||||
$type = sanitize_html_class((string) ($signal['type'] ?? ''));
|
||||
|
||||
// Image medal when dictionary provides icon_url.
|
||||
if ($icon_url !== '') {
|
||||
@@ -162,7 +168,8 @@ if (!function_exists('aksrt_get_user_name')) {
|
||||
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" />';
|
||||
} 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'])) {
|
||||
if ($icon_url !== '') {
|
||||
|
||||
@@ -151,6 +151,10 @@ if (!function_exists('aksrt_header_user_box_html')) :
|
||||
$avatar = get_avatar($user_id, 44, '', esc_attr($display_name), ['loading' => 'lazy']);
|
||||
$desc = (string) ($ctx['user_desc'] ?? '');
|
||||
$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) : '';
|
||||
// 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) {
|
||||
@@ -239,7 +243,7 @@ if (!function_exists('aksrt_header_user_box_html')) :
|
||||
$con .= $avatar;
|
||||
$con .= '<div class="user-right flex flex1 ac jsb ml10">';
|
||||
$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>';
|
||||
$con .= '</div>';
|
||||
$con .= $msg_icon;
|
||||
|
||||
@@ -41,6 +41,10 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
|
||||
if ($vip_badge) {
|
||||
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>
|
||||
<?php if ($target_user->description) : ?>
|
||||
@@ -49,6 +53,12 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
|
||||
<?php if (!empty($profile_signals)) : ?>
|
||||
<div class="aksrt-identity-chips">
|
||||
<?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']); ?>">
|
||||
<i class="fas fa-<?php echo esc_attr($signal['icon']); ?>" aria-hidden="true"></i>
|
||||
<span><?php echo esc_html($signal['label']); ?></span>
|
||||
|
||||
@@ -98,6 +98,10 @@ $pm_url = ($pm_enabled && $messages_url !== '')
|
||||
if ($vip_badge) {
|
||||
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>
|
||||
<?php endif; ?>
|
||||
@@ -124,7 +128,10 @@ $pm_url = ($pm_enabled && $messages_url !== '')
|
||||
<?php echo $city_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<?php endif; ?>
|
||||
<?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; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
Reference in New Issue
Block a user