Public: anyone can read this; the crew can change it.
The studio on a 360px phone: the front door, in a hand #471
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/66-the-studio-on-a-360px-phone-the-front-do → main 1 commit fork
2 files
changed +51
−0
src/web/studio/style.ts +23
| @@ -88,6 +88,28 @@ const rail = part(/* css */ ` | ||
| 88 | 88 | .studio-crew li span { opacity: .85; } |
| 89 | 89 | `); |
| 90 | 90 | |
| 91 | /** The phone: one document, narrower. Each rule belongs to a component above | |
| 92 | * and only ever applies at 600px and under, so nothing changes on a desktop. */ | |
| 93 | const phone = part(/* css */ ` | |
| 94 | @media (max-width: 600px) { | |
| 95 | .studio-head { flex-wrap: wrap; } | |
| 96 | .studio-head h1 { flex: 1 1 100%; flex-wrap: wrap; overflow-wrap: anywhere; } | |
| 97 | .studio-head .env-facts { flex: 1 1 100%; min-width: 0; overflow-wrap: anywhere; } | |
| 98 | .studio-head form, .studio-head form .btn { flex: 1 1 auto; } | |
| 99 | .studio-tell .bar { flex-wrap: wrap; } | |
| 100 | .studio-tell .bar .btn, .studio-ask-actions .btn { min-height: 44px; } | |
| 101 | .fcard-head .btn, .fcard-form .btn { min-height: 44px; } | |
| 102 | .fcard-head { flex-direction: column; align-items: stretch; } | |
| 103 | .fcard-head .badge, .fcard-head .fcard-app { align-self: flex-start; } | |
| 104 | .studio-ask-actions { display: flex; flex-direction: column; align-items: stretch; white-space: normal; width: 100%; } | |
| 105 | .studio-list li { grid-template-columns: 18px minmax(0, 1fr); row-gap: 2px; } | |
| 106 | .studio-list li > :nth-child(n+3) { grid-column: 2; } | |
| 107 | .studio-word { white-space: normal; } | |
| 108 | .studio .row-meta, .studio-list .row-meta { white-space: normal; overflow-wrap: anywhere; } | |
| 109 | .studio-tell textarea, .studio-tell .input { font-size: 16px; } | |
| 110 | } | |
| 111 | `); | |
| 112 | ||
| 91 | 113 | import { feedCss } from "./feed_style.ts"; |
| 92 | 114 | |
| 93 | 115 | export const studioCss = `@layer components {\n${[ |
| @@ -100,4 +122,5 @@ export const studioCss = `@layer components {\n${[ | ||
| 100 | 122 | rowActions, |
| 101 | 123 | rail, |
| 102 | 124 | part(feedCss), |
| 125 | phone, | |
| 103 | 126 | ].join("\n\n")}\n}`; |
test/studio-phone.test.ts added +28
| @@ -0,0 +1,28 @@ | ||
| 1 | import { describe, expect, test } from "bun:test"; | |
| 2 | import { studioCss } from "../src/web/studio/style.ts"; | |
| 3 | ||
| 4 | /** The studio's phone rules: mechanical checks; the look is judged by eye. */ | |
| 5 | describe("the studio at 360px", () => { | |
| 6 | const phone = studioCss.slice(studioCss.indexOf("@media (max-width: 600px) {\n .studio-head")); | |
| 7 | ||
| 8 | test("every component the issue names has a phone rule", () => { | |
| 9 | for (const sel of [".studio-head", ".studio-tell", ".fcard-head", ".studio-ask-actions", ".studio-list li", ".studio-word"]) { | |
| 10 | expect(phone).toContain(sel); | |
| 11 | } | |
| 12 | }); | |
| 13 | ||
| 14 | test("decision buttons stack and taps are 44px", () => { | |
| 15 | expect(phone).toContain("flex-direction: column"); | |
| 16 | expect(phone).toContain("min-height: 44px"); | |
| 17 | }); | |
| 18 | ||
| 19 | test("no studio rule fixes a pixel width above 360", () => { | |
| 20 | for (const m of studioCss.matchAll(/(?<![-\w])(?:min-|max-)?width:\s*(\d+)px(?!\))/g)) { | |
| 21 | expect(Number(m[1])).toBeLessThanOrEqual(360); | |
| 22 | } | |
| 23 | }); | |
| 24 | ||
| 25 | test("phone rules live only inside a media query", () => { | |
| 26 | expect(phone.startsWith("@media")).toBe(true); | |
| 27 | }); | |
| 28 | }); |
new above
offline