fix(button): remove redundant data-state enum (book §1 purity)

Audit 2026-05-27 caught 11 `data-state="idle"` violations across 3
disclosure trigger components (dropdown-menu, table.row-detail-trigger,
tree-grid.expand-trigger). Root cause was composition: each used
`<Button>` as its chrome via `{...props}` spread, and Button's morfo
declared a redundant `data-state ∈ [idle, loading]` enum that the
runtime emitted unconditionally — clobbering the outer trigger's
`data-state="open|closed"` via mergeProps order.

Doctrinal fix: REMOVE `data-state` from Button's morfo entirely.
Button's loading state is already fully expressed by the
`data-loading` presence flag (declared further down in the same morfo
and consumed by eidos as `[data-loading]`, not
`[data-state='loading']`). The 'idle' value was a redundant synonym
for "not loading" — and the book canon favours "presence = active
state, absence = rest" for binary states (cf. `data-disabled`,
`data-loading`).

**Book-pure rationale**:
- Disclosure (family `emerge`): `data-state ∈ [open, closed]` —
  dialog, drawer, popover, accordion, dropdown-menu, etc.
- Lifecycle (family `sustain`): `data-state ∈ [idle, loading, ...]` —
  image, avatar, button (until now), card, field.
- Mixing the two vocabularies in a single attr is the incoherence
  this fix removes.
- Button is interactive surface, NOT lifecycle: its "rest" is
  natural absence of `data-loading`, not a distinct `idle` state.

**Result**:
- `npm run morfo:check`: 16 issues / 6 components → **6 issues / 3
  components**. Removed all 11 `'idle'` violations.
- Composition pattern restored: `<Button>` no longer clobbers
  `data-state` on the host trigger.
- Eidos CSS unchanged (already uses `[data-loading]`).
- Tests: 803/801 unchanged (2 remaining failures are unrelated, in
  the user's words editor WIP).

**Not fixed in this commit** (3 remaining `morfo:check` failures):
- `avatar.provider`: missing `data-color` emission (4×)
- `color-picker.provider`: missing `data-state` emission
- `date-picker.provider`: `data-kind="date"` not in declared
  `[day, month, year]` enum.
Each requires a separate domain decision.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
dev 5 months ago
parent 681e056102
commit 3542f3a730

@ -81,13 +81,23 @@ export const buttonMorfo = {
kind: 'public',
defaultElement: 'button',
optional: false,
// Button's loading state is fully expressed by the `data-loading`
// presence flag (declared below) — eidos CSS already uses
// `[data-loading]`, NOT `[data-state='loading']`. The morfo
// previously declared a redundant `data-state ∈ [idle, loading]`
// enum that:
// (a) duplicated `data-loading`
// (b) emitted `data-state="idle"` on every non-loading button,
// which CLOBBERED the host disclosure's `data-state="open|closed"`
// whenever Button was used as the chrome for a
// RowDetailTrigger / ExpandTrigger / DropdownMenu Trigger
// (audit 2026-05-27 caught this).
// `data-state` removed entirely. The book doctrine favours
// "presence = active state, absence = rest" for binary states
// (data-disabled, data-loading). 'idle' is not a value — it's
// the natural rest where no flag is emitted.
states: ['idle', 'loading'],
data: [
{
attr: 'data-state',
values: ['idle', 'loading'],
value: v.stateRef('loading')
},
{
/**
* Resolved visual color. Per doctrine, when intent is

Loading…
Cancel
Save

Powered by TurnKey Linux.