@ccoalm/ccl-skills 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/references/mobile-quality-release.md +5 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/manual-invocation-and-prompts.md +6 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +5 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/defect-diagnosis/SKILL.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/feature-risk-router/SKILL.md +3 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/architecture-playbook.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/multi-tenant-isolation.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/references/state-machine-task-patterns.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/inference-capacity-operations.md +24 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/llm-client-gateway.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/model-prompt-evaluation.md +4 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/references/contracts-and-state.md +5 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/async-lifecycle-and-performance.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +3 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/metrics-conventions.md +8 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/sli-slo-design.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/canary-and-rollout-strategy.md +16 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/promotion-gate-and-review.md +9 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +12 -12
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/code-review-checklist.md +4 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/delivery-lifecycle.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/rd-standards-doc-family-checklist.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-system-source-of-truth.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/platform-mobile-patterns.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/tokens-and-components.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-audit.md +8 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/multi-tenant-isolation.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/state-machine-task-patterns.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/release-coordination/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +12 -15
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/attention-budget-ratchet.md +37 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/description-authoring.md +13 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +37 -30
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/eval-routing.md +24 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +5 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/rule-consolidation.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +81 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +12 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +30 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-contract-anchors.sh +126 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-size-budget.sh +197 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/contract-anchors.tsv +15 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/eval-routing-bank.rb +210 -36
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +3 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/gate_receipt.py +576 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_antipattern_grep_panel.sh +80 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_body_compliance_grading.sh +99 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +25 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_size_budget.sh +251 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_contract_anchors.sh +196 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_eval_routing_bank_grader_diagnostics.sh +222 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +16 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_frozen_case_sanctity.sh +178 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_frozen_case_sanctity_selfproof.sh +108 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_gate_receipt.sh +431 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_pinned_phrase_mutation_walk.sh +151 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_routing_bank_integrity.sh +86 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +27 -21
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +25 -15
- package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/classical-test-design-techniques.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/tc-review-and-prioritization.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/update-lifecycle.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +9 -9
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/ci-fixtures-and-flake-control.md +5 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/e2e-real-flow-testing.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/integration-contract-testing.md +10 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-code-authoring-patterns.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-topology-and-commands.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +2 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/annotation-driven-revision.md +9 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/figure-and-table-craft.md +8 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/SKILL.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/react-architecture.md +3 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-quality-release.md +37 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-ui-quality.md +10 -1
- package/dist/assets/release.json +127 -67
- package/package.json +1 -1
|
@@ -16,8 +16,14 @@
|
|
|
16
16
|
|
|
17
17
|
**`[禁]` 档(已核,勿再立)**:加粗/高亮密度;每 N 字一图的图表密度(唯一数字是学术期刊的
|
|
18
18
|
**印刷页数配额**,成因是版面成本不是可读性);"一行不超过 40 汉字"引 WCAG(该条的 CJK 40 是从
|
|
19
|
-
拉丁文 80
|
|
20
|
-
|
|
19
|
+
拉丁文 80 折半推导,且原文要求是「提供**机制**让用户改」不是「正文必须排这么宽」;作为**社区规范**
|
|
20
|
+
另有可追溯来源——阮一峰《中文技术文档的写作规范》"多于40个字的句子不能接受"——个人规范可引用,
|
|
21
|
+
不可当标准/厂商级权威);"留白提升理解约 20%"(错误引用链;2026-08 复核仍无任何标准/厂商/学术
|
|
22
|
+
一手源给出该比例)。
|
|
23
|
+
|
|
24
|
+
**降档更正(2026-08 复核)**:"句子不超过 25 词"从 `[禁]` 移出——GOV.UK 官方写作指引有一手源
|
|
25
|
+
("Try to split up sentences that are over 25 words long" + GDS 博客专文),属**单一机构 house
|
|
26
|
+
style**:可引用(点名 GOV.UK),不可当行业标准立硬规则,归 `[工]` 档强度。
|
|
21
27
|
|
|
22
28
|
**同体裁实测分布 ≠ 规范阈值**:可以说"本稿在同体裁公开样本分布的哪个位置",不能由此推出"写得好"。
|
|
23
29
|
分布定位的合法输出是描述不是裁决——这条与 `tighten-doc` SKILL.md「外部基线」条同源,按那条执行。
|
|
@@ -50,6 +50,7 @@ When checking a React project against team standards, split findings into determ
|
|
|
50
50
|
- Locate the owning route/page, component tree, state owner, API client, data-fetching layer, styling system, tests, and build scripts before editing.
|
|
51
51
|
- Identify whether state belongs in URL/query params, cache/server state, form state, local component state, browser storage, or global app state.
|
|
52
52
|
- Read repo wrappers first: package manager, dev/build scripts, lint/typecheck/test runners, browser/E2E tools, environment variables, and generated clients.
|
|
53
|
+
- Before generating component-library code: the **workspace lockfile resolution is the version authority** (`npm ls <pkg>` / `pnpm why` / yarn equivalent — a library config file or global CLI can resolve a different release than the workspace); take configuration ground truth (framework, aliases, installed components) from the library's own introspection surface (config file such as `components.json`, official info CLI/MCP, or the installed package's exports/types); write APIs against the resolved version, never from memory of "current" APIs (prop names and defaults shift across majors). After editing, close with the library's own linter/codemod check on the changed files when one exists (deprecated-usage and a11y rules the generic lint config does not know); for a library major-version migration, follow the official migration checklist + changelog for the exact from→to pair, apply, then re-run the library lint to prove no deprecated usage remains.
|
|
53
54
|
- If a design exists, map visible states and interactions to component ownership before implementing.
|
|
54
55
|
- For every visible UI change, load `../product-ui-ux-design/references/delivery-contract.md` and consume either its full Design brief + Phase 0 or its valid low-risk copy-only record + lightweight Phase 0 before coding. The lightweight path checks semantics, accessible name, localization, rendered extent, and target render without inventing unrelated matrices; risk-bearing copy uses the full path. For full slices, map structure, state/adaptation matrices, behavior and criteria to React ownership; record route/server, component/state owners, viewports/themes/input modes, and preserved behavior. When React is embedded in a native WebView, mini-program `web-view`, or Electron shell, this skill owns the content-layer member; the native/mini/desktop host owner must add its separate entry, binding and runtime record, even when host code is unchanged.
|
|
55
56
|
- Before the first implementation edit, add the canonical `client_entry` defined there: local rule identifier or short quote and implementation decision, target surface/runtime, planned run/capture command, and behavior that must remain unchanged.
|
|
@@ -50,6 +50,9 @@ Three reuse patterns recur in React libraries; pick by what the consumer needs t
|
|
|
50
50
|
- **Custom hook** — when reuse is logic only, no rendering shape required. Default choice for state machines, subscriptions, side-effect orchestration. See discipline above.
|
|
51
51
|
- **Headless / unstyled primitives** (Radix UI, Headless UI, Ariakit, downshift, react-aria) — when reuse is a11y + behavior (focus trap, keyboard navigation, ARIA contract) but every consumer needs different visual treatment. Default for design-system primitives: the primitive owns keyboard / focus / ARIA / portal / dismiss, the consumer owns styling. **Do not invent a custom focus-trap or ARIA implementation when a maintained headless primitive exists** — the bug surface is well-known and the maintained library has fixed bugs you have not heard of yet.
|
|
52
52
|
- **Compound components** (e.g., `<Select><Select.Trigger/><Select.Content/><Select.Item/></Select>`) — when consumers need to arrange children but share an implicit parent context. Cleaner than render props for this case. Default for `Tabs` / `Accordion` / `Select` / `Menu` / `Tooltip` shells.
|
|
53
|
+
- **Type the shared context as a grouped contract — `{ state, actions, meta }`** (data / state-changing functions / refs & config; a component with no refs or config declares `meta` as an optional key — `meta?:` — and consumers handle its absence) rather than a flat grab-bag. Subcomponents consume the contract, never a specific state hook, so any provider implementing it can inject the state — local `useState` for an ephemeral form, a global or server-synced store for a live surface — and the same composed UI works under either provider. One packed context value means a consumer that only calls `actions` still re-renders whenever `state` changes; when that measurably matters, split state and actions into two contexts (the React-docs reducer + context split) instead of one value. The split pays off only when the provided actions value is itself referentially stable — pass `dispatch` directly or memoize the actions object with its complete dependency list, never dropping a changing dependency (an `onSave` prop, a scoped API client) to force identity — that ships stale calls; an inline `{ dispatch }` wrapper takes a new identity on every provider render, and the optimization holds only while the declared dependencies are actually stable; actions must not close over current state (a reducer's `dispatch` qualifies; state-closing memoized callbacks do not), and reducers/updaters themselves stay pure — a side-effecting command that needs the current state snapshot, like a submit posting the form, accepts it as an argument from a state-reading consumer; when actions inherently depend on state, keep the single combined context.
|
|
54
|
+
- **The provider boundary, not the visual shell, decides who can share state.** When consumers outside the visual shell need the state, lift it into a dedicated provider component — the lowest-common-owner rule above still decides how high, and lift only the shareable model `state`/`actions`: each compound root keeps focus refs, generated IDs, and item registration instance-local — open/highlight state stays local by default too, lifting only when outside consumers genuinely coordinate it and then scoped by an explicit compound-instance identity — or two shells under one provider cross-wire focus and ARIA targets; a preview panel or submit button rendered outside the visual shell but inside the provider then reads `state` and calls `actions.submit`. Two smells that say state should have been lifted into a provider: syncing a child's state upward with an on-change `useEffect` callback, and imperatively reading child-owned React state through a ref because another consumer must coordinate with it — uncontrolled DOM values read at submit (`FormData`, file inputs) and imperative third-party-widget integrations are not this smell and stay valid per the uncontrolled-forms rule above.
|
|
55
|
+
- **Mode booleans multiplying on one component are a composition finding.** When a new component API — or an existing API already undergoing an intentional redesign — accumulates mode props (`isEditing` / `isCompact` / `isInline`-style) whose combinations multiply and some are impossible, replace the modes with explicit variant components — each variant composes the shared compound parts it needs and composes under the appropriate shared provider, or declares its own when the variant is the lowest common state owner. The `never`-union prop typing above makes illegal combinations uncallable at the type level; explicit variants remove the combinations altogether. Prefer variants when the modes are stable, meaningfully different compositions or ownership boundaries and conditional render branches, not just prop types, fork on the booleans; modes sharing one structural contract stay one component with a discriminated-union variant prop and exhaustive branching. Do not refactor a stable existing API solely for pattern conformance.
|
|
53
56
|
|
|
54
57
|
**Legacy patterns to recognize, not to reach for**:
|
|
55
58
|
- **Higher-Order Components (HOCs)** — `withAuth(Component)` / `connect(mapStateToProps)(Component)`. For the same concern in NEW code, prefer a custom hook (`useAuth()` / `useSelector()`). Existing HOC APIs are acceptable when a library / framework contract requires them (React-Redux `connect` is still a valid public API) — do not refactor working HOC integrations on cosmetic grounds. Do not introduce a new HOC when a hook does the same job.
|
|
@@ -20,6 +20,20 @@ For API-backed pages, centralize these mechanics in the client or the narrowest
|
|
|
20
20
|
| User error mapping | Show readable state near the affected region; keep raw transport details for logs/devtools, not UI copy | Component test asserts visible message and retry/next action |
|
|
21
21
|
| Long work | Expose task id/status, polling/retry/backoff, stale result protection, refresh recovery, and final status | API-client test plus browser smoke for one pending-to-final flow |
|
|
22
22
|
|
|
23
|
+
## Streaming Client Mechanics (SSE / WebSocket Chat, Live Logs, Progressive Results)
|
|
24
|
+
|
|
25
|
+
For AI-chat, live-log, or progressive-result surfaces consuming SSE/WebSocket streams, centralize these behaviors in the stream client wrapper (visible streaming *states* are enumerated in `references/complex-workspace-patterns.md`; provider-call retry/continuation semantics are owned by `llm-inference-integration/references/llm-client-gateway.md`):
|
|
26
|
+
|
|
27
|
+
- **Retry asymmetry**: automatic connection retry (bounded exponential backoff plus a connect timeout) is safe only when establishing the connection cannot re-trigger the operation — i.e. when subscribing/reconnecting to an existing server-issued operation or stream id, or when the send carries an idempotency key with an acknowledged dispatch boundary. Once a stream has consumed ANY event whose handler has a non-idempotent effect — a start/processing event that fired analytics or stateful handlers counts, not only visible deltas or dispatched tool events — automatic resubscription additionally requires a resume position — a resume cursor / SSE `Last-Event-ID` whose delivery semantics are verified strictly-after (an inclusive cursor or ambiguous-disconnect boundary replay re-delivers the boundary event), or paired with stable per-event ids plus client-side deduplication and idempotent side-effect handling — because a server that replays history or delivers at-least-once around the disconnect hands the new attempt events the stale-attempt guard reads as current, double-applying text, analytics, or tool side effects; without a resume mechanism, terminate for explicit user retry instead, and cover the overlapping-replay-after-disconnect case with a regression test. Per-tab attempt tokens, latches, and dedup state do not span browsing contexts: when two tabs can subscribe to the same operation, side-effectful handlers (tool dispatch, analytics, mutations) need server-side idempotency keyed by operation/event id as the primary control; a client-side single-executor election is an acceptable supplement ONLY with an atomic exclusive mechanism — the Web Locks API, or a lease with expiry and a fencing token the server validates — because two tabs reading ordinary shared state can both observe "no executor" and both claim it; without one of these, both tabs will independently execute the same replayed events. A POST-style send that starts billed generation or dispatches work server-side before the first delta arrives must NOT be auto-resubmitted on connect timeout or lost response — and a lost acknowledgement is an UNKNOWN dispatch, not a failure: the server may have accepted the operation, so the user-driven retry must reuse the original idempotency key or first reconcile the operation's server-side status before a new send is allowed, or the "safe" manual retry double-charges and repeats tool side effects; and never blind-retry a stream that already emitted visible deltas or dispatched tool calls — a replay duplicates them (the partial-stream discipline on the provider side owns when continuation is safe; ambiguous provider-call retry policy routes to `llm-inference-integration`). When no eligible resume/deduplication mechanism exists — or its retry budget is exhausted — mid-stream failure becomes a terminal error state with an explicit user-driven retry/regenerate, never an automatic reconnect-and-replay.
|
|
28
|
+
- **Guaranteed terminal state**: when the current attempt's transport closes — mid-stream error, abnormal close, or a NORMAL close — without an already-latched recognized terminal event, and the close is not eligible for automatic resubscription (per the retry-asymmetry rule above) or the reconnect budget is exhausted, synthesize a local terminal event and push it through the same message pipeline, so every streamed message provably reaches done/error/canceled (cover the clean-close-after-unknown-status case in a regression: a newer server emitting an unrecognized terminal status then closing normally must not leave the operation pending forever). While an eligible resubscription is still in budget, supersede the closed attempt first, suppress its terminal callback, and keep the operation visibly pending until the final transport attempt resolves — latching failure on a transient disconnect fires terminal side effects that a successful reconnect then cannot cleanly undo. A spinner that can never resolve on mid-stream drop is the recurring production bug this prevents. Terminal state is a per-attempt latch: the first end/error/canceled transition for an attempt wins, closes/aborts that attempt's transport, and every later callback for that attempt token — later terminal, close, and error callbacks AND ordinary data/tool events from a late open or buffered delivery on the same transport — is rejected; one failing connection can fire error then abnormal-close, or deliver a buffered delta after the UI went terminal, and without the full-coverage latch those double-fire state transitions, analytics, or tool-result handling.
|
|
29
|
+
- **Stale-stream guard covers synthesized events too**: tag each connection attempt with a monotonic token; every consumer — incoming stream events AND the locally synthesized terminal/close/error callbacks above — checks the token and drops anything from a superseded attempt, and abort the previous `AbortController` before opening a successor. Aborting the predecessor fires its own close/error handler, so an unguarded synthesized terminal event from the dead attempt overwrites the successor's UI state; an intentional abort of a superseded attempt emits nothing, and user cancellation of the *current* attempt is modeled as `cancel-requested` until the transport confirms teardown — if a buffered server `end` arrives first, resolve the operation as completed (the work may already be done and billed) and surface that instead of a `canceled` state whose retry would repeat the charge; only an unconfirmed teardown synthesizes `canceled` (branch on the abort reason, not just on "closed abnormally").
|
|
30
|
+
- **Reconnect policy is declared data, not scattered constants**: `{enabled, maxRetries, initialDelayMs, maxDelayMs, connectTimeoutMs, idleTimeoutMs}` on the transport wrapper; connect-timeout fires its own event so the UI distinguishes "still connecting" from "failed", and the idle/heartbeat timeout catches the proxy-buffered stall where the connection opened successfully but no event, close, or error ever arrives — without it the terminal-state guarantee (which triggers on closure) never fires and the UI stays pending forever.
|
|
31
|
+
- **Transport behind one interface, with declared degradation**: WS/SSE/polling behind a single event-surface interface so SSE-hostile networks (buffering proxies, restrictive gateways) degrade to polling without changing consumers — degradation is conditional on the server actually exposing the fallback protocol; when no polling/read endpoint exists (a WebSocket-only backend), a blocked transport resolves to the explicit unsupported/network-error terminal state rather than emitting semantically incompatible requests. Fetch-based SSE libraries need deliberate settings: background-tab behavior, abort wiring, and suppressing the library's built-in auto-retry when the wrapper owns retry policy.
|
|
32
|
+
- **Status enum → UI state via one lookup table**: map the wire message-status enum (e.g. start/processing/executed/end/error/canceled) to loading/disabled/retry UI in one table — not scattered conditionals per component. An unknown status maps to an explicit unsupported/error presentation with telemetry, keeping send/retry controls conservatively disabled until a recognized terminal event resolves the operation; treating unknown as "not loading" re-enables actions while the server may still be working and double-fires them.
|
|
33
|
+
- **Reload recovery is server-replayed history** through the same message pipeline with an `isHistory` flag that suppresses side effects (no tool re-execution, no analytics double-fire); add client-side partial-message persistence only when the product genuinely needs offline drafts.
|
|
34
|
+
- **Client-executed tool/action failures report back into the agent loop** as a terminal tool result; a console-only error leaves the backend agent waiting forever — that is a hang, not an error state.
|
|
35
|
+
- **Trace propagation applies mid-stream**: read the trace id from the stream response headers or first event and attach it to the message state, so a broken stream is supportable like any failed request.
|
|
36
|
+
|
|
23
37
|
## Test Layers
|
|
24
38
|
|
|
25
39
|
- Unit tests: pure mapping, validation, reducers, derived data, error translation, and utility behavior.
|
|
@@ -33,12 +47,30 @@ For API-backed pages, centralize these mechanics in the client or the narrowest
|
|
|
33
47
|
- Check bundle growth, code splitting, image optimization, caching headers, long tasks, unnecessary rerenders, list rendering, hydration/runtime errors, and Core Web Vitals risk.
|
|
34
48
|
- Build-time environment variables must be explicit; do not hide production behavior behind local-only defaults.
|
|
35
49
|
- Release checks should include build, smoke route, error monitoring, analytics event sanity, rollback or feature flag path, and cache invalidation/CDN implications when relevant.
|
|
50
|
+
- **Request waterfalls are a top-priority review category** (Vercel's impact-ranked React guidance grades sequential-to-parallel fixes as the 2-10× class): start independent async work before awaiting and join with `Promise.all`, defer an `await` into the branch that actually consumes it, avoid chained sequential awaits in route handlers / server components / effects, and place Suspense boundaries so the shell streams while slow regions resolve.
|
|
51
|
+
- **Bundle-size discipline**: import concrete module paths instead of barrel files (a barrel import can pull the whole package into the graph), keep import paths statically analyzable for tree-shaking, dynamic-import heavy or below-the-fold components, and preload on user intent (hover/focus) rather than eagerly.
|
|
52
|
+
|
|
53
|
+
## SEO And Discoverability (Public-Facing Pages)
|
|
54
|
+
|
|
55
|
+
Applies to marketing, content, docs, landing, and any page meant to be found via search or unfurled in social/chat previews. Auth-gated consoles and internal workbenches are exempt, but their public shells (landing, login, share/preview pages) are not. Grounded in Google Search Central, `schema.org`, and `ogp.me` (verified 2026-08); when in doubt those docs are authoritative, not memory.
|
|
56
|
+
|
|
57
|
+
- **Indexable pages need a unique, descriptive `<title>` and meta description per page** — no boilerplate duplicated across routes, no keyword stuffing. For SPAs this means route-level title/description management (framework metadata API or a head manager), not one static `index.html` title.
|
|
58
|
+
- **Variants whose primary content is duplicate or substantially equivalent declare `rel=canonical`** to pick the representative URL and consolidate signals; the canonical page carries a self-referential one. A parameterized URL is NOT automatically a duplicate — a filter/category landing page with distinct indexable content keeps its own canonical (canonicalizing it away tells the crawler it is non-representative and can drop it from results); decide indexability per parameterized route family, not by the presence of query parameters.
|
|
59
|
+
- **Keep-out uses `noindex` (meta tag or `X-Robots-Tag`), and the page must NOT be blocked by robots.txt** — a robots.txt block hides the `noindex` from the crawler; robots.txt manages crawl traffic, it does not remove pages from the index. Blocking a should-be-indexed page's JS/CSS in robots.txt also breaks rendering-based indexing.
|
|
60
|
+
- **XML sitemap when the site is large, new, or weakly interlinked**; a small (~<500 well-linked pages) site usually doesn't need one.
|
|
61
|
+
- **Structured data as JSON-LD** (Google's recommended format) with `schema.org` vocabulary for surfaces that qualify for rich results (articles, products, FAQs, breadcrumbs); validate with the Rich Results Test rather than eyeballing.
|
|
62
|
+
- **Social preview: the Open Graph required set is `og:title`, `og:type`, `og:image`, `og:url`** (add `og:image:alt` with any `og:image`); with incomplete Open Graph metadata the preview becomes incomplete or platform-dependent (platforms may fall back to ordinary HTML metadata or their own tags). Verify the unfurl with the target platforms' debuggers when share traffic matters.
|
|
63
|
+
- **Multilingual pages use bidirectional `hreflang`** (every variant lists itself plus all others — non-reciprocal tags are ignored), absolute URLs, and `x-default` for unmatched languages. Localized variants with distinct content (different languages, or regional pages with real differences) keep their own self-referential canonical — canonicalizing translations to the default-language URL marks them non-representative and removes them from localized results, defeating the hreflang set; same-language regional near-duplicates may share a canonical representative per the duplicate rule above, and hreflang then annotates the canonical set.
|
|
64
|
+
- **Client-rendered React has JS-SEO constraints**: Google renders JS in a deferred headless-Chromium queue, so (1) navigation must be real `<a href>` links (router `<Link>` components render one; click-handler-only navigation is invisible to crawling), (2) no fragment-based routing for indexable content — use History API paths, (3) soft-404s need a real 404/410 status (or a JS-injected `noindex` when the server can't), (4) primary content must not require user interaction to load, and (5) Google itself still recommends SSR/pre-rendering for critical content ("not all bots can run JavaScript"). For content-heavy public surfaces this is an SSR/SSG/Islands adoption signal per `Framework-Era Patterns` below.
|
|
65
|
+
- **Core Web Vitals are used by Google's ranking systems** (good thresholds: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1, at the 75th percentile of field data) and mobile-first indexing is fully rolled out — the mobile rendering is the indexed one, so mobile CWV and mobile-rendered content parity are the release-gate numbers for public pages.
|
|
66
|
+
- **Verify deterministically**: Lighthouse SEO category, the Rich Results Test, or a fetch-and-parse check beats hand review; for changed public routes assert title/description/canonical/OG in a component test or the browser smoke — asserting canonical VALUE per route class, not mere presence: distinct indexable pages self-canonicalize, duplicate variants resolve to their intended representative, and the canonical target is a successful indexable URL (not a redirect or error) — a presence-only check stays green while every route canonicalizes to the home page and drops out of results; keep the check in CI for the marketing/content app rather than re-auditing by hand each release.
|
|
67
|
+
- Primary pages per rule cluster (all under `developers.google.com/search/docs` unless noted): titles/descriptions → `appearance/title-link`, `appearance/snippet`; canonical → `crawling-indexing/consolidate-duplicate-urls`; noindex vs robots → `crawling-indexing/block-indexing`, `crawling-indexing/robots/intro`; sitemap → `crawling-indexing/sitemaps/overview`; structured data → `appearance/structured-data/intro-structured-data`; social preview → `ogp.me`; hreflang → `specialty/international/localized-versions`; JS SEO → `crawling-indexing/javascript/javascript-seo-basics` and `.../lazy-loading`; CWV/mobile-first → `appearance/page-experience`, `appearance/core-web-vitals`, `crawling-indexing/mobile/mobile-sites-mobile-first-indexing`.
|
|
36
68
|
|
|
37
69
|
## Build Tooling Currency
|
|
38
70
|
|
|
39
71
|
- **Vite 8 (released 2026-03) is the current stable**, shipping Rolldown as the default Rust-based bundler (10-30x faster builds, full plugin compatibility per Vite's own announcement). **Vite 7** (Node.js 20.19+/22.12+ required, Node 18 dropped; ESM-only distribution) remains the previous stable line for teams not yet ready for the Rolldown migration trade-off (Rust-toolchain debugging, plugin compat audit). Migration cadence: upgrade Node first if pinned to Node 18; then plan Vite 7 → 8 with a build-perf + plugin-compat smoke before flipping defaults. The historical `rolldown-vite` opt-in package is no longer the path forward — Vite 8 ships Rolldown directly.
|
|
40
72
|
- **Tailwind CSS v4 (stable Jan 2025) uses CSS-first configuration**: no `tailwind.config.js` for new projects; configure via `@theme { --color-brand: ... }` directives in the same CSS file that imports Tailwind. The first-party Vite plugin + automatic content detection eliminates most config; full builds 5x faster, incremental 100x faster than v3. Migration from v3 to v4 is not free (config shape, plugin API, some utility-class differences) — the migration guide is authoritative; do not infer migration steps from training-data memory.
|
|
41
|
-
- **Biome v2 (June 2025, "Biotype") for formatter + linter** when the team wants ESLint + Prettier collapsed into one Rust tool. Type-aware linting without TypeScript compiler dependency (Biome implements its own type inference for lint rules — this does NOT replace `tsc --noEmit` in CI for full type-check);
|
|
73
|
+
- **Biome v2 (June 2025, "Biotype") for formatter + linter** when the team wants ESLint + Prettier collapsed into one Rust tool. Type-aware linting without TypeScript compiler dependency (Biome implements its own type inference for lint rules — this does NOT replace `tsc --noEmit` in CI for full type-check); 520+ lint rules (many inspired by other linters — verified 526 as of 2026-08); CSS formatting/linting stable since v1.9; `biome migrate eslint` automates most config porting. Trade-off: smaller plugin ecosystem than ESLint; if the project depends on niche ESLint plugins (custom org rules, framework-specific lint), audit those before migrating. ESLint flat config + Prettier remains valid for teams with deep ESLint plugin investment.
|
|
42
74
|
- **React Compiler ESLint plugin**: when React Compiler is enabled (per `react-architecture.md`), keep `eslint-plugin-react-hooks` at the version that ships the compiler-aware preset; the recommended preset enforces the rules Compiler relies on (no mutating props, deterministic render, etc.). Without it the compiler may silently skip components that violate its assumptions.
|
|
43
75
|
|
|
44
76
|
## Framework-Era Patterns (When To Adopt, When To Defer)
|
|
@@ -50,7 +82,7 @@ Several React-adjacent architectures became visible in industry conversation (RS
|
|
|
50
82
|
- **Adoption condition**: the app is on a framework/runtime with **production-supported RSC and Server Functions enabled for this app path** — Next.js App Router in RSC mode is the canonical example; other frameworks ship RSC as opt-in or experimental on specific paths. Framework-native loaders / actions (Remix / React Router framework mode, Modern.js, RedwoodJS) are valid server data patterns but are NOT automatically RSC / Server Actions — `loader` / `action` / `useFetcher` are their own primitive. Mixing hand-rolled RSC into a Vite + React Router v6 SPA is a research project, not a delivery path.
|
|
51
83
|
- **Where RSC pays off**: content-heavy pages with mostly read traffic and small mutation surface (marketing, docs, dashboards with a stable read shape, list/detail screens). The data dependency moves to the server; the client bundle shrinks; first-load latency drops.
|
|
52
84
|
- **Where it does not**: highly-interactive workbench surfaces (the chart/table workbenches in `react-architecture.md`'s heavy-dashboard pattern), live-collaboration editors, anything whose interactivity-to-content ratio is high. The "use server" / "use client" boundary becomes a maintenance tax that outweighs the SSR win.
|
|
53
|
-
- **Server Actions / Server Functions** (`'use server'`) are async network calls with serializable args, designed for mutations / form POST paths where the framework owns revalidation. They can replace some fetch-based form submissions on RSC frameworks (progressive enhancement: form works without JS). They are NOT a general data-fetching replacement and they are not meant for retrofitting the API surface on a non-RSC framework.
|
|
85
|
+
- **Server Actions / Server Functions** (`'use server'`) are async network calls with serializable args, designed for mutations / form POST paths where the framework owns revalidation. They can replace some fetch-based form submissions on RSC frameworks (progressive enhancement: form works without JS). They are NOT a general data-fetching replacement and they are not meant for retrofitting the API surface on a non-RSC framework. **Every Server Action is a publicly reachable HTTP endpoint regardless of where it sits in the component tree — authenticate and authorize inside each action body**; UI-level gating or "the button is only rendered for admins" is not access control. On Next App Router, declare fetch caching semantics explicitly (`cache` / `next.revalidate` per the pinned major's docs) rather than relying on implicit defaults, which have changed across majors.
|
|
54
86
|
- **Streaming SSR with `<Suspense>` boundaries** is independent of RSC and works in plain React 18+: choose Suspense boundaries that match user-visible regions (header / nav / primary content / secondary panel) so the slowest region does not block the rest. Boundary placement is design — every fallback geometry must match the resolved region or layout jumps when the fallback resolves. Pair with `ErrorBoundary` at the same node — Suspense alone catches loading, not failure.
|
|
55
87
|
- **Don't pick the framework for the buzz; pick it for the deploy story.** Many SaaS / admin / data-console apps already work on Vite + React Router + TanStack Query and would gain little from migrating to App Router; the migration cost (auth, middleware, build pipeline, hosting, third-party SSR compatibility) is real.
|
|
56
88
|
|
|
@@ -81,7 +113,7 @@ When the front-end build output (HTML, JS, CSS, assets) is served from object st
|
|
|
81
113
|
- **Storage layout**: the build artifacts of each lane live under a stable prefix; the lane prefix is the unit of isolation. The standard lane (production baseline) has its own well-known prefix. The router resolves an inbound request to a `(lane, prefix, path)` tuple and serves the matching object.
|
|
82
114
|
- **Bucket separation by trust**: protected lanes (canary, preview, tenant-isolated, internal) and the public production baseline MUST live in separate buckets — the production baseline can be public-readable for CDN performance; protected lanes MUST be in a private bucket with identity-bound or network-bound origin access control. Mixing public and protected lanes in one bucket is a finding, because any per-prefix policy is easy to misconfigure into a public exposure.
|
|
83
115
|
- **Origin access control choice depends on the topology, but the lane-resolution contract is the same**: when a managed CDN fetches directly from object storage, use the provider's CDN-bound origin-access mechanism (S3 OAC bound to CloudFront, OSS-CDN equivalent) — but direct CDN-to-bucket is valid only for immutable public baseline assets that need no lane resolution, token exchange, manifest verification, or response-header control. Any path that needs lane resolution / preview-token validation / response-header policy / structured logging MUST go through the router; routing it directly via CDN bypasses the entire trust model. When a custom router proxies bytes from object storage, the router authenticates with its own IAM / service-account identity that has the minimum required permissions, and the bucket policy restricts access to that identity (and to the CDN-bound identity if a CDN sits in front). Do not rely on `Referer`-based bucket policies — `Referer` is client-set and trivially spoofable by non-browser clients (curl, scripts, native apps). The "TOS equivalent" or any other provider's equivalent must be identity-bound or network-bound and resistant to client-supplied header spoofing.
|
|
84
|
-
- **Immutable artifact prefixes**: each deployment writes to a fresh content-addressed prefix (e.g. `<commit-sha>/`); existing prefixes are never overwritten. The deploy step uploads all artifacts, verifies them against a generated build manifest (per-file expected size + content checksum via the provider's authoritative checksum API — `x-amz-checksum-*` on S3, equivalents on OSS / GCS / TOS), and only then flips the active manifest to the new prefix. ETag is not a reliable content checksum for multipart uploads — do not equate ETag match with content match.
|
|
116
|
+
- **Immutable artifact prefixes**: each deployment writes to a fresh content-addressed prefix (e.g. `<commit-sha>/`); existing prefixes are never overwritten. The deploy step uploads all artifacts, verifies them against a generated build manifest (per-file expected size + content checksum via the provider's authoritative checksum API — `x-amz-checksum-*` on S3, equivalents on OSS / GCS / TOS; for multipart uploads use the full-object checksum type where the algorithm/provider supports it; when only a parts-composite checksum is available, do not compare it against the manifest's whole-file digest — verify by independently reading back the completed object and hashing it with the manifest's algorithm), and only then flips the active manifest to the new prefix. ETag is not a reliable content checksum for multipart uploads — do not equate ETag match with content match.
|
|
85
117
|
- **Artifact retention and GC**: old immutable prefixes are kept for at least `max(CDN cache TTL, browser cache TTL, service-worker cache TTL, rollback window)` after they stop being active. Premature GC orphans live clients still holding old HTML that references old chunks → blank screens and chunk-load errors. Retention policy is part of the deployment contract, not an afterthought; GC is a separate scheduled job with explicit safety margins, never tied to "deploy succeeded" timing.
|
|
86
118
|
|
|
87
119
|
### Trust Model For Lane Selection
|
|
@@ -170,7 +202,7 @@ For portfolios with multiple React apps sharing a back-end:
|
|
|
170
202
|
|
|
171
203
|
- PR gate must run remotely (GitLab CI / GitHub Actions / equivalent), not only as a local pre-commit hook. Pre-commit catches obvious lint/format problems; PR-level gates catch what locally was skipped, what dirty branches drag in, and what the merge introduces.
|
|
172
204
|
- Required PR gate stages, in this order: install (frozen lockfile) → type check (`tsc --noEmit`) → lint → unit/component tests → build. Skipping any of these because "the local hook ran" is a recurring source of broken main branches.
|
|
173
|
-
- Husky + lint-staged + `verify-commit` are the local layer; they enforce style and message format. They are not a substitute for the PR gate.
|
|
205
|
+
- Husky + lint-staged + `verify-commit` are the local layer; they enforce style and message format. They are not a substitute for the PR gate — local hooks vanish under `--no-verify`, and correctness lint rules downgraded to `warn` severity gate nothing anywhere; rules that matter are `error` in the remote gate.
|
|
174
206
|
- Lane / environment separation in CI (online vs offline lanes, canary vs production deploy targets) belongs at the deploy stage, not at the build stage. Build artifacts should be lane-independent where possible; lane choice is a deploy-time variable.
|
|
175
207
|
- A portfolio without remote PR gates is a deliberate trade-off (fast iteration, small team, single owner) or an oversight; document which. Adding PR gates retroactively to ten apps is expensive — start one app at a time with the lightest gate (type check + lint + build) and expand.
|
|
176
208
|
|
|
@@ -188,3 +220,4 @@ A portfolio with **zero unit tests, zero component tests, and zero E2E** across
|
|
|
188
220
|
- Dead dependencies (declared in package.json with zero imports) are a recurring finding when multiple authors edit shared starter templates. Lint or a per-CI step (e.g., `depcheck`) catches them.
|
|
189
221
|
- Per-app `@umijs/max` / `next` / `vite` major-version skew (`4.0.x` to `4.4.x` across apps in the same portfolio) is a finding; cap the skew and run a periodic bump pass on the lowest-pinned app.
|
|
190
222
|
- Generated outputs (OneAPI clients, Hertz/Kitex stubs, OpenAPI clients) check into the repo and run formatter-skip rules; CI should fail when generated output is stale relative to the IDL/schema source.
|
|
223
|
+
- Optional deterministic React health scan: `react-doctor` (npm; by the react-scan/Million authors; young 0.x line — verified real and maintained 2026-08) scans correctness/performance/security/accessibility and outputs a 0–100 score; `--scope changed` restricts findings to files changed vs the base branch, which makes a cheap "score must not regress" PR ratchet. Supplement to — not replacement for — the ecosystem ESLint/typed-lint executors in `testing-strategy/references/fitness-functions.md` §4.1.3; pin the version in CI rather than `@latest`, and never wire any scanner mode that fetches and obeys remote playbook prompts at run time (remote-instruction execution is an untrusted-content hole, per `llm-inference-integration` trust rules).
|
package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-ui-quality.md
CHANGED
|
@@ -12,11 +12,19 @@
|
|
|
12
12
|
- Behavioral: pending, disabled, retry, cancel, duplicate-submit, offline/stale, partial, and interrupted states are visible where relevant.
|
|
13
13
|
- Psychology: the screen reduces uncertainty, waiting anxiety, fear of mistakes, and loss of control.
|
|
14
14
|
|
|
15
|
+
## Design-Token Diff Review
|
|
16
|
+
|
|
17
|
+
Before declaring a visible UI change complete, review the diff itself against the design-token source (design-system tokens, theme variables, documented semantic values — token/`DESIGN`-doc ownership per `product-ui-ux-design`):
|
|
18
|
+
|
|
19
|
+
- Scan the changed paths for likely hard-coded visual values, e.g. `rg -n '#[0-9a-fA-F]{3,8}\b|rgba?\(|hsla?\(|box-shadow:|linear-gradient|font-family:' -- <changed-paths>` — keep the `--` option terminator and collect the path list without shell word-splitting against an explicit review range — `git diff --name-only -z <base>...HEAD` for committed changes, `git diff --cached --name-only -z` for staged ones (a bare `git diff --name-only` sees only unstaged edits and silently yields an empty list on a committed candidate — treat an empty path list as a failed scan, not a pass) — so a repository-controlled filename starting with `-` cannot be parsed as an `rg` option (`--pre` would execute content instead of scanning it). Tune the pattern to the styling system (Tailwind arbitrary values `[...]`, styled-components literals, inline `style=`).
|
|
20
|
+
- Classify every hit three ways: (1) approved token/primitive usage or a documented exception (e.g. chart-adapter series colors); (2) pre-existing code outside the requested change — do not report it as caused by this change, and do not copy it either: an old component carrying a non-compliant value is not permission to repeat that value; (3) a new violation — fix it by referencing the theme/token primitive instead of restating its literal value.
|
|
21
|
+
- If the requested visual result cannot be achieved with the defined tokens/patterns, stop and route the gap to the design-system owner (`product-ui-ux-design`'s design-system source-of-truth path) instead of silently inventing a new color, radius, shadow, or type style; record the escalation in the change.
|
|
22
|
+
|
|
15
23
|
## Accessibility
|
|
16
24
|
|
|
17
25
|
- Prefer semantic HTML before ARIA. Use buttons for actions and links for navigation.
|
|
18
26
|
- Every interactive control needs an accessible name, keyboard reachability, visible focus, and a clear disabled/loading state.
|
|
19
|
-
- Forms need labels, validation messages associated with fields, error summary when useful, and keyboard-friendly submit/retry behavior.
|
|
27
|
+
- Forms need labels, validation messages associated with fields, error summary when useful, and keyboard-friendly submit/retry behavior. Form micro-contract worth enforcing in review: never block paste; keep submit enabled while the user is still editing, but latch an accepted submission synchronously — before the first await of validation/serialization/token acquisition — so a second click or Enter cannot dispatch twice, releasing the latch on the submission's terminal state (failure, and success too for reusable same-page forms like chat/comment/search); Enter submits the focused input and ⌘/Ctrl+Enter submits from a textarea; validate after input instead of blocking free typing; on failed submit, focus the first invalid field; warn before route-leave with unsaved changes; stay password-manager/2FA compatible (real inputs with `autocomplete` attributes).
|
|
20
28
|
- Modals, popovers, menus, and drawers need focus management, escape/outside-click behavior where appropriate, and return focus on close.
|
|
21
29
|
- Check contrast, text wrapping, zoom/text scaling, and screen reader names for icon-only controls.
|
|
22
30
|
|
|
@@ -25,6 +33,7 @@
|
|
|
25
33
|
- Define stable dimensions for fixed-format UI such as grids, toolbars, tables, boards, and controls.
|
|
26
34
|
- Define the primary viewport, stress viewport, and collapse rule before coding. Secondary panels, filters, metadata, and previews should collapse or dock before the primary reading/editing region becomes unusable.
|
|
27
35
|
- Use virtualization or pagination for large lists/tables when rendering cost becomes visible.
|
|
36
|
+
- Numeric floors worth enforcing in code review: interactive hit targets ≥ 24×24 CSS px (WCAG 2.2 SC 2.5.8 AA — spacing/inline/UA-default exceptions exist; platform HIGs recommend ~44-48 px on touch), mobile input font-size ≥ 16px (prevents iOS focus zoom), never disable user zoom (`user-scalable=no` / `maximum-scale=1` in the viewport meta is an accessibility finding), and flex/grid text truncation needs `min-width: 0` on the shrinking child or `overflow` clipping never engages.
|
|
28
37
|
- Preserve useful URL state for shareable/filterable pages.
|
|
29
38
|
- Handle back/forward, reload, auth expiry, offline/stale data, and browser storage expiry deliberately.
|
|
30
39
|
- Long task and upload/review pages should model route-leave, reload, polling timeout, stale task, and terminal failure as component states when the user could lose work or confidence.
|