From interaction-and-control-principles
Use this skill when designing or fixing controls that mislead users — elements that look interactive but aren't (false affordance), or controls that need to clearly communicate "you can't do that here" (anti-affordance, like disabled states). Trigger when reviewing UIs where users tap inert elements expecting response, when designing disabled states, when picking treatments for read-only fields, or when a "decorative" element is being mistaken for a button. Sub-aspect of `affordance`; read that first.
How this skill is triggered — by the user, by Claude, or both
Slash command
/interaction-and-control-principles:affordance-false-and-antiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The mirror images of standard affordance: the elements that *look* interactive but aren't, and the elements that *are* interactive but should signal "not right now." Both deserve as much design attention as standard affordance — and both are routinely neglected.
The mirror images of standard affordance: the elements that look interactive but aren't, and the elements that are interactive but should signal "not right now." Both deserve as much design attention as standard affordance — and both are routinely neglected.
A false affordance is an element whose visual treatment suggests interactivity it doesn't have. The user is led to expect an action; the action doesn't happen; trust drops.
Common sources:
Anti-affordance is the deliberate design of cues that tell the user "this is interactive in principle but unavailable to you right now." The most common case: disabled controls.
A disabled control needs to:
cursor: not-allowed).<button class="btn" disabled
aria-disabled="true"
title="Add at least one item to checkout">
Continue to checkout
</button>
.btn[disabled] {
background: hsl(0 0% 92%);
color: hsl(0 0% 50%);
cursor: not-allowed;
opacity: 0.7;
}
.btn[disabled]:hover {
/* no change — this is the anti-affordance */
background: hsl(0 0% 92%);
}
A common UX mistake: disabling a button silently when the user "isn't ready" (form has unfilled required field; cart is empty). The user sees a non-responsive button and doesn't know why.
Better:
<button class="btn">Continue to checkout</button>
<p class="hint">Add at least one item to your cart to continue.</p>
The user can read the hint and act; the button isn't a dead end.
A field that's input-shaped but not editable is a special anti-affordance case.
<label>Account ID
<input type="text" value="acct_8f3c7a" readonly />
</label>
input[readonly] {
background: hsl(0 0% 96%);
color: hsl(0 0% 30%);
cursor: text; /* still selectable for copy */
border: 1px solid hsl(0 0% 90%);
}
The treatment says "this is data, not a control" — neutral background, no focus outline activation, but still selectable so users can copy.
For fields that aren't currently editable but could be (e.g., locked behind a permission), the visual treatment should hint that editability exists in principle.
A subtle modern source: animating a non-interactive element. A heading that wiggles, an icon that pulses, a card that slowly tilts. These attract attention; users assume attention means interactivity.
If the animation is decoration only, audit whether it's drawing attention away from real interactive controls. If it's a callout for new content, ensure clicking does something — even if just to dismiss the callout.
<!-- Clickable: card-link wraps, hover responds -->
<a href="/projects/acme" class="card-link">
<article class="card">...</article>
</a>
<!-- Inert: no link wrap, no hover response -->
<article class="card card--inert">
<h3>System status</h3>
<p>All services operational.</p>
</article>
.card { padding: 16px; border: 1px solid hsl(0 0% 88%); border-radius: 8px; }
.card-link { display: block; text-decoration: none; color: inherit; }
.card-link:hover .card { border-color: hsl(220 90% 60%); box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
.card--inert {
background: hsl(0 0% 98%); /* slightly different bg signals "informational" */
border-color: hsl(0 0% 92%);
}
.card--inert:hover {
/* no change — anti-affordance */
}
The clickable card shifts on hover; the inert one stays still. Users learn quickly that hover-responsive cards are clickable and others aren't.
Three subtly different states, each with its own anti-affordance:
<!-- Read-only: data, never editable here -->
<label>Workspace ID
<input value="ws_abc123" readonly />
</label>
<!-- Disabled because of state: editable when condition is met -->
<label>Email
<input value="[email protected]" disabled aria-describedby="email-locked" />
</label>
<p id="email-locked" class="hint">Verify your email to enable changes.</p>
<!-- Unavailable due to permission: visible but not actionable -->
<label class="field-locked">
<span>Workspace name <LockIcon aria-hidden /></span>
<input value="Acme Inc" readonly />
<p class="hint">Only workspace admins can change this.</p>
</label>
The user gets specific anti-affordance signals for each: read-only data is visually demoted; conditionally-disabled is enabled-looking-but-with-explanation; permission-locked has a lock icon and explanation.
affordance (parent).affordance-signifiers — the standard case of making affordance visible.feedback-loop — disabled controls should still give some feedback (cursor change at minimum).accessibility-understandable (process) — disabled states must be programmatically conveyed (disabled attribute, aria-disabled).npx claudepluginhub hdeibler/universal-design-principles --plugin interaction-and-control-principlesGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
Dispatches multiple subagents concurrently for independent tasks without shared state. Use when facing 2+ unrelated failures or subsystems that can be investigated in parallel.