rastrillo / fiesta Public

Draw the sample's pane divider only between panes that show

.site-pane + .site-pane still matches a pane whose neighbour site.js
has hidden: a hidden element is a sibling all the same. So every pane
after Overview, shown alone, opened with a 24px gap, 20px of padding
and a rule, an empty band above its heading that Overview did not
have. The divider is for the page with no script, where every pane
shows one under the next, so it now names a shown pane on both sides
and keeps that page as it was.

The new browser test shows each pane alone at 1280 and 390 and holds
both its offset down the column and its heading's offset inside it to
Overview's: a margin moves the first and padding the second. Against
the old rule it failed for all four panes at both widths (40px down
the column with the heading 21px in, against Overview's 16 and 0). It
passes now, beside TestTheLandingReadsWithNoScript and
TestEverySamplePaneAndCourseSwitches. Its weight is a guess from its
docs-t1 time, level with the pane-switching test's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 640a5ad75ce81dbad937c3a3dedc1f0167aa1a3e parent 206bc43
3 files changed, +51 −1
  • hack/test-weights.txt +1 −0
  • internal/www/landing_browser_test.go +44 −0
  • site/static/site.css +6 −1
diff --git a/hack/test-weights.txt b/hack/test-weights.txt
index 4fd6d11..059b1fc 100644
--- a/hack/test-weights.txt
+++ b/hack/test-weights.txt
@@ -207,6 +207,7 @@ browser internal/www TestTheLandingReadsWithNoScript 8
browser internal/www TestTheSiteServesFromItsOwnOrigin 8
browser internal/www TestReducedMotionIgnoresScrolling 7
browser internal/www TestEverySamplePaneAndCourseSwitches 7
+browser internal/www TestEverySamplePaneStartsLikeTheFirst 7
browser internal/www TestTheBuntingFollowsTheTheme 7
browser internal/www TestTheSampleSwitchDrags 7
browser internal/www TestTheStillBuntingMatchesTheMovingOne 7
diff --git a/internal/www/landing_browser_test.go b/internal/www/landing_browser_test.go
index 071c725..3fa9705 100644
--- a/internal/www/landing_browser_test.go
+++ b/internal/www/landing_browser_test.go
@@ -724,6 +724,50 @@ func TestEverySamplePaneAndCourseSwitches(t *testing.T) {
rig.Screen("#pane-guests", "the Guests pane")
}
+// paneTopJS measures the pane that is showing: where it starts below
+// the top of the column that holds every pane, and where its heading
+// starts below the pane's own top. Both, because a gap above a pane
+// can be its margin, outside its box, or its padding, inside it.
+const paneTopJS = `(() => { const p = [...document.querySelectorAll("#sample .site-pane")].find(p => p.getClientRects().length > 0), c = p.parentElement.getBoundingClientRect(), r = p.getBoundingClientRect(), h = p.querySelector("h2").getBoundingClientRect();
+ return JSON.stringify({ID: p.id, Pane: r.top - c.top, Heading: h.top - r.top}); })()`
+
+// TestEverySamplePaneStartsLikeTheFirst: each sample pane, shown on its
+// own, starts its heading as high in the window as Overview does, on a
+// desktop and on a phone. A divider written between neighbouring panes
+// still matches when the pane before is hidden, which drew a gap and a
+// rule above every pane but the first.
+func TestEverySamplePaneStartsLikeTheFirst(t *testing.T) {
+ rig, _ := siteRig(t)
+ ctx, cancel := context.WithTimeout(rig.Context(), 2*time.Minute)
+ defer cancel()
+ type top struct {
+ ID string
+ Pane, Heading float64
+ }
+ for _, w := range []int64{1280, 390} {
+ open(t, ctx, rig, w, 900)
+ mustRun(t, ctx, soon(".site-themebar"))
+ var first top
+ at(t, ctx, paneTopJS, &first)
+ if first.ID != "pane-overview" {
+ t.Fatalf("at %d the sample opens on %s; want pane-overview", w, first.ID)
+ }
+ for _, id := range []string{"pane-guests", "pane-menu", "pane-music", "pane-settings"} {
+ showPane(t, ctx, id)
+ var got top
+ at(t, ctx, paneTopJS, &got)
+ if got.ID != id {
+ t.Fatalf("at %d, after pressing %s the sample shows %s", w, id, got.ID)
+ }
+ if math.Abs(got.Pane-first.Pane) > 0.5 || math.Abs(got.Heading-first.Heading) > 0.5 {
+ t.Errorf("at %d, %s starts %.0fpx down its column with its heading %.0fpx below that; Overview starts at %.0f with its heading at %.0f",
+ w, id, got.Pane, got.Heading, first.Pane, first.Heading)
+ }
+ }
+ rig.Screen("#pane-settings", fmt.Sprintf("the last pane at %d", w))
+ }
+}
+
// liveText is the text of the live region inside sel.
func liveText(t *testing.T, ctx context.Context, sel string) string {
t.Helper()
diff --git a/site/static/site.css b/site/static/site.css
index 0122c5f..2294419 100644
--- a/site/static/site.css
+++ b/site/static/site.css
@@ -62,7 +62,12 @@
holds them. */
.site-pane { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-content: start; }
.site-pane[hidden] { display: none; }
-.site-pane + .site-pane { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--rst-line); }
+/* With no script every pane shows, one under the next, and a rule
+ divides them. A hidden pane is still a sibling, so the divider names
+ shown panes on both sides: written as .site-pane + .site-pane it
+ drew a gap and a rule above every pane but the first when site.js
+ showed it alone. */
+.site-pane:not([hidden]) ~ .site-pane:not([hidden]) { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--rst-line); }
.site-pane h2 { margin: 0; font-size: 1.05rem; }
.site-pane h3 { margin: 0 0 4px; font-size: 0.95rem; }
.site-pane p { margin: 0; font-size: 0.85rem; color: var(--rst-text-muted); }