Skip to content

Icon button

In review
Live · web

Plain: toolbar / inline actions (tab, click them)

Disabled: inert, still visible

Filled circle: the send / stop slot (the only true circle)

Click handler fired 0 times. The island is live.

// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform; there is
// no shipped icon button yet, so this is the intended @dizyx/nockerl-react API, mirroring
// the canonical Compose shape.)
import { NockerlIconButton } from '@dizyx/nockerl-react';
import { MoreVertical, Plus, Send, Square } from 'lucide-react';
export function SessionToolbar() {
return (
<div className="toolbar">
<NockerlIconButton icon={<MoreVertical />} label="More options" onClick={openMenu} />
<NockerlIconButton icon={<Plus />} label="Add session" onClick={createSession} />
{/* The send / stop slot, the only filled-circle idiom */}
<NockerlIconButton icon={<Send />} label="Send message" variant="filled-circle" onClick={send} />
<NockerlIconButton icon={<Square />} label="Stop generating" variant="filled-circle" accent="error" onClick={stop} />
</div>
);
}
PropTypeDefaultDescription
icon *ReactNodeThe glyph to render (inline SVG node - e.g. <NockerlIcon .../>).
label *stringAccessible name (REQUIRED - the only thing that names an icon-only control).
variantNockerlIconButtonStyle'plain'The icon-button idiom. Defaults to plain.
accentstring'var(--color-accent-primary)'Circle fill for filled-circle (defaults to the cyan accent token). Pass a var(--token).
sizenumber40Touch-target dimension in px. Defaults to 40 (Compose default).
pressedbooleanTOGGLE mode: when set, the icon button HOLDS state: renders aria-pressed and, when true, the pressed VISUAL is the selection outline (a selection-weight cyan ring plus a cyan glyph, and no fill, design-laws section 6). The primary home for icon toggles (toolbar toggles, wrap, etc.). Omit for a plain command icon button.

Also accepts the native <button> attributes (e.g. onClick, disabled, id, aria-*, data-*), forwarded straight through, plus a forwarded ref.

Cross-platform parity (published packages)

The published Compose (com.dizyx.nockerl.design.components) and Swift (NockerlDesign) packages now share both idioms and the a11y contract. The earlier drift is resolved:

  • Both idioms ship. Swift has .plain and .filledCircle (the send/stop slot), matching Compose/web, no longer plain-only.
  • Control radius is tokenized. The .plain idiom consumes NockerlRadius.control (12pt); .filledCircle uses the pill radius. The hardcoded 10pt is gone.
  • Hit target meets the minimum. Swift renders a 40pt visual inside a ≥44pt hit frame (Apple HIG), matching Compose’s 40dp default. The old 24×22pt frame is gone.
  • Accessible name is required. Swift’s label is now a required, non-empty argument that asserts in debug, matching Compose’s contentDescription / web’s label. No more unnamed default.

Remaining, minor differences:

  • Sizing knob: Compose/web expose an explicit size (and Compose iconSize); Swift’s visual is fixed at 40pt (the shared control height). Disabled state is honored via .disabled(_:) on Swift rather than an enabled parameter.