feat(design): design lab + DESIGN.md skeleton (E28 S2a)
- docs/DESIGN.md: the written source of truth for the UI. Decided chapters (principles, device adaptation, action placement, text stays selectable, interaction rules, copy, anti-patterns, decision log); controls and surfaces are marked Pending until the owner picks from the lab. - /design-lab (dev only): renders each open decision's variants with the app's real CSS, themes and font scale. Batch A = six control decisions. - devOnlyGuard: a production build fails if anything under src/design-lab reaches the bundle (seen red with an unguarded import). - Size contract: tablet portrait + landscape and up, replacing the 1024x768 floor with its never-built "too small" message.
This commit is contained in:
@@ -127,7 +127,7 @@ describe("Button primitive (S5)", () => {
|
||||
});
|
||||
|
||||
it("carries the coarse-pointer minimum target, except on a link", () => {
|
||||
// The 1024x768 comfort contract: the minimum lives in the primitive, so no call site can opt
|
||||
// The tablet comfort contract: the minimum lives in the primitive, so no call site can opt
|
||||
// out of it by accident. A link is text in a sentence and is excluded by design.
|
||||
const { unmount } = render(<Button>Tap</Button>);
|
||||
expect(screen.getByRole("button", { name: "Tap" }).className).toContain("touch-target");
|
||||
|
||||
@@ -155,7 +155,7 @@ const ButtonImpl = forwardRef<HTMLButtonElement, ButtonOwnProps>(function Button
|
||||
// here rather than by asking every call site to pass `size="none"` alongside `variant="link"` —
|
||||
// a pairing everyone would eventually forget, which is how a "link" grows a button's box.
|
||||
const resolvedSize: ButtonSize = size ?? (variant === "link" ? "none" : "md");
|
||||
// The 1024x768 comfort contract lives HERE, not in the call sites: on a coarse pointer every
|
||||
// The tablet comfort contract (docs/DESIGN.md) lives HERE, not in the call sites: on a coarse pointer every
|
||||
// button-shaped control gets at least a 2.75rem box (see .touch-target in index.css). `link` is
|
||||
// excluded on purpose — it is a run of text inside a sentence, and a 44px-tall inline link would
|
||||
// wreck the paragraph it sits in rather than make anything easier to hit.
|
||||
|
||||
@@ -46,10 +46,10 @@ domain-free.
|
||||
|
||||
## The size contract (E10 S1c)
|
||||
|
||||
The app optimises for **1024x768 (tablet landscape) as the floor**; below it we owe an honest
|
||||
message, above it correct rendering all the way up. That contract lives in these components, not in
|
||||
the ~160 call sites, because a rule repeated in 160 places is a rule that is already broken
|
||||
somewhere:
|
||||
The app supports **tablet portrait and landscape and every size above them** (from 768px wide; phones
|
||||
are not a target — see [`docs/DESIGN.md`](../../../../docs/DESIGN.md) § Device adaptation). Measure a UI
|
||||
change at 1024x768, 820x1180 and a desktop size. That contract lives in these components, not in the
|
||||
~160 call sites, because a rule repeated in 160 places is a rule that is already broken somewhere:
|
||||
|
||||
- **Geometry is a ladder, not a free choice.** `Button`'s `SIZES` are the rungs, derived from what
|
||||
the app actually used. Pick a rung; do not hand-roll padding on a primitive.
|
||||
|
||||
Reference in New Issue
Block a user