diff --git a/web/src/components/community/Community.module.css b/web/src/components/community/Community.module.css
index 3ab63201e..054ecebc6 100644
--- a/web/src/components/community/Community.module.css
+++ b/web/src/components/community/Community.module.css
@@ -3467,12 +3467,15 @@
}
/* Your own profile's right-hand side: the CTA, and on a phone the sign-out
- that the header cannot fit. */
+ that the header cannot fit. The auto margin rather than a spacer span: at
+ 390px the name and the CTA do not fit on one line, and a spacer stays on
+ the first line while the CTA wraps to the left edge of the second. */
.profileSelfActions {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
+ margin-left: auto;
}
/* Off by default — the header's copy is the one you see on a desktop. */
@@ -4141,6 +4144,12 @@
grid-column: span 2;
}
+ /* In the grid a spacer would take a cell of its own and push Empty deck
+ onto a third row. */
+ .actionRow > .headerSpacer {
+ display: none;
+ }
+
.actionRow button,
.actionRow a {
min-height: 44px;
@@ -4160,9 +4169,12 @@
padding: 14px 16px;
}
- /* The section nav moves to the tab bar at the bottom of the screen. */
+ /* The section nav moves to the tab bar at the bottom of the screen, and the
+ gap that held it apart from the links goes with it. Only the header's own
+ gap: seven other rows use the same span to push a button right, and the
+ deck panel is drawn inside this , hence the child combinator. */
.sectionNav,
- .headerSpacer {
+ .pageHeader > .headerSpacer {
display: none;
}
diff --git a/web/src/components/community/communityCss.test.ts b/web/src/components/community/communityCss.test.ts
index 3c03df59f..39d7769d2 100644
--- a/web/src/components/community/communityCss.test.ts
+++ b/web/src/components/community/communityCss.test.ts
@@ -106,6 +106,54 @@ describe("the deck grid on a phone", () => {
});
});
+/** Every rule inside each `@media ` block, its selector list split. */
+function mediaRules(source: string, query: string) {
+ return source
+ .replace(/\/\*[\s\S]*?\*\//g, "")
+ .split(`\n@media ${query} {`)
+ .slice(1)
+ .flatMap((part) => [...part.slice(0, part.indexOf("\n}")).matchAll(/\n {2}([^{}]+) \{([^}]*)\}/g)])
+ .map(([, selectors, body]) => ({ selectors: selectors.split(",").map((s) => s.trim()), body }));
+}
+
+// On 26 October a bare `.headerSpacer { display: none }` in the phone block,
+// meant for the page header's gap, took out the same span in eight other rows.
+// At 390px the comment Post button sat at x 16 instead of 316, the deck
+// panel's ↑ buttons wandered between x 115 and 213 with each title's length,
+// and Make & publish on your own profile moved to the left edge.
+describe("the header spacer on a phone", () => {
+ const community = css("Community.module.css");
+
+ it("hides only the page header's own spacer", () => {
+ const hidden = mediaRules(community, MOBILE_MEDIA_QUERY)
+ .flatMap(({ selectors }) => selectors)
+ .filter((selector) => selector.endsWith(".headerSpacer"));
+ expect(hidden).toEqual([".pageHeader > .headerSpacer"]);
+ });
+
+ // The deck panel opens from the header's deck chip and is drawn inside the
+ // , so a descendant selector would still reach its rows.
+ it("reaches the header's spacer as a child, not the deck panel inside it", () => {
+ expect(community).not.toMatch(/\.pageHeader \.headerSpacer/);
+ });
+
+ // In the two-column action grid a spacer is a cell of its own, and Empty
+ // deck would drop to a third row.
+ it("still hides it in the action grid, wherever that grid is drawn", () => {
+ const grid = mediaRules(community, "(max-width: 900px)");
+ expect(grid.find(({ selectors }) => selectors.includes(".actionRow"))?.body).toContain("display: grid;");
+ const spacer = grid.find(({ selectors }) => selectors.includes(".actionRow > .headerSpacer"));
+ expect(spacer, ".actionRow > .headerSpacer is missing from @media (max-width: 900px)").toBeDefined();
+ expect(spacer!.body).toContain("display: none;");
+ });
+
+ // At 390px "@ada YOU" and its stats line take the first line, and the
+ // actions wrap to a second one where no spacer reaches.
+ it("keeps your profile's actions right when they wrap under the name", () => {
+ expect(declarations(rule(community, ".profileSelfActions"))).toContain("margin-left: auto");
+ });
+});
+
// The community's text colours, against each ground they are drawn on. On 26
// October the faint ink was #6B6558, 3.18:1 on the well-2 and 3.40:1 on the
// page, and axe found 426 color-contrast failures over eleven community pages,