fix(frontend): keep user center and user home avatar frames rounded

The two large hero avatar frames go back to their legacy rounded-square look: the user center profile header keeps 6px and the public user home hero keeps var(--radius). Both declare an explicit radius because the global avatar primitive (img.avatar, 0,1,1) ties them on specificity and only the later route rule wins the cascade.

Everything else stays: the primitive still circles the bare avatar-mini in the ban and report modals, the VIP modal avatar and the post-card meta avatar, the friends page avatar is circular, and the VIP icon still leads the badge row. The contract test now guards the two hero frames as rounded-square so the primitive cannot silently re-circularize them.
This commit is contained in:
2026-10-10 13:59:47 +08:00
parent 9cb5e7bea3
commit a3b708f9d4
3 changed files with 23 additions and 13 deletions
@@ -8,10 +8,11 @@ const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
// --radius-avatar is the single source of truth for frontend avatar shape.
// Route CSS loads after the public bundle, and `.uc-header__avatar img` /
// `.aksrt-author-hero__avatar img` tie the primitive at 0,1,1 specificity,
// so every framed avatar must opt in explicitly.
// --radius-avatar is the single source of truth for frontend avatar shape,
// except the two large hero frames on the user center and the public user
// home, which intentionally keep their legacy rounded-square look. Route CSS
// loads after the public bundle and ties the primitive at 0,1,1 specificity,
// so each frame must declare its own radius to win the cascade.
const primitiveSelectors = [
'img.avatar,',
'img.avatar-mini,',
@@ -37,18 +38,23 @@ test('the global avatar shape primitive renders every avatar image circular', ()
assert.ok(sizeSystem >= 0 && sizeSystem < start, 'primitive must sit after the avatar size system');
});
test('user center and public user home avatar frames are circular', () => {
test('the user center and public user home keep their legacy rounded-square avatar frames', () => {
const profile = read('assets/css/routes/profile/layout.css');
const userHome = read('assets/css/routes/user-home.css');
const friends = read('assets/css/routes/friends.css');
assert.match(profile, /\.uc-header__avatar\s*\{[^}]*border-radius:\s*var\(--radius-avatar,\s*50%\)/s);
assert.doesNotMatch(profile, /\.uc-header__avatar\s*\{[^}]*border-radius:\s*6px/s);
// Both frames must keep an explicit radius: the global primitive ties them at
// 0,1,1 specificity, so only the later route rule keeps them square.
assert.match(profile, /\.uc-header__avatar\s*\{[^}]*border-radius:\s*6px/s);
assert.doesNotMatch(profile, /\.uc-header__avatar\s*\{[^}]*--radius-avatar/s);
// img and the upload overlay both inherit the frame radius.
assert.match(profile, /\.uc-header__avatar img\s*\{[^}]*border-radius:\s*inherit/s);
assert.match(userHome, /\.aksrt-author-hero__avatar img\s*\{[^}]*border-radius:\s*var\(--radius-avatar,\s*50%\)/s);
assert.doesNotMatch(userHome, /\.aksrt-author-hero__avatar img\s*\{[^}]*border-radius:\s*var\(--radius\)/s);
assert.match(userHome, /\.aksrt-author-hero__avatar img\s*\{[^}]*border-radius:\s*var\(--radius\)/s);
assert.doesNotMatch(userHome, /\.aksrt-author-hero__avatar img\s*\{[^}]*--radius-avatar/s);
});
test('every other frontend avatar frame is circular', () => {
const friends = read('assets/css/routes/friends.css');
assert.match(friends, /\.friend-card__avatar\s*\{[^}]*border-radius:\s*var\(--radius-avatar,\s*50%\)/s);
assert.doesNotMatch(friends, /\.friend-card__avatar\s*\{[^}]*border-radius:\s*var\(--radius\)/s);