SDK 1.4.0: WebMCP tool registration
GEO Docs
Design language

Interaction

State model, hit targets, response timing, feedback levels, destructive actions, hide versus disable, forms, keyboard, tooltips, bulk selection, drag and drop, copy, navigation and touch gestures.

Input methods

Pointer, touch and keyboard are equal. Hover effects live only inside @media (hover: hover); touch devices never depend on hover to reveal information (checkboxes and drag handles that appear on hover are always visible on touch).

State model

Every interactive element defines these states.

StateOptical meaningAppearanceTiming
RestUnlitAs specified—
HoverCatches lightCards +1 level, up 1px, specular light; controls get --state-hover (ink 5%, dark 7%); primary button → brand-hoverFade 150ms
PressedPressed toward the tableScale 0.97 (cards 0.99), −1 level, --state-press (ink 9%, dark 12%)Snappy
Focus-visibleA beam2px brand ring, offset 2px, converging from outside; keyboard focus onlySnappy
SelectedLight passes throughbrand-tint fill + on-brand-tint text + check; refracts in from the click pointSnappy
CurrentA lit slabCurrent sidebar item: surface + elev-1 + brand icon + weight 600—
DraggedLifted toward the lightLevel 3, scale 1.02, grabbing cursorSmooth
DisabledOpaqueOpacity 0.40; no hover or press; cursor: not-allowed; the reason must be visible (tooltip)—
LoadingLight sweepingSpinner only after 300ms; once shown, at least 400ms; button keeps its width, label hidden—
InvalidA red banddanger 2px ring + message below the field + iconFade
Read-onlyTransparentNo container fill, text in ink-2—

Hit targets

CaseMinimum
Pointer32 × 32
Touch44 × 44 (visually smaller elements get a transparent hit area)
Spacing between targets8

Response timing

DurationFeedback
< 100msImmediate state change, no loading indicator
100ms – 1sNothing for the first 300ms; then an inline spinner at the trigger, shown for at least 400ms to avoid flicker
1 – 10sSkeleton (light sweep) on first load; a progress bar when progress is known
> 10sMove to a background task: a "Moved to background" toast + task center + an inbox notification when done

Small reversible actions (toggles, mark as read, reorder, favorite) update optimistically: change the UI at once, roll back on failure and explain why in a toast.

Feedback levels

From light to heavy: in the field (validation text) < status text ("Saved 10:32") < page banner (quota, announcements, permissions) < toast (action result, dismisses itself) < dialog (a decision is needed; blocking). Pick the lightest level that says it clearly, and use only one level for one event.

Destructive actions

KindApproach
Reversible (delete a draft, remove from a group, archive)Do it immediately + a toast with Undo, 6s, timer paused while the pointer is over it
Irreversible (permanently delete an article, revoke access)Confirmation dialog: the title names the object; the body states the consequence; danger button on the right, Cancel on the left
High impact (delete a site, delete a company, clear a cluster)Confirmation dialog + the danger button enables only after the object's name is typed

Hide or disable

SituationApproach
No permissionHide (useCan())
Temporarily unavailable (quota used up, state doesn't allow it)Disable + a tooltip with the reason and how to fix it
Form incomplete or invalidDon't disable submit; on click, show the errors and focus the first one

Forms

  1. Validate a field on blur, and only after it has been edited; once it has an error, re-check as the user types and clear the error as soon as it's fixed.
  2. On submit, validate every field, then scroll to and focus the first error.
  3. Put the message under the field and say how to fix it ("A domain can't contain spaces"), never just "Invalid format".
  4. With unsaved changes, a frosted save bar (level 3) rises from the bottom: "Unsaved changes [Discard] [Save]"; confirm before leaving the page.
  5. Editors autosave with a 2s debounce, with the status in the top bar: "Saving…" → "Saved 10:32"; on failure the status turns danger and offers Retry.

Keyboard

ScopeKeysAction
Global⌘K / Ctrl KSearch and command palette
Global/Focus the current list's search box
GlobalEscClose the topmost layer (tooltip → menu → dialog → drawer)
Global?Keyboard shortcut help
List↑ / ↓Move the current row
ListEnterOpen the current row
ListSpaceSelect / deselect the current row
ListShift + clickSelect a range
Tabs / segmented← / →Switch
MenuFirst letterJump to a matching item
Dialog—Focus the first field on open; trap focus inside; return focus to the trigger on close

Tab order matches visual order; a "Skip to main content" link is the first focus stop on every page.

Tooltips

  • Only for icon-only buttons, truncated text and disabled reasons. Never put interactive content in a tooltip.
  • Appears after the pointer rests 500ms; appears immediately when moving to a neighbour within 300ms; disappears immediately on leave; appears immediately on keyboard focus; Esc closes it.
  • Inverse surface (inverse fill + on-inverse text), r-xs, caption size, max width 260.

Selection and bulk actions

  • Desktop: checkboxes appear on row hover (always visible once anything is selected); touch: always visible.
  • With ≥ 1 item selected, a frosted bulk bar (level 3, r-full) rises at the bottom center: "3 selected · [Actions…] · Clear"; count changes use the bouncy spring.
  • Select-all selects the current page only, and the bulk bar offers "Select all 128".

Drag and drop

  • A handle (grip-vertical) appears on hover; on touch, long-press 500ms to lift.
  • Lifted: level 3, scale 1.02; other items make room with the smooth spring; the drop position shows as a brand-tint placeholder.
  • Auto-scroll within 48px of the container's top and bottom edges.
  • Keyboard alternative: the row menu's Move up / Move down / Move to top.

Copy

After copying, the icon turns into a check and the label reads "Copied" for 1.5s; when the clipboard is unavailable, select the text and say "Press ⌘C to copy".

  • Filters, sort, tab, page and any open drawer are in the URL, so refreshing and sharing keep the state.
  • Going back restores scroll position and filters.
  • Navigation always uses real links (they can open in a new tab); external links carry a ↗ icon and open in a new tab.

Touch gestures (compact class)

GestureThresholdResult
Direction lock8px from the startLock to horizontal or vertical to avoid mis-touches
Swipe right from the left edgeStarts ≤ 20px from the edgeOpens the navigation drawer
Close a drawer / bottom sheetMoved > 30%, or release velocity > 0.5px/msCloses; otherwise springs back (smooth)
Release pointJudge by the velocity-projected end point (deceleration 0.998/ms)Not where the finger stopped, but where the motion would land
Long press500msOpens the row menu (or starts a drag)
Pull to refreshNot usedUse the refresh button in the header

Empty, error and loading

StateAppearance
First loadA skeleton shaped like the final layout (light sweep), never a full-page spinner
RefreshKeep the old data, show a spinner only at the trigger; cross-fade in the new data
Empty (never had data)The empty template: 64 icon tile + title + explanation + primary action
Empty (no filter results)One sentence + "Clear filters", no large icon
ErrorA danger icon tile + the reason + "Retry" inside the card; a full-page message only when the whole page fails
No permissionName the role needed and who to contact; never show a 403 code