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
13 changes: 13 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2334,6 +2334,19 @@ li code {
.nodec { width: 40px; height: 40px; font-size: 12.5px; }
}

/* iOS Safari zooms the whole page when it focuses a text field whose text
is smaller than 16px. Phones get 16px in every text field; !important
because chapter styles size their lab inputs with more specific selectors. */
@media (max-width: 560px) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cover landscape phone viewports

On iPhones held in landscape, the CSS viewport is commonly wider than 560px (for example, 844px), so this media query does not apply and the lab, quiz, and command-palette fields remain at 13–15.5px. Focusing them can therefore still trigger the iOS Safari zoom that this change is intended to prevent; target mobile/touch devices independently of narrow viewport width, and include a landscape viewport in the test.

Useful? React with 👍 / 👎.

input:not([type]),
input[type="text"],
input[type="search"],
textarea,
select {
font-size: 16px !important;
}
}

@media (prefers-reduced-motion: reduce) {
*,
*::before,
Expand Down
34 changes: 32 additions & 2 deletions test/e2e/narrow.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// On a phone nothing is cut off at the right edge of any page, and printing
// shows every section, without the shell chrome, in the light palette.
// On a phone nothing is cut off at the right edge of any page and text fields
// are large enough that iOS does not zoom in; printing shows every section,
// without the shell chrome, in the light palette.

import { expect, test } from "@playwright/test";
import { elementsPastViewport } from "./overflow";
Expand Down Expand Up @@ -36,6 +37,35 @@ for (const width of [360, 390]) {
});
}

test.describe("text fields on a phone", () => {
test.use({ viewport: { width: 390, height: 844 } });

test("are at least 16px, so iOS Safari does not zoom in on focus", async ({ page }) => {
// The labs' inputs are on the page from the start
for (const route of ["/string", "/hash", "/bst", "/heap", "/trie", "/advanced"]) {
await page.goto(route);
const sizes = await page
.locator('input:not([type]), input[type="text"]')
.evaluateAll((els) => els.map((e) => parseFloat(getComputedStyle(e).fontSize)));
expect(sizes.length, route).toBeGreaterThan(0);
for (const size of sizes) expect(size, route).toBeGreaterThanOrEqual(16);
}
// A quiz fill-in appears only on its question, and the palette only when
// open, so measure a field with each of their classes instead
const shared = await page.evaluate(() =>
["q-input", "cmdk-input"].map((cls) => {
const field = document.createElement("input");
field.className = cls;
document.body.append(field);
const size = parseFloat(getComputedStyle(field).fontSize);
field.remove();
return size;
}),
);
for (const size of shared) expect(size).toBeGreaterThanOrEqual(16);
});
});

test("printing shows every section, without the shell chrome, in the light palette", async ({
page,
}) => {
Expand Down
Loading