fix(sidebar): collapse empty sidebar column on every scene

Sidebar visibility had two disconnected decisions: the body site-layout-*
class (via aksrt_is_show_sidebar_filter) only checked widget activity for
singles, while the homepage aside renderer skipped the column and
sidebar.php rendered an empty one. With the toggle on and no widgets, the
homepage kept a reserved gutter and squeezed the main content aside.

- collapse every scene to no-sidebar when the sidebar widget stack is empty
- resolve home_sidebar before the page branch so a static front page is
  checked against the zones the homepage renders
- guard the homepage aside with aksrt_is_show_sidebar() alone and drop the
  duplicate aksrt_homepage_has_sidebar gate
- early-return sidebar.php instead of rendering an empty column
- drop dead code: archive helper $preferred param, wp_is_mobile fallback,
  container--no-sidebar class, unreachable front-page fallback
- update post-visual-contract to pin the single visibility decision

Verification: php -l on all changed files; node --test
tests/widget-system/*.test.cjs 242/243 (the toast-contract failure is
pre-existing and unrelated).
This commit is contained in:
2026-09-05 15:40:11 +08:00
parent 2bf9b00022
commit 1839150ecb
10 changed files with 64 additions and 70 deletions
@@ -356,14 +356,42 @@ test('the pinned visual baseline records all source checksums', () => {
}
});
test('single posts reserve a sidebar column only when its widget stack has content', () => {
test('every scene reserves a sidebar column only when its widget stack has content', () => {
const layout = read('inc/helpers/template-tags/site-layout.php');
const single = read('inc/helpers/aksrt-single.php');
const aside = read('inc/helpers/aksrt-single-aside.php');
const homepage = read('inc/helpers/homepage/content.php');
const sidebarTemplate = read('sidebar.php');
const archiveHelpers = read('inc/helpers/aksrt-archive.php');
// Single visibility decision: an empty widget stack collapses every scene
// (home/category/tag/search/page/archive) to no-sidebar, not just singles.
assert.match(layout, /if \(!aksrt_has_current_sidebar_widgets\(\)\) \{\s*return false;/);
assert.doesNotMatch(layout, /is_single\(\) && !aksrt_has_current_sidebar_widgets/);
// The resolver must answer home_sidebar before the is_page() branch so a
// static front page is checked against the zones the homepage renders.
const resolver = layout.match(/function aksrt_resolve_sidebar_id\(\): string \{[\s\S]*?\n\}/);
assert.ok(resolver, 'aksrt_resolve_sidebar_id should exist');
assert.ok(
resolver[0].indexOf('is_home() || is_front_page()') < resolver[0].indexOf('is_page()'),
'home/front_page branch must precede the is_page() branch'
);
assert.match(layout, /if \(is_single\(\) && !aksrt_has_current_sidebar_widgets\(\)\) \{\s*return false;/);
assert.match(single, /\$show_aside = get_post_type\(\$post_id\) === 'post'[\s\S]*?&& aksrt_is_show_sidebar\(\);/);
assert.match(aside, /get_post_type\(\$post_id\) !== 'post' \|\| empty\(\$ctx\['show_aside'\]\)/);
// Homepage aside renders under the same single decision; no dual gate.
assert.match(homepage, /function aksrt_homepage_render_sidebar\(\): void \{\s*if \(!aksrt_is_show_sidebar\(\)\) \{\s*return;/);
assert.doesNotMatch(homepage, /aksrt_homepage_has_sidebar/);
// Generic sidebar template early-returns instead of rendering an empty column.
assert.match(sidebarTemplate, /if \(!function_exists\('aksrt_is_show_sidebar'\) \|\| !aksrt_is_show_sidebar\(\)\) \{\s*return;/);
assert.doesNotMatch(sidebarTemplate, /legacy parity/);
// Archive helper takes only the page-level toggle; no dead params.
assert.match(archiveHelpers, /function aksrt_archive_has_sidebar\(bool \$enabled = true\): bool/);
assert.doesNotMatch(archiveHelpers, /preferred|wp_is_mobile/);
});
test('article visual matrix pins every breakpoint, state and strict threshold', () => {