Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 80 additions & 4 deletions web/src/components/community/Community.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4167,11 +4167,31 @@
}

@media (max-width: 768px), (hover: none) {
/* 8px, not 14px: the wordmark below is 48px tall now and the links 44px,
and the header stays the 65px it was. */
.pageHeader {
flex-wrap: wrap;
align-items: center;
gap: 8px 12px;
padding: 14px 16px;
padding: 8px 16px;
}

/* Two links stacked 18.4px and 11.5px tall, the one pair in the community
that failed WCAG 2.5.8 at 390px: a 24px circle on each overlapped the
other. A 24px line each, the words pulled together at the seam, so the
wordmark reads as it did. */
.brand,
.pageTitle {
display: flex;
min-height: 24px;
}

.brand {
align-items: flex-end;
}

.pageTitle {
align-items: flex-start;
}

/* The section nav moves to the tab bar at the bottom of the screen, and the
Expand Down Expand Up @@ -4200,6 +4220,24 @@
margin-left: auto;
}

/* 44px of thumb for each. At 390px Alerts was 35.8×18.8, the handle
28.8×17.4, Pattern Lab ↗ 86.1×18.8 and the deck chip, a phone's only way
to the deck, 64.9×29.2. */
.headerNav > a,
.headerNav > button {
display: inline-flex;
align-items: center;
min-height: 44px;
}

/* The two narrowest take half the 12px gap on each side as well. The
negative margin gives it back, so the row is as long as it was. */
.notifyLink,
.headerNav > .userChip {
padding: 0 6px;
margin: 0 -6px;
}

.makeCta {
font-size: 12px;
padding: 8px 12px;
Expand All @@ -4211,14 +4249,37 @@
display: none;
}

/* A handle can be any length; a header row cannot. */
/* A handle can be any length; a header row cannot. The ellipsis needs a
block box, which a flex item already is, so the 44px comes from the line. */
.userChip {
max-width: 96px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

/* 108px is the 96px above with the 6px either side that the row gets back. */
.headerNav > .userChip {
display: block;
max-width: 108px;
line-height: 44px;
}

/* The notice's ✕ was 18.9×21. Both reach into the strip's 9px padding, so
the strip grows 5px, not 23. */
.fwNoticeLink {
display: inline-flex;
align-items: center;
min-height: 44px;
margin: -9px 0;
}

.fwNoticeClose {
min-width: 44px;
min-height: 44px;
margin: -9px -12px -9px auto;
}

/* The fifth item is what tipped the row onto a second line, and it is the
one the "You" tab already leads to. It moves to your own profile. */
.headerNav .signOutLink {
Expand Down Expand Up @@ -4294,16 +4355,31 @@
right: 6px;
}

/* No gap between the rows: each one is 44px of thumb with air of its own. */
.feedControls {
gap: 12px;
gap: 0 12px;
}

/* Six tabs with Oldest and Liked do not fit a phone's width on one line;
wrapping beats a row that scrolls the whole page sideways. */
.sortTabs {
flex-wrap: wrap;
column-gap: 16px;
row-gap: 8px;
row-gap: 0;
}

/* The tabs were 25.6px tall and Flashable now 18.1px. A tab keeps its
word on its underline and takes the extra height above it. The buttons
are the featured editor's copy of the same tabs. */
.sortTabs a,
.sortTabs button {
display: inline-flex;
align-items: flex-end;
min-height: 44px;
}

.filterChip {
min-height: 44px;
}

.feedSearchInput {
Expand Down
75 changes: 75 additions & 0 deletions web/src/components/community/communityCss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,81 @@ describe("text fields on a phone", () => {
});
});

// On 26 October, at 390px, the header's links were 17.4 to 18.8px tall, the
// deck chip 29.2px, the notice's ✕ 18.9×21, the wall's sort tabs 25.6px and
// Flashable now 18.1px. "Patternflow" (18.4px) and "COMMUNITY" (11.5px) sat
// one on the other, and a 24px circle on each overlapped the other: WCAG 2.5.8.
describe("touch targets on a phone", () => {
const community = css("Community.module.css");
const phone = (selector: string) =>
mediaRules(community, MOBILE_MEDIA_QUERY)
.filter(({ selectors }) => selectors.includes(selector))
.flatMap(({ body }) => declarations(body));
const px = (lines: string[], property: string) => {
const line = lines.findLast((l) => l.startsWith(`${property}:`));
expect(line, `${property} is missing`).toBeDefined();
return line!.slice(property.length + 1).trim();
};

it.each([".headerNav > a", ".headerNav > button", ".sortTabs a", ".sortTabs button", ".filterChip"])(
"gives %s 44px of height",
(selector) => {
expect(phone(selector)).toContain("min-height: 44px");
},
);

// The handle's ellipsis needs a block box, so it cannot be the flex box the
// other links are.
it("gives the handle its 44px from the line and keeps the ellipsis", () => {
const chip = phone(".headerNav > .userChip");
expect(chip).toContain("display: block");
expect(chip).toContain("line-height: 44px");
expect(phone(".userChip")).toContain("text-overflow: ellipsis");
});

it("widens Alerts and the handle without lengthening the row", () => {
for (const selector of [".notifyLink", ".headerNav > .userChip"]) {
const lines = phone(selector);
expect(lines, selector).toContain("padding: 0 6px");
expect(lines, selector).toContain("margin: 0 -6px");
}
// Every box is border-box, so the padding comes out of the handle's
// 96px unless the cap grows with it.
const caps = phone(".headerNav > .userChip").concat(phone(".userChip")).filter((l) => l.startsWith("max-width:"));
expect(caps).toEqual(["max-width: 108px", "max-width: 96px"]);
});

it("gives the wordmark's two links a 24px line each", () => {
for (const selector of [".brand", ".pageTitle"]) {
expect(phone(selector), selector).toContain("min-height: 24px");
}
});

// 8px of padding on each side of a 48px wordmark is 64px and a border, the
// 65px the header was with 14px around a 36.8px Sign in.
it("keeps the header as tall as it was", () => {
const [vertical] = px(phone(".pageHeader"), "padding").split(" ");
expect(parseFloat(vertical) * 2 + 2 * 24).toBeLessThanOrEqual(66);
});

// The ✕ and the link reach into the strip's own padding, so a 44px row in
// a strip with 9px above it does not push the header down 23px.
it("gives the notice's ✕ a 44px square inside the strip's padding", () => {
const close = phone(".fwNoticeClose");
expect(close).toContain("min-width: 44px");
expect(close).toContain("min-height: 44px");
const [top, right] = px(declarations(rule(community, ".fwNotice")), "padding").split(" ");
expect(px(close, "margin")).toBe(`-${top} -${right} -${top} auto`);
expect(phone(".fwNoticeLink")).toContain("min-height: 44px");
expect(px(phone(".fwNoticeLink"), "margin")).toBe(`-${top} 0`);
});

it("drops the gaps between the wall's control rows", () => {
expect(phone(".sortTabs")).toContain("row-gap: 0");
expect(phone(".feedControls")).toContain("gap: 0 12px");
});
});

/** A JSX opening tag from its `<` to its `>`, past the arrows in its handlers. */
function openingTag(source: string, from: number): string {
let depth = 0;
Expand Down
Loading