diff --git a/packages/website/src/components/nav.tsx b/packages/website/src/components/nav.tsx
index cd945b4..ca2f824 100644
--- a/packages/website/src/components/nav.tsx
+++ b/packages/website/src/components/nav.tsx
@@ -223,14 +223,6 @@ function LogoWordmark({
const lOpacity = useTransform(scrollY, [40, 120], [1, 0]);
const lWidth = useTransform(scrollY, [40, 120], [14, 0]);
- /* The "." in the icon fades out as it merges with the wordmark. Width
- collapses over the same range as opacity, since the dot is an inline
- sibling of "L" inside a `justify-content: center` box, so fading it to
- `opacity: 0` alone left its layout width in place, which kept "L" off
- centre (reading as if the square clipped its left side once rotated). */
- const dotOpacity = useTransform(scrollY, [60, 130], [1, 0]);
- const dotWidth = useTransform(scrollY, [60, 130], ["0.4em", "0em"]);
-
return (
/* No clip on this box. The square's rotated bounding box and its hard
offset shadow both grow past the flex item's edges, so an `overflow-hidden`
@@ -254,9 +246,6 @@ function LogoWordmark({
backgroundColor: LOGO_ORIGINAL_ORANGE,
}}
>
- {/* The dot stays in flow, as it does on the business site. Taking it
- out with `position: absolute` left the mark centring the bare "L",
- pushing the visible "L." off-centre inside the square. */}
L
- {/* `inline-block` defaults to `vertical-align: baseline`, which
- aligns the dot's OWN baseline (its bottom edge, per spec, once
- `overflow-hidden` is set) to L's text baseline - not to L's own
- visible bottom. Measured at every font size this icon uses
- (13-20px): that left the dot 4-6px above L's bottom edge.
- `text-bottom` aligns to the bottom of the parent's font instead,
- which lands exactly on L's bottom at every size (0px gap,
- measured), with no magic-number offset to keep in sync with the
- font. */}
-
- .
-
diff --git a/packages/website/tests/e2e/site-header-logo-mark.spec.ts b/packages/website/tests/e2e/site-header-logo-mark.spec.ts
index 6787c7b..acf1d1b 100644
--- a/packages/website/tests/e2e/site-header-logo-mark.spec.ts
+++ b/packages/website/tests/e2e/site-header-logo-mark.spec.ts
@@ -1,31 +1,17 @@
import { test, expect, type Page } from "@playwright/test";
/**
- * Header logo mark contract: the scroll-driven "L." icon must keep "." sitting
- * on L's own bottom edge (not merely overlapping its line), and "L" centred in
- * the square, at every point of its scroll transition (0-160px), not just at
- * rest and at the end.
+ * Header logo mark contract: the scroll-driven icon must keep "L" centred in
+ * the square at every point of its scroll transition (0-160px), not just at
+ * rest and at the end, and the square must actually rotate.
*
- * Two regressions this guards, both about the "." span, both invisible to a
- * plain screenshot at a glance:
- *
- * 1. (#37) The dot faded via `opacity` alone. Opacity does not remove layout
- * width, so the invisible dot kept reserving space inside the square's
- * `justify-content: center` box for the whole 60-130px fade range, pushing
- * "L" left of true centre the entire time - on a rotated square this reads
- * as a clipped corner rather than off-centre text.
- * 2. The dot's `inline-block` defaulted to `vertical-align: baseline`, which
- * aligns the dot's OWN baseline - its bottom edge, per spec, once
- * `overflow-hidden` is set - to L's TEXT baseline, not to L's own visible
- * bottom. Measured 4-6px of gap depending on the icon's current font size
- * (13-20px across the scroll range): the dot visibly floated above L's
- * bottom rather than resting on it. `align-text-bottom` closes this to 0px
- * at every size, measured directly, not eyeballed.
- *
- * A same-line check that only asks "do these two elements' vertical bands
- * overlap at all" passes even with the 4-6px gap from #2 - overlap is a much
- * weaker property than "sits on the same baseline" and is why that bug shipped
- * once already. `sitsOnBaseline` below asserts the tight version instead.
+ * The mark previously paired "L" with a "." that faded and collapsed on
+ * scroll. That pairing went through two rounds of alignment bugs - the dot's
+ * `opacity`-only fade left stale layout width that pushed "L" off centre
+ * (#37), and even after that fix the dot's `vertical-align: baseline`
+ * default put it 4-6px above L's own bottom edge instead of resting on it.
+ * The dot was dropped entirely rather than chase a third alignment fix: "L"
+ * alone, centred, has no second element to misalign against.
*/
const HOME_LINK = 'nav a[href="/"]';
@@ -36,37 +22,21 @@ async function markGeometry(page: Page) {
const square = document.querySelector(sel);
if (!square) throw new Error(`icon mark not found: ${sel}`);
const inner = square.querySelector("span");
- const dot = inner?.querySelector("span");
- if (!inner || !dot) throw new Error("L/dot spans not found inside mark");
+ if (!inner) throw new Error("L span not found inside mark");
const range = document.createRange();
range.selectNodeContents(inner.childNodes[0]);
const lRect = range.getBoundingClientRect();
- const dotRect = dot.getBoundingClientRect();
const squareRect = square.getBoundingClientRect();
- const dotStyle = getComputedStyle(dot);
return {
squareCenterX: squareRect.x + squareRect.width / 2,
squareTransform: getComputedStyle(square).transform,
- lTop: lRect.top,
- lBottom: lRect.bottom,
lCenterX: lRect.x + lRect.width / 2,
- dotTop: dotRect.top,
- dotBottom: dotRect.bottom,
- dotOpacity: Number(dotStyle.opacity),
};
}, ICON_MARK);
}
-// Deliberately NOT "do the two vertical bands overlap at all" - a 4-6px gap
-// between the dot's bottom and L's bottom (the #2 regression above) still
-// overlaps, since the dot's own height is well over 6px. This checks the
-// specific thing that matters: the dot's bottom edge sits where L's does.
-function sitsOnBaseline(g: Awaited>) {
- return Math.abs(g.dotBottom - g.lBottom) < 1.5;
-}
-
// The document sets `scroll-behavior: smooth`, so the two-argument
// `window.scrollTo(x, y)` form (which always defers to that CSS property)
// animates over several hundred ms instead of jumping. `behavior: "instant"`
@@ -94,8 +64,8 @@ test.describe("primary navigation logo mark", () => {
// project's WebKit run: neither a programmatic `scrollTo` + a manually
// dispatched `scroll` event, nor a real `mouse.wheel` gesture on a desktop
// (non-mobile) WebKit context, ever produced a change in this mark's
- // rendered `transform`/`opacity` in Playwright's bundled WebKit, headless or
- // headed - while `window.scrollY` itself updated correctly every time. That
+ // rendered `transform` in Playwright's bundled WebKit, headless or headed -
+ // while `window.scrollY` itself updated correctly every time. That
// implicates framer-motion's `useScroll()` reporting to Playwright's WebKit
// specifically, not this component: every other scroll-driven nav property
// (icon size, border, wordmark tracking) rides the same MotionValue and
@@ -109,7 +79,7 @@ test.describe("primary navigation logo mark", () => {
);
});
- test("L and the dot stay on the same line and centred through the whole scroll transition", async ({
+ test("L stays centred in the square through the whole scroll transition", async ({
page,
}) => {
await page.goto("/");
@@ -120,21 +90,14 @@ test.describe("primary navigation logo mark", () => {
for (const scrollY of [0, 60, 95, 130, 200]) {
settled = await scrollToAndSettle(page, scrollY);
- // The dot's bottom edge must sit on L's bottom edge (within 1.5px), not
- // float above it - a looser "these two elements' lines merely overlap"
- // check would miss the 4-6px baseline gap this guards.
expect(
- sitsOnBaseline(settled),
- `at scrollY=${scrollY}, dot bottom ${settled.dotBottom.toFixed(1)} should sit on L's bottom ${settled.lBottom.toFixed(1)} (within 1.5px)`,
- ).toBe(true);
+ Math.abs(settled.lCenterX - settled.squareCenterX),
+ `at scrollY=${scrollY}, L (centre ${settled.lCenterX.toFixed(1)}) should sit on the square's centre (${settled.squareCenterX.toFixed(1)})`,
+ ).toBeLessThan(3);
}
if (settled === null) throw new Error("unreachable: loop always assigns");
- // At full scroll, the dot has faded and collapsed; "L" must sit on the
- // square's true horizontal centre, not left of it, and the square must
- // actually be rotating rather than stuck at rest.
- expect(settled.dotOpacity).toBeLessThan(0.05);
- expect(Math.abs(settled.lCenterX - settled.squareCenterX)).toBeLessThan(3);
+ // The square must actually be rotating at full scroll, not stuck at rest.
expect(settled.squareTransform).not.toBe("none");
});
@@ -147,8 +110,7 @@ test.describe("primary navigation logo mark", () => {
await scrollToAndSettle(page, 200);
const g = await scrollToAndSettle(page, 0);
- expect(g.dotOpacity).toBeCloseTo(1, 1);
expect(g.squareTransform).toBe("none");
- expect(sitsOnBaseline(g)).toBe(true);
+ expect(Math.abs(g.lCenterX - g.squareCenterX)).toBeLessThan(3);
});
});