Replaced simple breadcrumb dropdown with the full sidebar-style
site picker (search, favicons, name+domain, add new site, animation).
Removed SitePicker from sidebar and cleaned up unused props.
Three nav groups in home mode:
- Your Sites: each site with favicon, Add New Site
- Workspace: Integrations, Pricing, Workspace Settings
- Resources: Documentation (external link)
Same styling as site dashboard sidebar nav items.
Home page now uses the same sidebar layout as dashboard pages.
Sidebar shows simplified home mode (logo, app switcher, profile)
without site-specific nav groups. Stat cards removed — plan info
lives in settings, site count is self-evident from the list.
- New SidebarProvider context for shared collapse state
- ContentHeader visible on desktop: collapse icon left, "Live" right
- Collapse button removed from sidebar bottom (moved to header)
- Keyboard shortcut [ handled by context, not sidebar
- Realtime indicator polls every 5s, ticks every 1s for freshness
Shell now has the glass treatment so sidebar and surrounding area
are one seamless surface. No more visible line between sidebar
and content panel. Desktop sidebar is transparent over the shell.
Mobile sidebar keeps its own glass since it overlays independently.
No longer expands sidebar first. When collapsed, dropdown appears
to the right of the button (like AppLauncher/UserMenu/Notifications).
When expanded, opens below the button.
Dropdown now uses createPortal + fixed positioning like AppLauncher,
UserMenu and NotificationCenter. Renders over page content instead
of over the glass sidebar, so /65 opacity looks correct.
Use opacity instead of bg-color swap for proper transition-opacity
animation on mobile backdrop. Add shimmer gradient to the sidebar
loading placeholder in DashboardShell.
Convert the sidebarContent(isMobile) closure function to a proper
SidebarContent component with explicit props, enabling correct React
reconciliation for both desktop and mobile sidebar instances.
The share/[id] layout is a server component that imported FAVICON_SERVICE_URL
from icons.tsx, pulling in the entire React icon registry and triggering
createContext on the server. Moved the constant to its own favicon.ts module.
- PageSpeed API client with types for config, checks, and audits
- SWR hooks: usePageSpeedConfig, usePageSpeedLatest, usePageSpeedHistory
- GaugeIcon added to sidebar under Infrastructure group
- Add relative z-10 to sidebar aside to fix dropdowns (AppSwitcher,
Notifications, UserMenu) rendering behind content area due to
backdrop-blur-xl creating a stacking context
- Move Site Settings from bottom utility section into Infrastructure
nav group where it logically belongs
- Remove ThemeToggle from sidebar (available in user settings)
- Rename Settings to Site Settings for clarity
Use variant='sidebar' for ThemeToggle, NotificationCenter, and
compact UserMenu so they render with the same icon+label layout
as nav items. Fixed dropdown positioning uses fixed to escape
sidebar overflow:hidden.
Use left-aligned rows with fading labels for theme, notifications,
and profile — matching the nav items pattern. Fix app switcher
alignment at top to match logo row.
Move theme toggle, notifications, app switcher, and user profile from
the top header bar into the sidebar. App switcher at the top (scope
switch), utilities at the bottom. Header now only shows on mobile for
the hamburger menu.
The sidebar now uses next/dynamic with ssr:false, meaning it renders
NOTHING in the server HTML. No DOM content = no possible flash of
"Ci" or any text during SSR-to-hydration gap. The sidebar only mounts
on the client where localStorage is immediately available, so
collapsed state is correct from the very first render.
Previous opacity-0 approach still rendered DOM content which could
flash during SSR hydration. Now render an empty div (just border +
background, no content) at collapsed width until useEffect fires.
Then swap in the real aside with content. Zero DOM content = zero
possible flash of text or icons.
The sidebar is now invisible (opacity-0) on the initial render.
In a single useEffect: read collapsed state from localStorage,
then requestAnimationFrame to reveal (opacity-1). React batches
the state update with the reveal, so the sidebar appears at its
correct width with correct label visibility in one frame. No
intermediate states, no hydration mismatch, no transitions on load.
Root cause 1: hydration mismatch — SSR rendered collapsed=true but
client useState initializer read localStorage synchronously, causing
an immediate state change and visual flash. Fix: always initialize
collapsed=true, read localStorage in useEffect so the transition
is smooth (collapsed→expanded animates cleanly).
Root cause 2: three-phase badge rendering (skeleton→letter→favicon)
caused visible state changes. Fix: just show the empty orange badge
until the favicon arrives. No skeleton, no letter fallback. One state
transition: empty→favicon.
When clicking the site picker in collapsed mode, the sidebar expands
and opens the dropdown. After selecting a site or clicking outside,
the sidebar re-collapses to its previous state.
Fix "Ci" flash on reload: default collapsed to true on SSR and when
no localStorage value exists, preventing hydration mismatch where
labels briefly render at full opacity in the narrow sidebar.
Use Google's favicon service to display the site's actual favicon
instead of the first-letter initial. Falls back to the letter if
the favicon fails to load. Matches the site list dashboard behavior.
Every interactive item (logo, site picker, nav links, settings,
collapse) now wraps its icon in a 28px flex container. Combined with
consistent px-2 outer + px-2.5 inner padding, all icon containers
start at exactly 18px from the sidebar edge and center at 32px — the
midpoint of the 64px collapsed sidebar.
Reduce all sidebar section outer padding from px-3 to px-2 so hover
backgrounds are wider and items center properly in the 64px collapsed
width. All sections now consistent: px-2 outer + px-2.5 inner.