From a2a9a438c470886ff45790d60fbd8694f45eeb9a Mon Sep 17 00:00:00 2001 From: Younggi Choi <74581798+choiyounggi@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:19:45 +0900 Subject: [PATCH] knowledge(design): product-surface vs brand-surface rules, UI hardening against real content, anti-slop craft-floor rows Source: pbakaus/impeccable skill references at 114ea1d3 (operate.md, craft-floor.md, harden.md), plus W3C text-size, W3C Pointer Events, MDN and WCAG 1.4.4 for the hardening page. Two new frontend/design pages, +10 rows on anti-slop-visual-design, responsive-layout fluid-type row scoped, back-links on async-ui-states, index rows, log entries, ingest report with the two-reviewer round. Co-Authored-By: Claude Fable 5.1 --- .dev-loop/INGEST_REPORT.md | 238 ++++++++---------- log.md | 8 + .../frontend/data-fetching/async-ui-states.md | 2 +- .../design/anti-slop-visual-design.md | 15 +- .../design/product-ui-vs-brand-surface.md | 82 ++++++ wiki/frontend/design/responsive-layout.md | 4 +- .../ui-hardening-against-real-content.md | 89 +++++++ wiki/frontend/index.md | 4 +- 8 files changed, 306 insertions(+), 136 deletions(-) create mode 100644 wiki/frontend/design/product-ui-vs-brand-surface.md create mode 100644 wiki/frontend/design/ui-hardening-against-real-content.md diff --git a/.dev-loop/INGEST_REPORT.md b/.dev-loop/INGEST_REPORT.md index 939e9fb..814b7b2 100644 --- a/.dev-loop/INGEST_REPORT.md +++ b/.dev-loop/INGEST_REPORT.md @@ -1,149 +1,127 @@ -# Knowledge ingest — WebMCP as the development standard: 2 pages re-verified, 2 new pages, routing widened +# knowledge(design): product-surface vs brand-surface rules, UI hardening against real content, anti-slop craft-floor rows -Trigger: a Korean WebMCP explainer video (2026-09) pasted for evaluation. Its own content -(declarative vs imperative API, shared page logic, token savings vs browser agents) was already -covered by the two pages ingested 2026-08-18; adoption advocacy and proposal history were again -left out. Verifying the video's claims against primary sources found the wiki a month behind its -sources; the owner then decided (2026-09-28) that the additive WebMCP tool layer is the -development standard for web UI work, QA, and bug fixes, which changes routing. +Source: the Impeccable design skill (github.com/pbakaus/impeccable), read at commit +114ea1d3838fca73b253af45f873b9c4f5f213c8 (2026-09-29). Origin: the user asked whether +hgko-dev.tistory.com/551 (an Impeccable install guide) should steer design work. The +guide itself is stale (`/teach-impeccable`, `.impeccable.md` and the `dist/claude-code` +path no longer exist) and carries no design knowledge; the skill's reference files do. +This PR ingests the three areas where the wiki and the hallmark skill had no coverage. ## Verified best-practice -### 1. `consequentialHint` / `debugging` annotations, ChatGPT site-tools constraints, DevTools pane → **verified** - -- https://webmachinelearning.github.io/webmcp/ — Draft Community Group Report dated 2026-09-26; - IDL `partial interface Document { readonly attribute ModelContext modelContext }` (Document - only); `dictionary ToolAnnotations { readOnlyHint, untrustedContentHint, consequentialHint, - debugging }`; no user-confirmation primitive defined. (fetched 2026-09-28) -- https://developer.chrome.com/docs/ai/webmcp/imperative-api — page dated 2026-09-21; - `document.modelContext.registerTool({ name, description, inputSchema, execute, annotations }, - { signal, exposedTo })`; `consequentialHint` "allows agents and browsers to enforce mandatory - user confirmation prompts before executing high-stakes tools"; `debugging` Chrome 156+; no - `requestUserInteraction` mention (the security page's old note on it was removed). -- https://developer.chrome.com/docs/devtools/application/webmcp — page dated 2026-05-12; the - WebMCP pane is in the Application panel; Available Tools (name, description, invocation count), - Invoked Tools (status, input, output), Run tool with manual parameters, schema-mismatch errors - in the output pane. -- https://learn.chatgpt.com/docs/webmcp — "Site tools are ChatGPT's implementation of the - proposed WebMCP standard"; feature-detects `document.modelContext.registerTool`; declarative - API and iframe registrations unsupported; "Each tool invocation receives a safety review before - it runs"; GPT-5.6 Sol / GPT-6 Sol only, Luna disabled; desktop app; not in Enterprise/Edu; - surfaces: built-in browser, ChatGPT Work, Codex; user toggle under Settings → Browser → - Permissions. (help.openai.com's site-tools article was dropped as a source: it returns 403 to - fetchers, so its claims could not be verified.) -- https://developer.chrome.com/docs/ai/webmcp/secure-tools — page dated 2026-09-01; budgets - 30 / 500 / 150 / 1.5K. Contains no auth-state guidance, so the parity gate's both-auth-states - row is derived from security-agent-exposure-in-session-tool-exposure (PII via read tools, - server-side authz unchanged), not from this page. -- https://developer.chrome.com/docs/ai/webmcp — page dated 2026-08-07; origin trial from Chrome - 149; Model Context Tool Inspector extension; prompts go to `gemini-3-flash-preview`. - -### 2. WebMCP-as-standard routing (owner decision) → **policy, not a sourced claim** - -The widened triggers (any new or changed user action in a web UI) and the parity gate's -"every action has a tool unless on the exclusion list" are the owner's development standard, -stated as such in log.md. Every mechanical directive inside those pages is sourced as above. -The standard keeps the existing "human UI primary, tool layer additive" directive unchanged. +1. **Product (Operate) surfaces invert the brand-surface rules.** New page + `frontend-design-product-ui-vs-brand-surface`: Persuade / Operate / Read / + Experience modes chosen from the requested surface; on Operate surfaces one tuned + sans, fixed rem scale (1.125–1.2), Restrained color floor with a second neutral, + full state vocabulary, skeleton loading, structural responsiveness, 150–250 ms + state motion and no page-load sequence, standard affordances permitted, overlays + escape overflow ancestors, modal only for interruption or protected focus. + Verified by reading `skill/reference/operate.md` and `skill/SKILL.src.md` at the + pinned SHA; cross-checked against the Anthropic frontend-design post the anti-slop + page already cites. `confidence: unverified` — the sources are a third-party skill + file and a blog post, and no production use of this wiki's own is described; the + page says what upgrades it. +2. **UI hardening against real content.** New page + `frontend-design-ui-hardening-against-real-content`: extreme-input test set, + translation-expansion budget by source length, truncate / clamp / wrap with + `min-width: 0`, logical properties + RTL glyph flip, `Intl` formatting and + `Intl.PluralRules`, interrupted-gesture handling (`touch-action`, `pointerId` + pinning, cancel paths), error copy with recovery, pending-state submit guard, + virtualization. Verified live: W3C article-text-size (IBM table ≤10 chars + 200–300% … >70 chars 130%; Korean 0.8×), W3C Pointer Events (`pointercancel` + MUST fire on stream suppression; capture implicitly released), MDN logical + properties, `Intl` / `Intl.PluralRules`, `overflow-wrap` (only `anywhere` + counts toward min-content), `hyphens`, `-webkit-line-clamp`, `touch-action` + (pointercancel on browser gesture take-over), `title` accessibility concerns, + WCAG SC 1.4.4 — all HTTP 200. `confidence: verified`. +3. **Craft-floor rows for brand surfaces.** `frontend-design-anti-slop-visual-design` + +2 directive rows (theme browser-default surfaces from the palette; shadows carry + offset + soft blur), +2 edge cases (route task UIs and hostile content to the new + pages), +6 Instead-of rows (eyebrow, decorative section numbers, geometric + occlusion mask, mono as costume, glyph icons, image hover animation). Verified by + reading `skill/reference/craft-floor.md` at the pinned SHA. Body 85 → 96 lines. + +Gap evidence: `grep -rli dashboard wiki/frontend` → 0 files; hallmark v1.1.0 has 3 +"product UI" mentions and 0 `pointercancel` / RTL rules; the wiki's only Impeccable +citation was README-level (log.md 2026-08-21) because the skill's paths churn — every +new citation here is a commit-pinned permalink. ## Existing-layer check -Pages read: frontend-agent-interfaces-agent-facing-tool-surfaces, security-agent-exposure-in-session-tool-exposure, qa-process-release-gates, testing-strategy-differential-testing, testing-strategy-cross-layer-effect-tests, testing-strategy-failing-test-first - -Whole-wiki grep `webmcp|modelContext|toolname|agent-friendly|answer engine` → 7 files (the two -WebMCP pages, their two domain indexes, INDEX.md, log.md, platforms/tools/plugin-mcp-server- -registration which matches only on a modelcontextprotocol URL). skills/, hooks/, agents/, -templates/, AGENTS.md → 0 mentions. qa/testing/debugging indexes → no WebMCP routing line (the -one qa hit is the model-coupled-guidance-aging-detector page, unrelated). `wiki_search` was -unavailable (dev-loop-wiki MCP server failed to connect this session); the category pages were -read directly per the skill's fallback. - -Merge targets: both existing WebMCP pages were **revised in place** (same trigger, same -directive, newer sources) — no new page for that material. The two new pages have new triggers -(a release gate; a test strategy) that no existing qa/testing page covers: release-gates is the -generic checklist page and is linked, not extended; differential-testing / cross-layer-effect- -tests / failing-test-first are referenced from the testing page's edge cases. - -Related links added both ways: qa-process-release-gates ↔ qa-process-agent-tool-parity-gate; -testing-strategy-cross-layer-effect-tests ↔ testing-strategy-agent-tool-shared-handler-tests; -frontend agent-facing-tool-surfaces and security in-session-tool-exposure ↔ both new pages. +Pages read: frontend-design-anti-slop-visual-design, frontend-design-responsive-layout, frontend-data-fetching-async-ui-states, frontend-design-design-canvas-workflow, frontend-accessibility-interactive-elements + +- `wiki_search` (dev-loop-wiki MCP) was down this session (CONNECTION_CLOSED); dedupe + was done by reading the design category index and grepping the frontend domain for + each candidate's trigger terms (`dashboard`, `product UI`, `::selection`, + `pointercancel`, `inline-start`, `RTL`, `i18n`, `empty state`, `skeleton`). +- anti-slop-visual-design: same trigger for the craft-floor rows → merged as rows. + Its motion row ("spend the motion budget on one page-load") conflicts with the + Operate rule; resolved as a condition-dependent edge-case row that routes task UIs + to the new page, not as an overwrite. +- responsive-layout: already owns overflow / `min-width: 0` / CJK wrapping / iOS + 16px; the hardening page links to it for those rows instead of repeating them. +- data-fetching/async-ui-states: owns empty / loading / error states; the hardening + page's edge case defers to it. Back-links added on both. +- No local layer (`wiki-local/`) exists in this repo. ## Open-PR check -`gh pr list --state open` (2026-09-28): one open PR, #223 (knowledge/choiyounggi-20260927-220735, -15 insights). Its file list contains none of the four WebMCP-related pages; the only overlap is -appended log.md entries (union merge). +Open PRs #223, #225–#231 (listed 2026-09-29 with `gh pr list --state open`) are +knowledge-flush batches touching backend, infrastructure, testing, mobile and +platforms pages; none touch `wiki/frontend/design`, `wiki/frontend/index.md` or the +three source files. Two concurrent local sessions (dev-loop-doliolid-70, -f8) had +queued the identical ingest; both confirmed zero writes and stood down before this +branch was edited. ## Routing decision -- frontend/agent-interfaces/agent-facing-tool-surfaces — revised (owning artifact: the UI code). -- security/agent-exposure/in-session-tool-exposure — revised (confirmation gating). -- **qa/process/agent-tool-parity-gate** — new page in the existing `process` category beside - release-gates: it is a release-decision checklist for one surface, so it belongs where - release-gates and regression-scope live; no new category. -- **testing/strategy/agent-tool-shared-handler-tests** — new page in the existing `strategy` - category beside test-level-choice / cross-layer-effect-tests: it decides what to test and at - which level for a two-entry-point action; no new category. -- AGENTS.md routing step 7 — one row added (web UI user action → frontend agent-interfaces, - then the qa parity gate); tests/review-routing.bats pin 6 → 7 rows in the same commit. -- INDEX.md frontend / qa / testing route lines and the three domain indexes updated; log.md - gained two ingest entries and two revise entries. +- Layer: bundled `wiki/` (general knowledge, no repo-specific files named). +- Domain: frontend. Category: design — both new pages are design-time decisions + about how a surface is styled and verified; the hardening page was considered for + `forms` and `accessibility` and rejected because its trigger is content shape and + locale, not a form lifecycle or an assistive-technology contract. +- Page ids: `frontend-design-product-ui-vs-brand-surface`, + `frontend-design-ui-hardening-against-real-content`. ## Verification -- `node scripts/wiki-lint-prohibitions.js wiki` → directives 79, violations 0 (pin unchanged). -- `node scripts/wiki-structure-checks.js wiki --layer bundled` → pages 353, findings 0. -- bats: tests/wiki-*.bats + tests/review-routing.bats + tests/orchestrate-review-pass.bats → - 273/273 ok (one earlier `Recall@5` flake re-ran green; baseline on an untouched HEAD worktree - measured the same 0.87). -- Body lines: frontend 110, security 76, qa 70, testing 66 (limit 120). +- `node scripts/wiki-lint-prohibitions.js` → directives 79 / compliant 79 / + violations 0 (unchanged from the pre-edit baseline; rows written positively, so + `tests/wiki-lint-prohibitions.bats:25` keeps its pin). +- `PATH=/opt/homebrew/bin:$PATH bats tests/wiki-structure-checks.bats tests/wiki-index.bats tests/wiki-lint-prohibitions.bats tests/wiki-contradiction.bats tests/wiki-index-freshness.bats tests/wiki-lint-score.bats tests/wiki-agent-gate.bats` → `1..136`, 136 ok, 0 not ok. +- Inline `[page-id]` links and `related:` ids in the three touched pages all resolve + (grep `^id:` per id). +- No banned qualifiers (`usually`, `consider`, `might`, `generally`, `as appropriate`, + `often`, `should`) in the two new pages. ## Independent review (before commit) -- General reviewer (feature-dev:code-reviewer, fresh context): FAIL → 2 major + 4 minor, all - applied: `consequentialHint` scope aligned with the security page's class table; gate edge row - for a vanished runtime (human-UI release not blocked); logout added to the AbortSignal edge - row and the Registration test; step 2/3 of the testing page conditioned on imperative vs - declarative; AGENTS.md step-7 row admits the exclusion list; qa index clause matched to the - page trigger. -- Adversarial fact-checker (fresh context, every source re-fetched): 8/8 targeted claims - confirmed; FAIL on 2 unsupported sentences + 8 imprecisions, all applied: dropped the - `navigator.modelContext` history (no cited source has it); Run tool no longer claimed to write - Invoked Tools (that log is agent↔page); `consequentialHint` quote re-attributed (draft: "client - or agent"; Chrome: "agents and browsers"); secure-tools' stale `requestUserInteraction()` - mention recorded; `readOnlyHint` "requested" → "in its read-only example"; budgets labelled - as Chrome's recommendations applied as limits, parameter names included; cross-origin edge - row now names `allow="tools"` + `exposedTo` + `getTools({ fromOrigins })`; origin trial and - local flag separated; `SubmitEvent.agentInvoked` / `respondWith()` added to the declarative - test directive. - -## CI agent gate (run 36329841491) — blocker refuted, advisories applied - -- Blocker claimed the CG draft has no "client or agent … selectively enforce" language. Ground - truth (`curl -sL https://webmachinelearning.github.io/webmcp/`, 504,537 bytes, tags stripped, - 2026-09-28): the phrase occurs once, in §6 Security considerations under the mitigation for - "Misrepresentation of Intent": "A boolean consequentialHint annotation acts as a signal to the - client or agent that the tool performs a consequential action … This way they can selectively - enforce mandatory user confirmation prompts before executing high-stakes tools". The gate's - fetch read a truncated page. The page now names the section beside the quote. -- Advisory (chromestatus unverifiable from CI): confirmed via the JSON API — stage 150 - desktop/Android 149–156; Firefox and Safari "No signal". The source line now records the API - path. -- Advisory (Run tool vs Invoked Tools): Do 8 no longer implies manual runs are excluded from the - log; it states only what the DevTools page states. -- Advisory (cross-link gap): qa parity gate ↔ backend-common-api-design-agent-tool-granularity - linked both ways, with one sentence placing the parity table as the release-time reading of - that page's design-time capability map. - -## CI agent gate, second run (36330523418) — blocker applied, quote advisory stands - -- Blocker: the frontend page stated "the development standard is an additive WebMCP tool per - action" as unconditional fact under confidence: verified. Applied: the trigger, the frontend - domain description and load-when line, and the INDEX.md frontend row now condition on "this - wiki's development standard (owner decision, log.md 2026-09-28, a policy rather than a sourced - fact)". Routing width is unchanged; the sentence is a policy the wiki declares, not a claim about - the world. -- Advisory (§6 quote unverifiable from CI): the gate's fetch truncates before §6.3.2 and curl is - blocked in its sandbox; it records the quote as unverifiable, not refuted. Ground-truth grep is in - the PR comment; the two sources lines name the section. -- Advisory (duplication with test-level-choice's extract-and-wire edge row): linked both ways and - named in step 1 as the general rule applied to two entry points. +Two fresh-context reviewers (`feature-dev:code-reviewer`, one general brief and one +adversarial brief; read-only, no Bash) on the 7-file diff. Findings applied: + +- anti-slop: a source bullet had been appended after the last table on a page that + keeps its sources in frontmatter → removed; the task-UI edge case named a + "fluid-type" row this page does not have and missed the default-font-rejection and + extreme-contrast rows → rewritten to name the exact rows and widened to Read / + Experience surfaces (a docs site previously had no route to the Read guidance). +- responsive-layout: the `clamp()` row stated fluid type unconditionally → scoped to + brand/content surfaces with the app-UI alternative and a `related:` link. +- product-ui-vs-brand-surface: `field-tested` over-claimed (no production context + described) → `unverified` with the upgrade condition in the page; trigger lines + now list mode choice, Read/Experience surfaces, overlay clipping, modal-first + (index/trigger drift); 65–75ch attributed to operate.md in the source line. +- ui-hardening: `min-width: 0` rationale was wrong for `overflow-wrap: anywhere` + (only `break-word` leaves min-content unchanged) → corrected with the MDN quote; + `title` tooltip replaced by hover+focus tooltip (MDN accessibility concerns); + `touch-action: none` added to the drag row (MDN: browser fires `pointercancel` + when it takes a gesture); iOS 16px row now defers to responsive-layout instead of + restating an unsourced mechanism; WCAG 1.4.4 source added; IBM 51–70 row + anomaly noted as published. +- Not applied: "ui-hardening bundles four topics" (both reviewers rated it below + their confidence bar; the page's one case is "data is present but hostile" and it + routes empty/loading/error out to async-ui-states). + +After the fixes: the same seven bats wiki suites re-run → `1..136`, 136 ok, 0 not ok; +prohibitions still directives 79 / violations 0; all inline links resolve. + +🤖 Generated with [Claude Code](https://claude.com/claude-code) diff --git a/log.md b/log.md index 72b6053..0c6a20f 100644 --- a/log.md +++ b/log.md @@ -208,3 +208,11 @@ Append-only. Format: `## [YYYY-MM-DD] 70 chars 130%; Korean 0.8× English), truncate / line-clamp / wrap + `min-width: 0`, logical properties + `[dir=rtl]` glyph flip, `Intl.NumberFormat` / `DateTimeFormat` / `PluralRules`, interrupted-gesture rule (`pointerId` pinning; clear drag state on `pointercancel`, `lostpointercapture`, out-of-bounds `pointerup`, window `blur` — Pointer Events §3.3.7 MUST fire `pointercancel` on stream suppression and implicitly release capture), evidence-naming for gesture tests, error copy with recovery, pending-state submit guard, virtualization. Sources: W3C article-text-size, W3C Pointer Events, MDN logical properties + Intl, Impeccable harden.md pinned to 114ea1d3. Back-links added on responsive-layout and data-fetching/async-ui-states. + +## [2026-09-29] revise | frontend/design/anti-slop-visual-design +2 directive rows (theme browser-default surfaces from the palette on brand pages; shadows carry offset + soft blur — halo and hard-offset are costumes) / +2 edge cases (task UI → product-ui-vs-brand-surface; hostile content or custom drag → ui-hardening-against-real-content) / +6 Instead-of rows (eyebrow, decorative section numbers, geometric occlusion mask, mono as costume, glyph icons, image hover animation). Source: Impeccable craft-floor.md pinned to 114ea1d3. Body 85 → 96 lines. Index load-when extended with browser-surface theming. Prohibition directives: rows written positively, count re-checked by `node scripts/wiki-lint-prohibitions.js` after the edit. + +## [2026-09-29] revise | review round on the Impeccable ingest (general + adversarial reviewer, both fresh-context): anti-slop stray trailing source bullet removed (page keeps sources in frontmatter); anti-slop task-UI edge case widened to Read/Experience surfaces and now names the exact rows it scopes (font pairing, default-font rejection, extreme contrast, page-load motion); responsive-layout fluid-type row scoped to brand/content surfaces with the app-UI alternative; product-ui-vs-brand-surface downgraded to `confidence: unverified` (third-party skill + blog only, no production context) and its trigger now lists mode choice, Read/Experience surfaces, overlay clipping and modal-first; ui-hardening: `overflow-wrap: anywhere` vs `break-word` min-content rule corrected (only `break-word` needs `min-width: 0`), `title` replaced by a hover+focus tooltip, `touch-action` added to the drag row, iOS 16px row defers to responsive-layout, WCAG 1.4.4 / MDN overflow-wrap / hyphens / line-clamp / touch-action / title sources added, IBM 51–70 anomaly noted as published. diff --git a/wiki/frontend/data-fetching/async-ui-states.md b/wiki/frontend/data-fetching/async-ui-states.md index 9a10001..1b40ac2 100644 --- a/wiki/frontend/data-fetching/async-ui-states.md +++ b/wiki/frontend/data-fetching/async-ui-states.md @@ -11,7 +11,7 @@ sources: - https://tanstack.com/query/latest/docs/framework/react/guides/optimistic-updates - https://react.dev/reference/react/Component last_verified: 2026-07-10 -related: [frontend-state-client-vs-server-state, frontend-data-fetching-race-conditions, frontend-data-fetching-query-state-vs-fetch-state] +related: [frontend-state-client-vs-server-state, frontend-data-fetching-race-conditions, frontend-data-fetching-query-state-vs-fetch-state, frontend-design-ui-hardening-against-real-content, frontend-design-product-ui-vs-brand-surface] --- # Designing Loading, Error, Empty, and Data States for an Async View diff --git a/wiki/frontend/design/anti-slop-visual-design.md b/wiki/frontend/design/anti-slop-visual-design.md index fc66de3..6e38587 100644 --- a/wiki/frontend/design/anti-slop-visual-design.md +++ b/wiki/frontend/design/anti-slop-visual-design.md @@ -8,8 +8,9 @@ sources: - "hallmark skill v1.1.0 (anti-AI-slop design skill; distills Anthropic's frontend-design skill, the Claude cookbook on frontend aesthetics, and the 2026 tactile-rebellion consensus)" - "https://claude.com/blog/improving-frontend-design-through-skills — Anthropic Engineering, 2025-11-12 (distributional-convergence mechanism, font avoid-list + taxonomy, extreme-contrast thresholds, domain-derived aesthetic direction, second-order convergence warning; Korean translation: velog.io/@xxziiko)" - "https://github.com/pbakaus/impeccable — README (gray-text-on-colored-background and bounce/elastic-easing anti-patterns; command list unstable across versions, cited for README-level rules only)" -last_verified: 2026-08-21 -related: [frontend-design-html-in-canvas, frontend-accessibility-interactive-elements, frontend-design-responsive-layout, frontend-design-design-canvas-workflow, frontend-design-lightness-steps-on-dark-surfaces, frontend-design-custom-property-values-read-from-script] + - "https://github.com/pbakaus/impeccable/blob/114ea1d3838fca73b253af45f873b9c4f5f213c8/skill/reference/craft-floor.md — Impeccable skill 4.x craft floor, pinned to a commit (browser-surface theming, offset+blur shadows vs halo and hard-offset, eyebrow ban, section numbers, geometric occlusion mask, mono-as-costume, glyph icons, image-hover)" +last_verified: 2026-09-29 +related: [frontend-design-html-in-canvas, frontend-accessibility-interactive-elements, frontend-design-responsive-layout, frontend-design-design-canvas-workflow, frontend-design-lightness-steps-on-dark-surfaces, frontend-design-custom-property-values-read-from-script, frontend-design-product-ui-vs-brand-surface, frontend-design-ui-hardening-against-real-content] --- # Making Web UI Look Designed, Not Generated @@ -69,6 +70,8 @@ Typography, layout, motion, states: | Animate `transform`/`opacity` only, with named easing tokens | Support `prefers-reduced-motion`; cut motion before adding it — spend the whole motion budget on one orchestrated page-load with staggered reveals (`animation-delay`), then content is just there; scattered per-element micro-interactions read as noise. Ease/cubic-bezier tokens only — bounce/elastic easing reads as dated | | Ship all 8 states per interactive element | default · hover · focus-visible · active · disabled · loading · error · success — see [frontend-accessibility-interactive-elements] for the focus rules; the focus ring appears instantly, ≥3:1 contrast | | Prefer silent success and optimistic update + Undo | Toasts are for failures and non-visible async effects; confirmation modals are for irreversible actions only. Tooltip delay: hover ~800ms, focus 0ms | +| Theme the browser's own surfaces from the palette | `::selection`, `caret-color`, `scrollbar-color`, the `:focus-visible` ring, `text-underline-offset` / `text-decoration-thickness`, and `font-variant-numeric: tabular-nums` in data all ship as browser defaults that belong to no design system; setting them from the tokens is the cheapest signal that the page was built rather than assembled, and the one an unsteered generation skips most reliably (brand surfaces only — a product surface keeps standard scrollbars, see [frontend-design-product-ui-vs-brand-surface]) | +| Shadows carry an offset and a soft blur | A zero-offset colored halo is decoration; a zero-blur hard offset (`4px 4px 0`) is a neobrutalist costume that only a world which chose that aesthetic earns | ## Edge cases @@ -80,6 +83,8 @@ Typography, layout, motion, states: | The base design passes and an effect layer is wanted | Canvas/shader effects go on top of a passing base, not instead of one — see [frontend-design-html-in-canvas] | | Writing reusable design guidance (a prompt, skill, or wiki page) rather than styling directly | Pitch it at the "right altitude": name the design axes and their decision logic (pairing categories, contrast thresholds), never exact hex/pixel values (over-constrains) and never "make it look good" (assumes context the model lacks) | | Narrow viewports (verify 320/375/414/768px) | Root `overflow-x: clip` on both `html` and `body`; clickable text stays on one line (shorten the label, then `white-space: nowrap`, then drop the item); image-bearing grid tracks use `minmax(0, 1fr)` | +| The surface is a task UI (dashboard, admin, settings, editor, data table) or a Read / Experience surface (docs, guides, changelog, portfolio, gallery), not a landing or marketing page | Route to [frontend-design-product-ui-vs-brand-surface]: it scopes this page's font-pairing, default-font-rejection, extreme-contrast and page-load-motion rows to Persuade surfaces and replaces them for the others | +| The screen will show user-generated or translated content, or a custom drag control | The visual rules above assume friendly data; run [frontend-design-ui-hardening-against-real-content] before shipping | ## Instead of @@ -96,3 +101,9 @@ Typography, layout, motion, states: | `loading="lazy"` on the hero (LCP) image or video | `fetchpriority="high"` on the LCP element; lazy-load only below the fold | Lazy LCP roughly doubles p75 paint time | | An uppercase eyebrow label (`01 / FEATURES`) on every section | Zero eyebrows by default; only for genuinely ordinal content, 1–2 per page, stacked above the heading in the same column | Decorative numbering erases the hierarchy it claims to create | | Straight quotes, `--`, `...`, "Jane Doe", "Acme" | Curly quotes, `—`, `…`, plausible domain-specific names | Unproofread details mark generated copy | +| A kicker or eyebrow label above a heading | Delete the label and let the heading carry its own weight | The heading already names the section; the label is the one scaffold no brief earns back | +| Section numbers (`01 / 02 / 03`) as decoration | Number sections only when the sequence itself is information the reader needs | Decorative ordinals promise a structure the content does not have | +| A circle, polygon or radial-gradient cutout approximating a photo subject's edge | Derive an alpha matte from the image or produce a real cut-out asset | The geometric approximation reads worse than omitting the effect | +| Monospace for everything "technical" | Mono only for code, data and measurement; the body face for prose about technology | Mono as a costume signals the theme was chosen by category, not by content | +| Emoji or Unicode glyphs standing in for an icon set | One icon library, or authored SVG, in one consistent stroke and weight | Glyph icons vary in weight and color per platform and read as a placeholder | +| A hover animation on an image (scale, tilt, brightness), directly or via its parent | Put the feedback on the container that is actually the action target (border, background, shadow) | An image is not the control; animating it invents an affordance | diff --git a/wiki/frontend/design/product-ui-vs-brand-surface.md b/wiki/frontend/design/product-ui-vs-brand-surface.md new file mode 100644 index 0000000..4b85fcd --- /dev/null +++ b/wiki/frontend/design/product-ui-vs-brand-surface.md @@ -0,0 +1,82 @@ +--- +id: frontend-design-product-ui-vs-brand-surface +domain: frontend +category: design +applies_to: [css, general] +confidence: unverified +sources: + - "https://github.com/pbakaus/impeccable/blob/114ea1d3838fca73b253af45f873b9c4f5f213c8/skill/reference/operate.md — Impeccable skill 4.x, Operate-mode depth (one family, fixed rem scale, 150–250 ms, no page-load sequence, second neutral, all component states, dropdown clipping); pinned to a commit because the repo restructures its reference paths between versions" + - "https://github.com/pbakaus/impeccable/blob/114ea1d3838fca73b253af45f873b9c4f5f213c8/skill/SKILL.src.md — the four visitor modes (Persuade / Operate / Read / Experience) and the rule that the mode comes from the requested surface, not the product" + - "https://claude.com/blog/improving-frontend-design-through-skills — Anthropic Engineering, 2025-11-12; the brand-surface rules this page inverts for product surfaces" +last_verified: 2026-09-29 +related: [frontend-design-anti-slop-visual-design, frontend-data-fetching-async-ui-states, frontend-accessibility-interactive-elements, frontend-design-responsive-layout] +--- + +# Styling a Product Surface Where the Design Serves a Task + +## When this applies + +Deciding which of Persuade / Operate / Read / Experience a surface is; building or +restyling an app surface (dashboard, admin, settings, data table, editor, +authenticated tool) or a docs, guide, changelog, portfolio or gallery surface +rather than a landing page; a brand-surface rule (display/body font pairing, +fluid `clamp()` headings, one orchestrated page-load animation, a committed +accent field) is about to be applied to one of those; a product UI reads +over-designed or subtly strange; a dropdown is clipped by an overflow ancestor; +a modal is the first idea for a task. + +## Do this + +First name the mode of the surface in front of you. The mode comes from the +requested surface, not from the product: a developer tool's landing page is +Persuade; a fashion brand's help center is Read. + +| Case | Do | +|------|----| +| The visitor decides and acts (landing, pricing, campaign) | Persuade — apply [frontend-design-anti-slop-visual-design] as written; expression carries the page | +| The visitor completes a task (app UI, dashboard, editor, admin, settings) | Operate — apply the rows below; scanability, consistency and familiar affordances outrank expression, and the brand lives in precise details | +| The visitor understands something (docs, guides, changelog) | Read — this page's typography and consistency rows plus a 65–75ch prose measure; navigation matters more than component density | +| The visitor is inside the work (portfolio, gallery) | Experience — the artifact leads from the first viewport and the interface recedes; anti-slop rules apply to the chrome that remains | + +Operate-mode rules (rows are ordered general → specific): + +| Rule | Detail | +|------|--------| +| Judge by earned familiarity | The test is whether a category-fluent user trusts the interface at once or pauses at each subtly-off control; the failure mode is strangeness without purpose, not flatness | +| One well-tuned sans for headings, labels, buttons, body and data | A display/body pairing is a brand-surface move; a display face in UI labels, buttons or data cells is the product-UI tell | +| Fixed rem type scale with a 1.125–1.2 ratio between steps | Users view at a consistent DPI and a fluid h1 that shrinks inside a sidebar reads worse; many more type elements share the screen, so exaggerated size contrast becomes noise | +| Restrained color is the floor | Accent goes on primary actions, current selection and state indicators only; give sidebars, toolbars and panels a second neutral a step cooler or warmer than the content surface; inactive states get muted, never full-saturation, color | +| Standardize a state vocabulary once | hover, focus, active, disabled, selected, loading, error, warning, success, info — one token set, reused on every screen | +| Ship every component state | default, hover, focus, active, disabled, loading, error; loading is a skeleton in place of the content, not a spinner in the middle of it; an empty state teaches the interface ([frontend-data-fetching-async-ui-states]) | +| Consistent affordances across screens | Same button shape, same form-control vocabulary, same icon style; when the save button looks different on two screens, one of them is wrong | +| Responsive behavior is structural | Collapse the sidebar, switch the table to a responsive form, change column counts at breakpoints — layout adapts, type stays fixed | +| Motion conveys state, 150–250 ms | State change, feedback, loading, reveal; the surface loads straight into the task with no page-load choreography | +| Standard patterns are permitted here | System fonts, top bar + side nav, breadcrumbs, tabs, command palettes, dense tables — density and sameness screen to screen are virtues on a task surface | + +## Edge cases + +| Case | Then | +|------|------| +| One Operate surface earns committed color (an onboarding welcome screen, a report where one category color carries the data) | Commit on that surface alone and keep Restrained as the floor everywhere else; record the exception next to the token set | +| A dropdown, menu or tooltip is clipped by an `overflow: hidden` / `overflow: auto` ancestor | Let the overlay escape the container: ``, the popover API, `position: fixed`, or a portal — an absolutely positioned child cannot leave a clipping ancestor | +| The brief reaches for a modal | A modal is for a task that needs interruption or protected focus; exhaust inline and progressive-disclosure alternatives first | +| The brand-surface page says to theme scrollbars, selection and the caret | On an Operate surface keep standard scrollbars and form controls; theme only the focus ring, selection color and tabular numerals from the palette — reinvented standard affordances are the product-UI version of decoration | +| The same product needs both a marketing site and the app | Route each surface separately: the site follows [frontend-design-anti-slop-visual-design], the app follows this page; sharing tokens is right, sharing the motion and type rules is not | + +## Instead of + +| If you are about to | Do this instead | Why | +|---------------------|-----------------|-----| +| Pair a display face with a body face in a dashboard | One tuned sans across headings, labels, controls and data | Product UI has more type elements per screen; a second voice reads as noise | +| Size app headings with `clamp()` | Fixed rem steps at a 1.125–1.2 ratio | A heading that shrinks in a narrow panel looks broken, not responsive | +| Open the app with a staggered reveal sequence | Load into the task; spend 150–250 ms on state transitions only | Users arrive mid-flow and wait through choreography every visit | +| Custom scrollbars, hand-drawn select controls, a non-standard modal | Native or platform-standard controls styled from the tokens | Familiar affordances are what let a fluent user trust the screen at once | +| A spinner centered in the content area | A skeleton in the shape of the content | The skeleton keeps layout stable and signals what is coming | +| Full-saturation accent on disabled or inactive controls | Muted tokens for inactive states, accent for the current selection | Saturated inactive states compete with the one action that matters | + +## Sources + +- https://github.com/pbakaus/impeccable/blob/114ea1d3838fca73b253af45f873b9c4f5f213c8/skill/reference/operate.md — product slop test (earned familiarity), one-family typography, fixed rem scale and 1.125–1.2 ratio, "line length still applies for prose (65–75ch)" with Read surfaces taking this file's typography rules, Restrained color floor, state vocabulary, second neutral, all component states, skeleton loading, structural responsiveness, 150–250 ms motion, no page-load sequence, product constraints and permissions, dropdown clipping +- https://github.com/pbakaus/impeccable/blob/114ea1d3838fca73b253af45f873b9c4f5f213c8/skill/SKILL.src.md — Persuade / Operate / Read / Experience modes; "choose the mode from the requested surface, not the product"; modal only for interruption or protected focus (craft-floor reflex) +- https://claude.com/blog/improving-frontend-design-through-skills — the brand-surface rules (font pairing, extreme contrast, one orchestrated page-load) that this page scopes to Persuade surfaces +- Status note: `confidence: unverified` because the only sources are a third-party skill file and a blog post — no official document and no production use of this wiki's own is described. Cross-checked 2026-09-29 against this wiki's anti-slop page and the hallmark v1.1.0 skill, both landing-page-centric (21 macrostructures, 9 hero types, 8 footers, zero product-UI rows). Upgrade to `field-tested` after the first project applies it and records the context here diff --git a/wiki/frontend/design/responsive-layout.md b/wiki/frontend/design/responsive-layout.md index d7f8e7c..999d860 100644 --- a/wiki/frontend/design/responsive-layout.md +++ b/wiki/frontend/design/responsive-layout.md @@ -25,7 +25,7 @@ sources: - https://www.w3.org/TR/css-text-3/ - https://developer.mozilla.org/en-US/docs/Web/CSS/flex-shrink last_verified: 2026-09-06 -related: [frontend-design-anti-slop-visual-design, frontend-accessibility-interactive-elements, frontend-performance-bundle-and-assets, frontend-design-design-canvas-workflow] +related: [frontend-design-anti-slop-visual-design, frontend-accessibility-interactive-elements, frontend-performance-bundle-and-assets, frontend-design-design-canvas-workflow, frontend-design-ui-hardening-against-real-content, frontend-design-product-ui-vs-brand-surface] --- # Making One Layout Work From 320px Phones to Desktop @@ -50,7 +50,7 @@ Work through these in order — each later item assumes the earlier ones hold: | A grid of cards/tiles must reflow by width | `grid-template-columns: repeat(auto-fit, minmax(, 1fr))` — zero media queries; `auto-fit` collapses empty tracks and stretches the rest, `auto-fill` keeps empty tracks | | A component must respond to its container, not the viewport (sidebar vs main placement) | `@container` query with `container-type: inline-size` on the ancestor; keep an intrinsic grid/flex layout as the no-support fallback | | Sizing interactive targets | ≥24×24 CSS px per WCAG 2.2 AA (SC 2.5.8); 44×44 meets AAA (SC 2.5.5). A smaller target is compliant only when a 24px-diameter circle centered on it intersects no other target's circle — see [frontend-accessibility-interactive-elements] for the rest of the interactive contract | -| Fluid type | `font-size: clamp(, , )`, then verify at 200% browser zoom before shipping — a clamp ceiling can stop text from reaching 200% of its original size, which fails WCAG 1.4.4 | +| Fluid type on a brand or content surface | `font-size: clamp(, , )`, then verify at 200% browser zoom before shipping — a clamp ceiling can stop text from reaching 200% of its original size, which fails WCAG 1.4.4. An app / task UI uses fixed rem steps instead ([frontend-design-product-ui-vs-brand-surface]) | | Serving images | `srcset` + `sizes` so the browser picks the resource for the slot's layout width; explicit `width`/`height` attributes on every `` so space is reserved pre-load (prevents CLS; matters most on lazy-loaded images) | | Final gate before shipping | Render at 320px CSS width: all content and functions present with no horizontal scrolling (WCAG 1.4.10 reflow — 320px equals a 1280px desktop at 400% zoom) | diff --git a/wiki/frontend/design/ui-hardening-against-real-content.md b/wiki/frontend/design/ui-hardening-against-real-content.md new file mode 100644 index 0000000..0bba6e7 --- /dev/null +++ b/wiki/frontend/design/ui-hardening-against-real-content.md @@ -0,0 +1,89 @@ +--- +id: frontend-design-ui-hardening-against-real-content +domain: frontend +category: design +applies_to: [css, javascript, general] +confidence: verified +sources: + - https://www.w3.org/International/articles/article-text-size/ + - https://www.w3.org/TR/pointerevents/ + - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values + - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl + - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules + - https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap + - https://developer.mozilla.org/en-US/docs/Web/CSS/hyphens + - https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp + - https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action + - https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/title + - https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html + - "https://github.com/pbakaus/impeccable/blob/114ea1d3838fca73b253af45f873b9c4f5f213c8/skill/reference/harden.md — Impeccable skill 4.x `harden` reference (extreme-input test set, interrupted-gesture rules, error-recovery copy); pinned to a commit because the repo restructures its reference paths between versions" +last_verified: 2026-09-29 +related: [frontend-design-responsive-layout, frontend-data-fetching-async-ui-states, frontend-forms-validation-timing, frontend-accessibility-interactive-elements, frontend-design-product-ui-vs-brand-surface] +--- + +# Hardening a UI Against Real Content, Languages and Interrupted Input + +## When this applies + +A screen was built and checked only with short English placeholder data and is +about to ship; a UI is gaining translations or a right-to-left locale; a field, +list or label will show user-generated content; a custom drag, slider or +scrubber control is being written or reviewed; a diff sets fixed widths on +text containers or hard-codes English plurals and number formats. + +## Do this + +Run the extreme-input set first, then fix what it surfaces with the rows below: +100+ character names, empty and single-character values, emoji and CJK text, +numbers in the millions, 1000+ list items and 50+ options, zero items, and the +same screen at 200% zoom. + +| Case | Do | +|------|----| +| Any text container that will be translated | Budget the expansion by source length (W3C / IBM figures for English → European languages, quoted as published — the 51–70 row is printed above the 31–50 row): ≤10 chars 200–300%, 11–20 180–200%, 21–30 160–180%, 31–50 140–160%, 51–70 151–170%, over 70 130%. Size buttons and labels by padding around the content, never by a fixed width | +| A long single value must stay on one line (a name in a row, a tab label) | `overflow: hidden; text-overflow: ellipsis; white-space: nowrap`, and expose the full value in a tooltip that opens on hover and on keyboard focus — a `title` attribute reaches neither keyboard nor touch users | +| A multi-line preview must stop at N lines | `display: -webkit-box; -webkit-line-clamp: N; -webkit-box-orient: vertical; overflow: hidden` | +| Free text that must wrap | `overflow-wrap: anywhere` — its break opportunities count toward the min-content size, so the flex/grid item can shrink; `break-word` breaks the same words but leaves min-content unchanged, so pair it with `min-width: 0` on the item ([frontend-design-responsive-layout]). Add `hyphens: auto` with the document `lang` set, because hyphenation rules are per language | +| Spacing, borders or icons that point along the reading direction | Logical properties: `margin-inline-start`, `padding-inline`, `border-inline-end`, `inset-inline-start`; flip directional glyphs under `[dir="rtl"]` with `transform: scaleX(-1)` | +| Dates, times, numbers, currency in the UI | `Intl.DateTimeFormat` / `Intl.NumberFormat` with the user's locale; the same value renders `1,234.56` in en-US and `1.234,56` in de-DE from one code path | +| Count-bearing copy ("3 items") | `Intl.PluralRules` or the i18n library's plural forms; a hand-rolled `count !== 1 ? 's' : ''` is wrong outside English | +| A custom drag, slider or scrub control | Set `touch-action: none` (or the axis you keep) on the control so the browser hands it the moves instead of panning; record the `pointerId` that started the drag and act only on that pointer's moves; on `pointercancel`, `lostpointercapture`, a `pointerup` outside the control, or window `blur`, clear the dragging state and release capture — the browser fires `pointercancel` when it takes the stream for a gesture `touch-action` still allows (pan, pinch, palm rejection) and implicitly releases capture with it | +| Confirming a gesture fix | Drive the input in the project's test runner when it can synthesize pointer events; state what produced the evidence (emulated viewport, synthesized touch, engine, or a physical device) and name what stayed untested | +| An error reaches the user | The message names the problem and the recovery, with a retry control; validation errors sit inline by the field and the user's input is preserved ([frontend-forms-validation-timing]) | +| A submit can fire twice | Disable the control while the request is pending and drive the disabled state from the request state, not from a click counter | +| A list can reach thousands of rows | Paginate or virtualize, and add search or filter — rendering all rows at once is the failure the extreme-input set exists to catch | + +## Edge cases + +| Case | Then | +|------|------| +| The source language is Korean, Chinese or Japanese and the target is English | Expansion runs the other way (W3C measured Korean "views" at 0.8× English) and CJK breaks between any two characters — check both the longest and the shortest locale, and read the CJK pill rule in [frontend-design-responsive-layout] | +| An input's font-size is under 16px on iOS | [frontend-design-responsive-layout] owns that fix (raise the input to 16px rather than adding `maximum-scale`) | +| The screen has zero items after the extreme-input pass | The empty, loading and error states belong to [frontend-data-fetching-async-ui-states]; this page owns what happens when data is present but hostile | +| A second finger or pointer lands mid-drag | The first drag keeps its `pointerId` or ends cleanly; it never jumps to the new pointer. After any of the cancel paths above, the next tap or drag works without a reload — test that explicitly | +| Text at 200% browser zoom overflows its container | The container must grow with the text; a fixed height or width on a text box fails WCAG 1.4.4 (text must resize to 200% without loss of content or function) even when the 100% render passed | + +## Instead of + +| If you are about to | Do this instead | Why | +|---------------------|-----------------|-----| +| `width: 6rem` (or `w-24`) on a button so it lines up | Padding-based sizing and a flex/grid row that aligns the buttons | German or Portuguese labels run 1.4–3× the English width and overflow the box | +| `margin-left` / `padding-right` / `border-right` on layout that follows the text direction | `margin-inline-start` / `padding-inline-end` / `border-inline-end` | Physical properties stay put under `dir="rtl"`, so every offset lands on the wrong side | +| Concatenating a number with a locale-specific separator by hand | `Intl.NumberFormat(locale, options).format(value)` | Grouping and decimal separators swap between locales; one code path covers all of them | +| Clearing drag state only on `pointerup` | Clear it on `pointercancel`, `lostpointercapture`, out-of-bounds `pointerup` and window `blur` too | A scroll or a palm touch ends the stream with `pointercancel`, and `pointerup` never arrives — the control stays stuck "dragging" until reload | +| Shipping a generic "Something went wrong" | Name the failure and the recovery, with a retry control | A message without a next action leaves the user at a dead end, which reads as a broken product | + +## Sources + +- https://www.w3.org/International/articles/article-text-size/ — Flickr "views" ratios by language (Korean 0.8, German 2.8, Italian 3.0); IBM average expansion table by English source length (≤10 chars 200–300% … over 70 chars 130%); "the smaller the source message, the higher the likely translation length" +- https://www.w3.org/TR/pointerevents/ — `pointercancel` definition: the user agent MUST fire it when it detects a scenario to suppress a pointer event stream; the "suppress a pointer event stream" steps fire `pointercancel`, `pointerout`, `pointerleave` and implicitly release pointer capture; `lostpointercapture` fires on release; `pointerId` identifies each active pointer +- https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action — "By default, panning (scrolling) and pinching gestures are handled exclusively by the browser. An application using Pointer events will receive a pointercancel event when the browser starts handling a touch gesture"; declaring which gestures the browser keeps is what lets the control receive the moves +- https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap — `anywhere`: soft wrap opportunities introduced by the word break "are considered when calculating min-content intrinsic sizes"; `break-word`: the same breaks, but they "are NOT considered when calculating min-content intrinsic sizes" +- https://developer.mozilla.org/en-US/docs/Web/CSS/hyphens — "Hyphenation rules are language-specific. In HTML, the language is determined by the lang attribute. Browsers will only hyphenate if this attribute is present" +- https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp — requires `display: -webkit-box` and `-webkit-box-orient: vertical` +- https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/title — "Use of the title attribute is highly problematic for: People using touch-only devices, People navigating with keyboards, People navigating with assistive technology" +- https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html — SC 1.4.4: text resizes to 200% without loss of content or functionality +- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values — logical properties are defined relative to the content's writing direction rather than a physical side; Hebrew and Arabic are right-to-left +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl — locale-sensitive number, date and time formatting +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules — plural categories are selected per locale, which is why an English `count !== 1` rule is wrong elsewhere +- https://github.com/pbakaus/impeccable/blob/114ea1d3838fca73b253af45f873b9c4f5f213c8/skill/reference/harden.md — the extreme-input test set, truncate / line-clamp / wrap CSS, `min-width: 0` on flex and grid items, interrupted-gesture handling (second pointer, `pointercancel`, `lostpointercapture`, release outside, `blur`), evidence-naming rule for gesture tests, error copy with recovery, double-submission guard diff --git a/wiki/frontend/index.md b/wiki/frontend/index.md index 3088f79..03dffe0 100644 --- a/wiki/frontend/index.md +++ b/wiki/frontend/index.md @@ -84,7 +84,9 @@ Match your situation to a "load when" line; load only matching pages. | Page | Load when | |------|-----------| | [design-canvas-workflow](design/design-canvas-workflow.md) | ANY task whose deliverable is a visual design the user will react to — a new screen/UI mockup, redesign proposal, design variants or exploration, landing/marketing page draft, mobile prototype, poster/print/report layout — or a new screen is about to be built with no agreed design spec (mandatory routing: when the session lists the `design` skill, the design phase goes through it, never a hand-rolled mockup file; the page carries the no-skill fallback) | -| [anti-slop-visual-design](design/anti-slop-visual-design.md) | Styling or restyling web UI without a design spec; picking the theme/aesthetic direction for a new screen (the committed non-generic direction is the default, not an upgrade); output looks "AI-generated" or template-like; choosing colors, fonts, page structure, or motion for new UI; reviewing a UI diff for template tells; writing reusable design guidance for an LLM | +| [anti-slop-visual-design](design/anti-slop-visual-design.md) | Styling or restyling web UI without a design spec; picking the theme/aesthetic direction for a new screen (the committed non-generic direction is the default, not an upgrade); output looks "AI-generated" or template-like; choosing colors, fonts, page structure, or motion for new UI; reviewing a UI diff for template tells; writing reusable design guidance for an LLM; theming browser-default surfaces (selection, caret, scrollbar, focus ring, tabular numerals) on a brand page | +| [product-ui-vs-brand-surface](design/product-ui-vs-brand-surface.md) | Building or restyling an app/task surface — dashboard, admin, settings, editor, data table, authenticated tool — rather than a landing or marketing page; deciding which of Persuade / Operate / Read / Experience a surface is; a brand-surface rule (font pairing, fluid `clamp()` headings, page-load animation, committed accent field) is about to be applied to a task UI; a product UI reads over-designed or subtly strange; a dropdown is clipped by an overflow ancestor; a modal is the first idea for a task | +| [ui-hardening-against-real-content](design/ui-hardening-against-real-content.md) | A screen verified only with short English placeholder data is about to ship; adding translations or an RTL locale; a field, list or label will show user-generated content (100+ char names, emoji, CJK, thousands of rows); writing or reviewing a custom drag / slider / scrub control (`pointercancel`, `lostpointercapture`, second pointer, window blur); a diff sets fixed widths on text containers, hand-rolls English plurals, or formats numbers and dates without `Intl`; error copy needs a recovery action | | [responsive-layout](design/responsive-layout.md) | Building or reviewing UI that must work across viewport sizes (phone → desktop); choosing breakpoints, touch-target sizes, fluid type, or responsive images; a layout overflows horizontally or breaks on mobile; fixing a zoom/reflow accessibility failure (WCAG 1.4.4/1.4.10/2.5.8); a mobile media query overrides `position` on a container a third-party SDK mounts into and a gap appears only with the SDK loaded | | [html-in-canvas](design/html-in-canvas.md) | Wanting shader/3D/canvas-composited effects on real interactive HTML (forms, buttons, sections); about to hand-draw UI widgets inside a canvas with manual hit-testing; adding a canvas effect layer to an existing page | | [multi-shape-canvas-mask](design/multi-shape-canvas-mask.md) | Masking or clipping canvas content to the union of several shapes with `globalCompositeOperation = 'destination-in'`; painted strokes vanish after a per-shape mask loop; reviewing a loop that applies `destination-in` once per shape |