← Commit history

New sub-skill: input-feedback - the acknowledgment doctrine (hover/press/focus states on every interactive element; John's ruling 2026-08-18)

John Lauer ·2be38c3b53 ·2mo ago ·parent 088c229
3 files changed +146−2
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-ui-design",-  "version": "1.5.0",+  "version": "1.5.1",   "type": "skill",   "description": "The Adom UI Design System plus a bundle of focused UI skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps, clickable-paths, dark-default). One install drops the whole set of skill files onto your container.",   "dependencies": {},
page.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-ui-design",-  "version": "1.5.0",+  "version": "1.5.1",   "type": "skill",   "description": "The Adom UI Design System plus a bundle of focused UI skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps, clickable-paths, dark-default). One install drops the whole set of skill files onto your container.",   "dependencies": {},
skills/input-feedback/SKILL.mdadded+144
@@ -0,0 +1,144 @@+---+name: input-feedback+user-invocable: true+description: >-+  The acknowledgment doctrine: every interactive element must answer the human's INPUT itself -+  "yes, you just hovered me" (a subtle border/background shift) and "yes, you just pressed me"+  (a real mousedown/:active state), plus a keyboard :focus-visible ring - BEFORE and separate from+  whatever the action later does. Covers the three acknowledgment moments (hover, press, focus),+  copy-paste CSS recipes on the house tokens for buttons, icon buttons, pills, toggles, rows and+  inputs, the timing rules (hover ~150ms, press instant), touch behavior, disabled/busy states,+  the ralph-test checklist, and the anti-patterns (hover-only buttons, color-only shifts,+  outline:none). Read BEFORE shipping ANY clickable UI. Trigger words: input feedback, button+  feedback, press state, pressed state, mousedown state, active state, :active, hover state,+  hover effect, acknowledge click, acknowledge hover, button not responsive, buttons feel dead,+  click feedback, tap feedback, focus ring, focus-visible, tap highlight, button press animation,+  ui acknowledgment, responsive buttons.+---++Parent skill: **adom-ui-design**++# Input feedback - the UI acknowledges the human++**The doctrine (John, 2026-08-18, verbatim):** the UI must "acknowledge the effort of the human+to click something or hover something ... they are getting feedback from the ui that they just+performed an action and the ui is saying back to them with subtlety of 'yes, you just hovered me'+or 'yes, you just pressed me' - i.e. a mousedown state on a button when the user pushes, or a+slight border treatment upon hover."++This is DISTINCT from "every action answers back" (toasts, state changes - see the `toasts`+sub-skill). That is acknowledgment of the OUTCOME. This skill is acknowledgment of the INPUT+itself, which happens EARLIER: the finger goes down, and before any request fires or any state+changes, the element visibly yields. A button with no pressed state feels dead even when the+action behind it works perfectly - the human is left asking "did that register?" for the whole+round-trip. Input feedback is what makes an interface feel like a physical object instead of a+picture of one.++## The three acknowledgment moments++Every interactive element gets all three. No exceptions, no "it's just a small button".++1. **Hover** - "you found me." A SUBTLE shift: border color up one step, background up one+   surface, or text brightening. One or two properties, ~150ms ease. Hover says "I am+   interactive and I noticed you" - it must never shout.+2. **Press (mousedown / touchstart)** - "you pushed me." The element visibly compresses the+   INSTANT the pointer goes down: translateY(1px) + scale(~0.97-0.98) + a darker fill. This is+   the moment users mean when they say a UI feels responsive. It fires on mousedown, NOT on+   click - the acknowledgment must not wait for the release.+3. **Keyboard focus** - "you reached me without a mouse." `:focus-visible` (never bare+   `:focus`) with a 2px accent outline, offset 2px. Removing outlines without a replacement is+   an accessibility bug, full stop.++## Copy-paste recipes (house tokens)++Assumes the standard tokens (`--border #30363d`, `--surface #161b22`, `--elevated #1c2128`,+`--overlay #21262d`, `--accent #00b8b1`). Adapt values, keep the STRUCTURE: hover = one step up,+active = compress + one step down.++```css+/* Standard button */+.btn{transition:background .15s ease,border-color .15s ease,transform .06s ease}+.btn:hover{background:var(--overlay);border-color:#4b5563}          /* border treatment on hover */+.btn:active{transform:translateY(1px) scale(.98);background:var(--surface)}  /* the press */+.btn.primary:active{background:#009d97;border-color:#009d97;transform:translateY(1px) scale(.98)}++/* Icon-only button: compress harder, it has no label to carry the change */+.iconbtn{transition:background .15s ease,border-color .15s ease,transform .06s ease}+.iconbtn:hover{background:var(--elevated);border-color:var(--border)}+.iconbtn:active{transform:scale(.9);background:var(--overlay)}++/* Pills / chips / segmented choices */+.pill:hover{border-color:#4b5563;background:var(--elevated);color:var(--text)}+.pill:active{transform:translateY(1px) scale(.97)}++/* Toggle: hover ring + iOS-style knob stretch while held */+.toggle:hover{border-color:var(--accent)}+.toggle:active::after{width:22px}          /* knob elongates under the finger */+.toggle.is-on:active::after{left:17px}     /* stretch inward from the on side */++/* Interactive rows / list items (ONLY if actually clickable) */+.row:hover{border-color:#4b5563}++/* Inputs */+input:focus-visible{outline:none;border-color:var(--accent)}++/* Keyboard reachability - one rule for everything clickable */+.btn:focus-visible,.iconbtn:focus-visible,.pill:focus-visible,.toggle:focus-visible{+  outline:2px solid var(--accent);outline-offset:2px}+```++## Timing rules (these make or break the feel)++- **Hover: ~150ms ease.** Slow enough to feel smooth, fast enough to feel attributed to you.+- **Press: INSTANT - 60ms transform transition or less.** A press that eases in over 150ms reads+  as lag, not acknowledgment. Put `transform .06s ease` in the base transition list so the press+  snaps and the release springs back on the same short curve; colors may keep the 150ms.+- **Never put a long transition on `:active` properties.** The #1 way to build a press state+  that still feels dead.++## Touch++- `:active` fires on touch contact (the historical 300ms tap delay is gone on modern mobile) -+  the same CSS covers tap feedback. Verify it on a phone width anyway.+- Set `-webkit-tap-highlight-color: transparent` ONLY when your own :active state exists -+  removing the platform's acknowledgment without providing yours is how taps go dead on mobile.+- Elaborate hover effects belong behind `@media (hover: hover)` so they cannot stick "on" after+  a tap on touch screens. A simple border/background hover is harmless either way.++## Disabled and in-flight++- **Disabled elements acknowledge NOTHING**: no hover, no press (`pointer-events` intact for the+  tooltip explaining WHY it is disabled, but visual state frozen, opacity ~0.5,+  `cursor:not-allowed`). Acknowledging input on a control that cannot act is a lie.+- **In-flight (pressed, action running)**: hold a working state - label swap ("Starting...") or+  an inline spinner - until the outcome lands (then the toast/state change takes over; see+  `toasts`). The gap between press-acknowledgment and outcome-acknowledgment must never be+  visually empty on slow actions.++## Ralph-testing input feedback++- **The checklist**: for EVERY interactive element - button, icon button, pill, toggle, row,+  link, input - confirm a visible change on (a) hover, (b) mousedown HELD (press and hold it:+  the compressed state must persist while held), (c) keyboard Tab focus. If any of the three+  produces no visible change, it fails.+- **CSS `:active` and `:hover` only respond to TRUSTED input** - a JS+  `dispatchEvent(new MouseEvent('mousedown'))` will NOT trigger them, so you cannot fake the+  verification from page script. Drive real input (CDP Input dispatch / pup move+click verbs) or+  eyeball it by hand. A real-input `move` onto the element + screenshot proves hover; press+  states need held real input or a human eye.+- Static, non-interactive elements (status cards, labels) get NO hover state - a hover shift on+  something unclickable is a fake affordance and will draw dead clicks.++## Anti-patterns (each of these shipped somewhere and got called out)++- **Hover-only buttons** - the exact failure that created this skill: the fusion Dashboard+  shipped with `:hover` and zero `:active` states, and John pressed buttons that never pressed+  back ("why are none of your buttons responsive when i push them down?").+- **Color-only 5% shifts** - too subtle to register as acknowledgment. Pair a color step with a+  border or transform change.+- **`outline: none` without `:focus-visible` replacement** - keyboard users lose the interface.+- **Transition-lagged presses** - see timing rules.+- **Hover states on static content** - fake affordances.++Reference implementation: `dashboard/ui/index.html` on adom/fusion-bridge (v1.9.60) - every+interactive element carries all three moments on the house tokens.