refactor(layout): migrate 3 floating popovers to .glass-panel-strong (Brief 3) #124

Merged
varutasu merged 1 commit from brief-3-floating-popovers into main 2026-06-04 17:25:14 -04:00
3 changed files with 53 additions and 14 deletions

View file

@ -87,12 +87,10 @@ function UserProfileDropdown({ user, onMobileMenuClose }) {
{/* Menu — Liquid Glass popover, high-tint w/ ember-subtle rim. */} {/* Menu — Liquid Glass popover, high-tint w/ ember-subtle rim. */}
<div <div
className="absolute bottom-full left-0 right-0 mb-2 rounded-xl z-20" className="glass-panel-strong absolute bottom-full left-0 right-0 mb-2 rounded-xl z-20"
style={{ style={{
background: 'var(--glass-surface-high)', boxShadow:
backdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))', 'var(--rim-light-inner), var(--ember-rim-subtle), var(--elevation-ambient)',
WebkitBackdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
boxShadow: 'var(--rim-light-inner), var(--ember-rim-subtle), var(--elevation-ambient)',
}} }}
> >
<div className="py-2"> <div className="py-2">
@ -693,14 +691,12 @@ export default function Layout({ children, user = null, showSearch = false }) {
{/* Mobile Menu Drawer - Slides in from left when "More" is tapped */} {/* Mobile Menu Drawer - Slides in from left when "More" is tapped */}
<div <div
className={` className={`
md:hidden fixed inset-y-0 left-0 z-50 w-64 transform transition-transform duration-300 ease-in-out glass-panel-strong md:hidden fixed inset-y-0 left-0 z-50 w-64 transform transition-transform duration-300 ease-in-out
${isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full'} ${isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full'}
`} `}
style={{ style={{
background: 'var(--glass-surface-mid)', boxShadow:
backdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))', 'var(--rim-light-inner), var(--rim-light-outer), var(--elevation-pronounced)',
WebkitBackdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
boxShadow: 'var(--rim-light-inner), var(--rim-light-outer), var(--elevation-pronounced)',
}} }}
> >
<div className="p-4 h-full flex flex-col"> <div className="p-4 h-full flex flex-col">

View file

@ -211,11 +211,8 @@ function UserMenu({ user }) {
<div <div
role="menu" role="menu"
aria-label="Account menu" aria-label="Account menu"
className="absolute right-0 top-full mt-2 w-56 rounded-xl z-30 overflow-hidden" className="glass-panel-strong absolute right-0 top-full mt-2 w-56 rounded-xl z-30 overflow-hidden"
style={{ style={{
background: 'var(--glass-surface-high)',
backdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
WebkitBackdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
boxShadow: boxShadow:
'var(--rim-light-inner), var(--ember-rim-subtle), var(--elevation-pronounced)', 'var(--rim-light-inner), var(--ember-rim-subtle), var(--elevation-pronounced)',
}} }}

View file

@ -88,3 +88,49 @@ describe('Layout — logged-out rendering (regression: P0 #7)', () => {
expect(container.textContent).not.toContain('Guest'); expect(container.textContent).not.toContain('Guest');
}); });
}); });
describe('Layout — floating popovers use .glass-panel-strong (regression: Brief 3 of unify-glass-panel-surfaces)', () => {
afterEach(() => cleanup());
// The sidebar UserProfileDropdown and the mobile drawer are floating
// glass surfaces. Brief 3 migrated them from inline
// `background: var(--glass-surface-*)` + `backdropFilter` to the
// canonical `.glass-panel-strong` className. If a future PR reverts
// either to the inline pattern, the bespoke-glass-surface CI gate
// (Brief 7) will catch the regression — these unit tests catch the
// SAME regression at the component-render level, before the gate
// would fire.
it('mobile drawer panel carries .glass-panel-strong when isMobileMenuOpen', () => {
const { container } = render(
<Layout user={{ email: 'foo@bar.com', role: 'user' }}>page body</Layout>
);
// The drawer is rendered (translated off-screen via `-translate-x-full`)
// even when closed, so we can assert on it from a default mount.
const drawer = container.querySelector(
'.glass-panel-strong.md\\:hidden.fixed.inset-y-0.left-0'
);
expect(drawer).not.toBeNull();
expect(drawer.className).toContain('glass-panel-strong');
expect(drawer.className).toContain('w-64');
});
it('mobile drawer panel does NOT carry an inline `background: var(--glass-surface-*)` style after Brief 3', () => {
// Regression lock against reverting Brief 3 to handrolled inline
// glass styles on the mobile drawer specifically. The drawer is
// always in the DOM (translated off-screen when closed), so we can
// query it from a default mount.
const { container } = render(
<Layout user={{ email: 'foo@bar.com', role: 'user' }}>page body</Layout>
);
const drawer = container.querySelector(
'.glass-panel-strong.md\\:hidden.fixed.inset-y-0.left-0'
);
expect(drawer).not.toBeNull();
const inlineStyle = drawer.getAttribute('style') ?? '';
expect(inlineStyle).not.toMatch(/var\(--glass-surface-/);
expect(inlineStyle).not.toMatch(/backdrop-filter/i);
expect(inlineStyle).toContain('box-shadow');
expect(inlineStyle).toContain('--elevation-pronounced');
});
});