Skip to content

default-theme: SSR always renders the desktop sidebars — visible flash/fade-out of sidenav and TOC on viewports ≤ 1100px #160

Description

@daveycodez

Summary

Because mobileLayout() is false during SSR and only flips on the client (src/default-theme/globals.ts), the server always renders the desktop layout. On viewports ≤ 1100px, a full page load therefore paints both desktop sidebars — the sidenav <aside> from Layout.tsx and the table-of-contents <aside> from components/Article.jsx — which are then removed by JS once the signal flips. The result is a visible flash/fade of both sidebars on every mobile refresh.

Related to #152 (same mobileLayout machinery), but a separate symptom: #152 is about the mid-hydration flip crashing hydration; this one is a pure FOUC that persists even with #152's suggested fix applied — deferring the initial flip to the window load event actually makes the flash longer, since the stale desktop layout stays up until load.

What the user sees (≤ 1100px, hard reload)

  1. Sidenav: Layout.tsx SSRs the desktop branch of the Show (<aside class={styles.sidenav}>). At ≤ 1100px, Layout.module.css styles .sidenav as position: fixed; z-index: 51; width: min(20rem, 70dvw); background: …; animation: contentHide 300ms ease-out forwards — those rules are written for the mobile Dialog.Content, but they match the SSR'd desktop aside too. So the aside briefly renders as an open drawer overlaying the page and plays the contentHide slide-out animation on load. When mobileLayout() later flips, the Show swaps to the (closed) Dialog branch and the element disappears from the DOM.
  2. Table of contents: Article.jsx renders it behind <Show when={!mobileLayout() && …}>, and Article.module.css has no mobile rule for .aside at all — so the TOC renders in-flow at phone widths and vanishes when the signal flips.

At > 1100px nothing changes visually, so the bug only shows on narrow windows — easy to miss in desktop-first dev.

Reproduction

  • @kobalte/solidbase 0.6.9 (also current pkg.pr.newLayout.tsx / Article.jsx unchanged), default theme, any docs page with a sidebar + TOC.
  • Emulate iPhone SE (375×667) or any window ≤ 1100px and hard-reload.
  • Observe the sidenav drawer flash + slide out, and the TOC render then disappear.

Suggested fix

Hide the SSR'd desktop elements with the same media query CSS already keys off, so they never paint at mobile widths and the later JS removal is invisible:

/* Layout.module.css */
@media screen and (max-width: 1100px) {
	aside.sidenav {
		display: none;
	}
}

/* Article.module.css */
@media screen and (max-width: 1100px) {
	.aside {
		display: none;
	}
}

The aside element selector is enough to keep the mobile drawer working — the drawer is Dialog.Content, which renders a div. (Alternatively give the desktop aside its own class so the two stop sharing .sidenav rules — that sharing is also what causes the stray contentHide animation in step 1 above.)

A deeper fix — same note as in #152 — would be to render markup that's valid for both widths and gate visibility entirely in CSS rather than swapping Show branches on a client-only signal; the CSS above stays within the current architecture.

I've verified the equivalent CSS downstream (in our docs app, layered above the theme styles): at 375×667 both asides are display: none from first paint, the drawer still opens fine, and desktop is unaffected. Happy to open a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions