yk-button: the per-context contract moves onto the YkButton class doc
commit 7d6001756d2def6cd41f4f8ebff38c96d9e34190 · parent bdc1230fa7
· browse this tree
Ximianth, 2026-10-24 10:54:20 UTC
yk-button was converted before the rule the docs round settled on (a contract lives on the most specific exported declaration; the header keeps intent), so its header still held the dual-context contract, the claim list, the activation-is-click story, and the activation feedback, and its class doc pointed readers at the header -- which the .d.ts doesn't carry. All of it now lives on YkButton; the header keeps the purpose and the norm pointer. Wording is unchanged apart from backticks on attribute names and keys, and two file names becoming links: the claim idiom to MegacolumnKeyDetail, press-flash.ts to flashPress.
diff --git a/packages/yukey/src/yk-button.ts b/packages/yukey/src/yk-button.ts index b46f6e7..ea4dd0c 100644 --- a/packages/yukey/src/yk-button.ts +++ b/packages/yukey/src/yk-button.ts @@ -1,51 +1,7 @@ // yk-button: the button that makes the guidelines' keyboard norms structural. // Use it and the norms happen; there is nothing to remember per screen. // (The norms: docs/precepts-we-love-the-keyboard.md, "buttons always show their hotkey" and the megacolumn sections. -// Each property's doc names the norm it carries.) -// -// Dual context, detected at connect time: -// -// - Inside a megacolumn row (a [data-mc-row] ancestor in the same shadow tree): -// renders non-focusable markup -- the container must stay the column's only tab stop, -// and non-focusable content also gives clicks their native behavior of focusing the container. -// Activation arrives as megacolumn-key events, -// heard on the row element so the button answers row-scoped keys even when the forward targeted the row's other control. -// The button claims keys: it preventDefaults the forwarded `megacolumn-key` event, -// so later listeners and the controller treat the key as handled -// (the claim idiom is megacolumn-controller.ts's; it is defined there). -// First claim wins, and a disabled button claims but declines to act. What it claims: -// - Enter/Space at this button's own position: activate. -// - Its hotkey, only when hotkeyDirect: activate -// (via the column-scoped marking contract: data-mc-hotkey plus data-mc-hotkey-direct on the host). -// Without hotkeyDirect the button declines its own hotkey: -// that decline lets the controller resolve the marking as a focus move to this row, -// and Enter/Space then activate (see-before-commit). -// - x/Delete from any position, aux buttons only: activate (the removal norm). -// - i at this button's own position: yk-info. -// - Standalone (no [data-mc-container] ancestor): a real native button; -// Tab reaches it, Enter/Space activate natively, the focus ring is the tokens'. -// Its hotkey and i answer only while it holds focus -// (a broader standalone hotkey scope is an app-level concern, not this component's). -// - Inside a container but outside any row: fail closed (throws at connect). -// A native tab stop loose in a container would quietly fracture the single-tab-stop model, -// and the scan cannot see into this shadow root. -// -// Activation is a `click` event on this element in every context and by every trigger (mouse, Enter/Space, hotkey): -// authors write one @click handler. -// In row mode every host click is also claimed (preventDefault) as it passes: -// the megacolumn controller synthesizes an Enter-equivalent megacolumn-key from unclaimed clicks on row controls -// (its pointer-activation norm for plain rows), -// and the claim tells it this widget already owns click-activation -- -// without it, a mouse click would activate twice, once natively and once synthesized. -// Standalone clicks stay unclaimed: no controller listens, -// and a native button's click default is not this component's to consume. -// -// Activation feedback: every successful activation flashes the pressed look for --yk-press-ms (press-flash.ts), -// so keyboard triggers are as visible as a mouse press; -// a pointer held down shows the same look for as long as it stays down. -// Disabled buttons show neither: nothing happened, nothing must claim otherwise. -// The yk-pressed/yk-held classes live on the host element, outside classMap's managed set, -// so renders never clobber a flash in flight. +// Each property's doc names the norm it carries; the YkButton class doc has the per-context contract.) import { css, html, LitElement, type TemplateResult } from "lit"; import { customElement, property } from "lit/decorators.js"; @@ -61,8 +17,49 @@ import { dispatchYkInfo } from "./yk-info.js"; * and one `click` event for every trigger. * Hit targets from `--yk-hit-min-h`/`--yk-hit-min-w`. * - * Standalone it is a native button; - * inside a megacolumn row it is a non-focusable row control (the file header lists which keys it claims in that mode). + * Dual context, detected at connect time: + * + * - Inside a megacolumn row (a `[data-mc-row]` ancestor in the same shadow tree): + * renders non-focusable markup -- the container must stay the column's only tab stop, + * and non-focusable content also gives clicks their native behavior of focusing the container. + * Activation arrives as megacolumn-key events, + * heard on the row element so the button answers row-scoped keys even when the forward targeted the row's other control. + * The button claims keys: it preventDefaults the forwarded `megacolumn-key` event, + * so later listeners and the controller treat the key as handled + * (the claim idiom is defined on {@link MegacolumnKeyDetail}). + * First claim wins, and a disabled button claims but declines to act. What it claims: + * - Enter/Space at this button's own position: activate. + * - Its hotkey, only when {@link hotkeyDirect}: activate + * (via the column-scoped marking contract: `data-mc-hotkey` plus `data-mc-hotkey-direct` on the host). + * Without hotkeyDirect the button declines its own hotkey: + * that decline lets the controller resolve the marking as a focus move to this row, + * and Enter/Space then activate (see-before-commit). + * - x/Delete from any position, aux buttons only: activate (the removal norm). + * - `i` at this button's own position: yk-info. + * - Standalone (no `[data-mc-container]` ancestor): a real native button; + * Tab reaches it, Enter/Space activate natively, the focus ring is the tokens'. + * Its hotkey and `i` answer only while it holds focus + * (a broader standalone hotkey scope is an app-level concern, not this component's). + * - Inside a container but outside any row: fail closed (throws at connect). + * A native tab stop loose in a container would quietly fracture the single-tab-stop model, + * and the scan cannot see into this shadow root. + * + * Activation is a `click` event on this element in every context and by every trigger (mouse, Enter/Space, hotkey): + * authors write one @click handler. + * In row mode every host click is also claimed (preventDefault) as it passes: + * the megacolumn controller synthesizes an Enter-equivalent megacolumn-key from unclaimed clicks on row controls + * (its pointer-activation norm for plain rows), + * and the claim tells it this widget already owns click-activation -- + * without it, a mouse click would activate twice, once natively and once synthesized. + * Standalone clicks stay unclaimed: no controller listens, + * and a native button's click default is not this component's to consume. + * + * Activation feedback: every successful activation flashes the pressed look for `--yk-press-ms` (see {@link flashPress}), + * so keyboard triggers are as visible as a mouse press; + * a pointer held down shows the same look for as long as it stays down. + * Disabled buttons show neither: nothing happened, nothing must claim otherwise. + * The `yk-pressed`/`yk-held` classes live on the host element, outside classMap's managed set, + * so renders never clobber a flash in flight. */ @customElement("yk-button") export class YkButton extends LitElement { @@ -136,7 +133,7 @@ export class YkButton extends LitElement { color: inherit; } /* activation feedback: the same look for held-under-a-pointer and for the - post-activation flash (see the file header) */ + post-activation flash (see the class doc) */ :host(.yk-held) .btn, :host(.yk-pressed) .btn { background: var(--yk-wash-pressed); @@ -304,7 +301,7 @@ export class YkButton extends LitElement { this.#releaseHold(); } - /** #onMegacolumnKey answers forwarded keys in row mode; the file header has the table of which keys it claims. */ + /** #onMegacolumnKey answers forwarded keys in row mode; the {@link YkButton} class doc has the list of which keys it claims. */ #onMegacolumnKey = (e: Event): void => { if (e.defaultPrevented) return; // someone earlier in the row claimed it const { key, position } = (e as CustomEvent<MegacolumnKeyDetail>).detail;