Sticky save bar překryje sekci — funkčně OK, UX rozbité
import { Aside } from ‘@astrojs/starlight/components’;
Symptom
Sekce “Symptom”Formulář s několika sekcemi (např. „Firma”, „Fakturace a ceny”, „Sklad a příjem”, …) má dole sticky bottom-0 save bar přes celou šířku. Uživatel vidí:
- ✅ Horní sekce viditelné a použitelné
- ❌ Sekce v oblasti bottom baru zcela chybí — např. „Sklad a příjem” jen s hlavičkou, obsah nikde
- ✅ Save funguje (data se ukládají do DB — Playwright/API test zelený)
Uživatel si toggle / input v „skrytém” místě prostě nevšimne a nikdy ho nezapne. Bug reportovaný jako „nefunguje toggle X” — ale toggle funguje, jen ho není vidět.
Root cause
Sekce “Root cause”// Původní layout:<div className="space-y-5"> <Section title="Firma">…</Section> <Section title="Fakturace">…</Section> <Section title="Sklad a příjem">…</Section> {/* <— tahle se skryje */} <Section title="Zobrazení">…</Section> <Section title="Banka">…</Section>
{/* Sticky save bar */} <div className="sticky bottom-0 -mx-4 bg-white border-t px-4 py-3"> <button>Uložit</button> </div></div>Sticky element:
- Má
bg-white(neprůhledný) - Roste přes celou šířku (
-mx-4) - Je poslední child → sticky ho drží u dolního okraje viewportu
- Sekce nad ním, které padnou do jeho geometrické oblasti, jsou vizuálně pod ním (překryty)
Full-page screenshot to nezachytí, protože fixed/sticky ve full-page renderu se vykreslí na svém dokumentovém místě (uvnitř toku), ne na reálné viewport pozici při scrollu.
Fix
Sekce “Fix”Responsive FAB pattern — floating na desktopu vpravo dole, inline full-width na mobilu:
{/* Mobile: inline na konci formuláře, full-width */}<div className="flex items-center justify-end gap-3 pt-2 sm:hidden"> <button onClick={save} className="inline-flex w-full items-center justify-center gap-2 rounded-xl bg-blue-600 px-5 py-3 text-sm font-medium text-white"> Uložit </button></div>
{/* Desktop: fixed floating vpravo dole, kompaktní */}<div className="pointer-events-none fixed bottom-0 right-0 z-40 hidden p-6 sm:flex"> <div className="pointer-events-auto flex items-center gap-3 rounded-2xl border border-slate-200 bg-white/95 px-4 py-3 shadow-lg backdrop-blur-sm"> <button onClick={save}>Uložit</button> </div></div>Klíčové:
pointer-events-nonena wrapperu +pointer-events-autona inner boxu — click-through mimo tlačítkobg-white/95 backdrop-blur-sm— obsah pod je vidět, ale button je čitelnýhidden sm:flex/sm:hidden— bez JS detekce, pure Tailwind
Jak se tomu vyvarovat v jiných systémech
Sekce “Jak se tomu vyvarovat v jiných systémech”-
Detection preemptive:
- Grep pro
sticky bottom-0sbg-white|bg-slate-50|bg-slate-100|bg-neutral— pokud se najde, hrozí ten pattern. - Playwright screenshot test s
fullPage: false(viewport-only) po každém formulářovém deployi. - Test scroll:
page.evaluate(() => window.scrollTo(0, document.body.scrollHeight))+ screenshot — pozice fixed prvků.
- Grep pro
-
Anti-pattern:
- Sticky element s neprůhledným pozadím na full-width, který má výšku větší než padding-bottom parenta.
fullPage: truescreenshot jako jediný důkaz „vypadá to OK” — nezachytí layered fixed/sticky problémy.- Testy jen na desktopu 1920px — mobil má jiné breakpointy a jiné layouts.
-
Lepší přístupy:
- Floating Action Button (FAB) v rohu (Material Design pattern) — nezasahuje do toku
- Sticky bar s
padding-bottomroven výšce baru na parentu → obsah nikdy nepadne pod něj - Sticky bar s
backdrop-blur+bg-white/80→ obsah pod ním prosvítá, uživatel ví že tam něco je
-
E2E test:
await page.setViewportSize({ width: 1440, height: 900 });await page.goto("/formular");await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));await page.screenshot({ fullPage: false, path: "form-scrolled.png" });// Manuální revize screenshotu — každá sekce viditelná?
Sister bugs / související
Sekce “Sister bugs / související”deploy/build-passes-locally-lint-fails-production— jiná past kdy funkční test projde ale prod je rozbitý.