diff --git a/.ai/lessons.md b/.ai/lessons.md index 885025a087..d9365ef354 100644 --- a/.ai/lessons.md +++ b/.ai/lessons.md @@ -135,3 +135,6 @@ ## Session writes are last-writer-wins - Laravel saves the whole session payload at the end of every request, and pages run concurrent `wire:poll` requests. A value that one request changes can be overwritten by a request that started earlier. `KeepConcurrentTeamSwitch` protects `currentTeam`; protect any new session value that changes mid-session the same way, or keep it out of the session. + +## Keep `:has()` off page-wide ancestors +- A `:has()` on `html`, `body`, `.app-shell`, or a sibling of `
` (for example `html:has(.x) .y`) makes Chrome restyle the page on every DOM change. Chart libraries read layout many times per render, so one ApexCharts render took ~400 ms and blocked navigation. Use a class set by the Blade layout instead, and keep `:has()` on small, local subjects. diff --git a/resources/css/app.css b/resources/css/app.css index 38849b6e79..0674b14c48 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -6441,12 +6441,14 @@ html.dark.is-scrolled:not([data-nav-style="floating"]) .app-topbar > div:not(.ap background-image: none; } - html:not([data-nav-style="floating"]):has(.app-main-full :is([data-ec-root], .cv-root)) .app-topbar { + /* A layout class, not html:has(): an html-level :has() restyles the whole + page on every DOM change, which made chart renders take seconds. */ + html:not([data-nav-style="floating"]) .app-shell-canvas > .app-topbar { background-color: transparent; backdrop-filter: none; } - html:not([data-nav-style="floating"]):has(.app-main-full :is([data-ec-root], .cv-root)) .app-topbar > div:not(.app-topbar-brand) { + html:not([data-nav-style="floating"]) .app-shell-canvas > .app-topbar > div:not(.app-topbar-brand) { border-bottom-color: transparent; } } diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index a6807574eb..355618d79f 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -34,7 +34,7 @@ } }" @open-global-search.window="open = false" @page-width-changed.window="pageWidth = $event.detail" :style="{ '--sidebar-w': collapsed ? '4rem' : '14rem' }" x-cloak - @class(['app-shell dark:text-inherit text-black', 'flex h-dvh flex-col' => $fullBleed ?? false])> + @class(['app-shell dark:text-inherit text-black', 'app-shell-canvas flex h-dvh flex-col' => $fullBleed ?? false])> {{-- ============ DESKTOP TOP BAR ============ --}}
getComputedStyle(document.documentElement).getPropertyValue(name).trim(); const accent = () => cssVar('--chart-status-3xx') || '#3b82f6'; - const gridColor = () => cssVar('--chart-geo-empty') || 'rgba(128,128,128,0.15)'; + const compactNumber = new Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1 }); + const gridColor = () =>cssVar('--chart-geo-empty') || 'rgba(128,128,128,0.15)'; // `24h` buckets are hourly, `7d`/`30d` daily — pick a matching axis/tooltip format. const timeFormat = range => (range === '24h' ? 'HH:mm' : 'dd MMM'); @@ -84,7 +85,8 @@ minWidth: 28, maxWidth: 28, style: { colors: textColor }, - formatter: value => Math.round(value).toLocaleString(), + // Compact (1K, 1.2M) so large counts fit the fixed label width. + formatter: value => compactNumber.format(Math.round(value)), }, }, grid: {