Back to Lobehub

UX — Design Values & Execution Checklists

.agents/skills/ux/SKILL.md

2.2.614.6 KB
Original Source

UX — Design Values & Execution Checklists

How LobeHub products should feel, and concrete rules to get there. Use this when building or reviewing any user-facing flow. For component/styling choices see react, for wording see microcopy, for imperative modal wiring see modal.

Design values

LobeHub follows four product design values — Natural・Meaningful・Certainty・ Growth. Read them before designing: references/design-values.md (definitions + conflict priority).

The checklists below are the execution layer. Each item is tagged with the value(s) it serves; for what those values mean, see the file above.

How this is organized

The checklists are grouped by interaction type — the kind of thing the user is doing. Jump to the module that matches the surface you're building (reading a list, editing content, running an action, …); each module collects the rules specific to that interaction. The same surface often spans several modules (an editable list is Read + Edit + Act) — walk each that applies.


1. Read — viewing data & lists

Any surface that displays records, lists, or detail. Covers the states a data view can be in, behavior at scale, and keeping the user's place visible.

1.1 Data states: empty / loading / error・Meaningful・Certainty

Every data surface has four states — design all of them, not just "has data".

  • Empty state is a purpose-built page, not a blank screen. It explains what this is, why it's empty, and gives a clear next action (CTA + value props). ✅ Devices: an empty "Connect your first device" page with primary/secondary connect paths and "what you can do once connected" cards — ❌ not a bare title over skeleton rows or a blank body. (Meaningful)
  • Distinguish the empty variants — "no data yet" (onboarding CTA) vs "no match for filters" (clear-filters affordance) are different screens. (Certainty)
  • Loading state designed (skeleton / NeuralNetworkLoading), not a flash of blank or layout shift. (Natural)
  • Error state designed — surface the reason and a retry/back path. (Meaningful)

1.2 Lists at scale・Certainty・Natural

A list/data page must be designed for its whole range of sizes, not just the demo data.

  • Walk the scale: 1 / 2 / 5 / 20 / 100 / 1k–10k rows. Pick the right mechanism per range — plain render → load-more / pagination → virtual scroll; add batch-select / bulk actions once counts get large. (Certainty)
  • Co-design empty / loading / error with the data state (see §1.1). A list isn't done until all four render well. (Natural)

1.3 Selection visibility in scrolled lists・Certainty・Natural

A capped / scrollable / virtualized list mounts at scrollTop = 0. If the active item sits below the fold, the user lands on a valid selection that is off-screen — and reads it as "nothing is selected" or a broken page. Any list that can open with a pre-selected item must scroll that item into view. This is an easy case to miss: it only shows up once the list is long enough and the selection is restored rather than freshly clicked.

  • Scroll the active item into view on mount / restore. When the selection is restored from a URL query, deep link, or persisted state (not a fresh click), bring it into view — the container starts at the top otherwise. ✅ The nested thread list is capped to ~9 rows; a thread restored from ?thread= below the fold is scrolled into view on mount. (Certainty)
  • Hardest when the selection has no other anchor. If the parent/container row isn't highlighted while a child is active (no breadcrumb, no header echo), an off-screen active row means zero visible feedback — design for exactly this case. (Meaningful)
  • Use block: 'nearest' (or equivalent). Only scroll when the row is actually off-screen; an already-visible selection must not jump. (Natural)
  • Re-run once async rows mount. The active id is usually known before the list finishes loading; key the scroll off a list-ready signal (e.g. row count), not only off the id, so a restored selection still lands when the data arrives. (Certainty)
  • Mirror it across duplicated list variants so the behavior can't regress in just one (e.g. parallel agent / group lists). (Certainty)

1.4 Option visibility in pickers・Certainty・Meaningful

  • Pickers list every valid target. Watch for options dropped by backend list queries (pagination, virtual flags, scope filters) and add them back. ✅ The default "LobeAI" (inbox) agent is virtual and excluded from the sidebar list, so the move picker re-adds it. An empty picker must mean "genuinely none", never "we filtered out the only option". (Meaningful)

2. Edit — entering & changing content

Any surface where the user types or edits. Input is expensive effort; the overriding rule is never lose it.

2.1 Protect in-progress edits・Certainty・Meaningful

Typed / edited content is real user effort; losing it is one of the most infuriating outcomes a product can produce. Whenever an editor holds unsaved input, assume the exit can be accidental — a misclick, a refresh, a crash, a navigation, a failed save — and build a safety net: back the draft up locally and recover it.

  • Back up the draft locally as the user types. Persist to localStorage / IndexedDB / store so a refresh, crash, accidental close, or navigation doesn't vaporize the content. (Certainty)
  • Restore on return. Coming back to the same editing context auto-restores (or offers to restore) the unsaved draft, rather than showing a blank field. (Meaningful)
  • Guard destructive exits. Closing / navigating / switching items away from a dirty editor warns or auto-saves — never silently discards. (Certainty)
  • Survive a failed save. If the save errors, keep the user's content in the field / draft and let them retry; never clear the input on failure. (Meaningful)
  • Scope the draft to its target (per topic / message / item id) so drafts don't bleed across entities or resurrect on the wrong item. (Certainty)

3. Act — operations, flows & buttons

Any surface where the user performs an action — a single op, a bulk op, or a multi-step flow. Covers momentum, focus, and full entity lifecycle.

3.1 Flow & momentum・Natural・Meaningful

Every action chain must push the user forward, never dead-end or block the flow.

  • Forward momentum — after any operation, lead the user to the next step, don't just stop. (Meaningful)
  • Success state = primary "go to result", secondary "dismiss" — the strong button is the forward action (take me to the result); "Done" is the weak/ secondary button. ✅ After moving topics: primary = "Go to «target»", secondary = "Done". (Meaningful・Natural)
  • Bulk ⇄ single-item parity — an action on a multi-select toolbar must also be reachable on a single item (its context menu), and vice versa. (Certainty)
  • Confirm → in-progress → done, in one surface — bulk/irreversible/async ops use a modal state machine: a confirm step stating exactly what happens → an in-progress view with dismissal locked → a done (or error) view in the same modal. Never fire-and-forget with only a toast; never leave a dead spinner. (Certainty・Meaningful)

3.2 One primary button per surface・Certainty

  • One primary button per surface. The single primary CTA tells the user the core action; everything else is secondary/tertiary. Never a pile of primary buttons competing for attention. (Certainty)

3.3 Entity lifecycle completeness・Meaningful・Certainty

The recurring trap: a feature ships only the display of a list, but edit / delete / management are never built — so the user can add something and then be stuck with it. For every entity a user can see, design its full lifecycle: create / read / update / delete, plus state transitions (enable/disable, connect/disconnect, install/uninstall). A read-only list the user can't manage breaks the flow.

The allowed operation set depends on the entity's source / ownership — decide it explicitly before building. Worked example, the tools/connectors list:

Entity classAddEditRemove
Official / built-in (skills, tools)✗ not removable
Community (installed MCP)installconfigureuninstall / remove
User-custom (custom connector)createeditdelete
  • No display-only features. For every listed entity, enumerate CRUD + lifecycle ops and build the ones that apply. (Meaningful)
  • Operation set per source/ownership class — built-in may be read-only; anything the user installed must be removable; anything the user created must be editable and deletable. (Certainty)
  • Each item exposes its allowed ops (hover action / context menu / detail page), and there's a clear entry point to add/create where applicable. (Natural)
  • An intentionally-absent op is a documented decision, not an oversight (e.g. official tools can't be deleted — by design). (Certainty)

4. Feedback — loading & system response

How the product answers back while and after the user acts — loading visuals and proactive guardrails.

4.1 Loading visuals・Natural

Never use antd Spin — it doesn't match the product's loading visual. Use a project loader:

NeedComponent
Default loading (in-flight)NeuralNetworkLoading from @/components/NeuralNetworkLoading (size prop)
Inline dotsDotsLoading / BubblesLoading from @/components
Branded full-pageLoading from @/components/Loading/BrandTextLoading
List / card placeholdera skeleton (e.g. SkeletonList)

When in doubt, reach for NeuralNetworkLoading — it's the default in-flight indicator (e.g. modal "in progress" states).

4.2 Capability-gated features・Certainty・Meaningful

A feature can be fully built and still produce a broken result when the selected model — or its still-loading config — can't deliver the capability the feature depends on (for example, an agentic run on a model without tool calling). This is usually the user's configuration choice, not a defect; but if the product stays silent the user reads it as the product being broken. When a feature's success depends on a capability the current config may lack, the product owes a proactive, non-blocking reminder — a guardrail, not a gate.

  • Surface the mismatch, don't fail silently. When a feature needs a model capability (tool calling, vision, reasoning, long context) the current model lacks, show a soft inline warning at the point of action — never a hard block or a modal that stops the user. (Meaningful)
  • Stay reactive. The reminder clears the moment the user switches to a capable model — derive it from live state, not a one-shot check. (Natural)
  • Don't warn while config is loading. A capability that hasn't resolved yet looks "unsupported"; warning then is a false alarm — exactly the glitch users mistake for a product bug. Warn only on a resolved unsupported state. (Certainty)
  • Scope to the mode that needs it. Show only when the capability-dependent mode is on; one reminder per root cause, never a pile of overlapping notices. (Natural・Certainty)
  • State the problem and the remedy. The copy says what's wrong and what the user should do about it. (Meaningful)

5. Grow — discoverability & progressive disclosure

How the product deepens as the user's needs deepen.

5.1 Progressive disclosure・Growth

The product should grow with the user — deeper power shows up as needs deepen.

  • Progressive disclosure — keep the novice path clean; reveal advanced capabilities as the user gets there, don't dump everything at once. (Growth・Natural)
  • Surface related actions at the moment of need — make the next capability discoverable in context (e.g. after the first item exists, offer what to do with it), not buried in a far-off menu. (Growth・Meaningful)

Quick review checklist

Read — viewing data & lists

  • Empty / loading / error states are all designed; empty is a real page with a CTA.
  • List designed across 1 → 10k rows (virtual scroll / pagination / batch as needed).
  • Capped/scrollable/virtualized list scrolls the restored active item into view on mount (block: 'nearest', re-run after async rows mount).
  • Pickers show all valid targets (default/inbox included); empty = truly none.

Edit — entering & changing content

  • Editors back up in-progress input locally and recover it after refresh/crash/failed-save; destructive exits warn, never silently discard.

Act — operations, flows & buttons

  • Action leads the user forward; success offers a primary "go to result".
  • Bulk action has a single-item entry (and vice versa).
  • Async/bulk/irreversible action: confirm → in-progress (locked) → done/error.
  • Exactly one primary button per surface.
  • Listed entities have their full lifecycle (not display-only); ops match source (built-in / installed / custom).

Feedback — loading & system response

  • No antd Spin; use NeuralNetworkLoading / project loaders.
  • Capability-gated feature warns (soft, reactive, load-gated) when the model can't deliver it; copy gives the remedy.

Grow — discoverability & progressive disclosure

  • Advanced capability is progressively disclosed / discoverable at the moment of need.
  • modal — imperative createModal state-machine wiring for confirm/progress/done.
  • microcopy — wording for confirm / done / empty / error states.
  • react — component priority, Button usage, styling.