@rcarls/rc-button 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @rcarls/rc-button
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 30eb232: Add icon-only activation-area sizing and Material icon-button variants. Align menu
8
+ selection and submenu affordances, and keep anchored popups within the viewport
9
+ across native and fallback positioning.
10
+ - 53baa54: Support a direct native `<a href>` child, styled the same as a `<button>`
11
+ child (border, padding, background, `full-width`), for a real navigation
12
+ action styled as a button, without losing native link affordances (open in
13
+ new tab, copy link). `icon-only` sizing, the touch-target hit-slop, and the
14
+ `disabled`/`pending`/`progress` states stay button-only: those assume a real
15
+ `HTMLButtonElement`.
16
+
17
+ Treat that anchor as a valid direct child in development validation as well as
18
+ styling and documentation.
19
+
20
+ - 53baa54: Add `--rc-button-touch-target-overlap-inline-start` and
21
+ `--rc-button-touch-target-overlap-inline-end` for `icon-only` buttons, zero
22
+ by default. A theme or consumer sets one to let the accessible touch-target
23
+ inflation on that side overlap into whatever sits just outside the host,
24
+ such as a toolbar's own edge padding, instead of also reserving layout
25
+ space there: the visible icon shifts flush with that edge while the
26
+ invisible hit region keeps its full accessible size.
27
+
28
+ ### Patch Changes
29
+
30
+ - 7adbd76: Keep development diagnostics concise and prevent expected test fixtures from emitting noisy warnings.
31
+
3
32
  ## 0.5.0
4
33
 
5
34
  ## 0.4.2
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # @rcarls/rc-button
2
2
 
3
- Progressive-enhancement button wrapper with structural state affordances.
3
+ Progressive-enhancement action wrapper for a direct native `<button>` or
4
+ `<a href>` child, with structural state affordances for buttons.
4
5
 
5
6
  ```html
6
7
  <rc-button toggle default-selected>
@@ -46,7 +46,7 @@
46
46
  "declarations": [
47
47
  {
48
48
  "kind": "class",
49
- "description": "Structural button wrapper that preserves a direct native `<button>` child for\nprogressive enhancement, forms, labels, and keyboard behavior.",
49
+ "description": "Structural action wrapper that preserves a direct native `<button>` or\n`<a href>` child for progressive enhancement and native interaction.\n\nA direct native `<a href>` child gets the same visual/layout styling\n(border, padding, background, `full-width`) for a genuine navigation\naction styled as a button. `icon-only` sizing, the touch-target hit-slop,\nand the `disabled`/`pending`/`progress` states stay button-only — those\nassume a real `HTMLButtonElement` (a native `disabled` state, a progress\naffordance that takes over the button's content) an anchor doesn't have.",
50
50
  "name": "RCButton",
51
51
  "cssProperties": [
52
52
  {
@@ -105,6 +105,25 @@
105
105
  "description": "Button inline and min-inline size when `icon-only`. Falls back to `--rc-button-block-size`, then `--rc-control-block-size`, then `2.5rem`.",
106
106
  "name": "--rc-button-icon-size"
107
107
  },
108
+ {
109
+ "description": "Minimum accessible touch target block size for an `icon-only` button. A floor, not a fixed size: has no effect once `--rc-button-block-size` already meets or exceeds it. Grows the host (reserving layout space) and the light-DOM hit-slop (the actual larger clickable region) together; the visible child button itself stays at its own size, centered.",
110
+ "name": "--rc-button-touch-target-block-size",
111
+ "default": "3rem"
112
+ },
113
+ {
114
+ "description": "Minimum accessible touch target inline size for an `icon-only` button. Defers to `--rc-button-touch-target-block-size` when unset.",
115
+ "name": "--rc-button-touch-target-inline-size"
116
+ },
117
+ {
118
+ "description": "Zero by default. A theme or consumer sets this on an `icon-only` button that sits at a real leading edge (no neighbor on that side) to let its touch-target inflation overlap into whatever sits just outside the host, such as a container's own edge padding, instead of also reserving layout space there.",
119
+ "name": "--rc-button-touch-target-overlap-inline-start",
120
+ "default": "0px"
121
+ },
122
+ {
123
+ "description": "The trailing-edge counterpart to `--rc-button-touch-target-overlap-inline-start`.",
124
+ "name": "--rc-button-touch-target-overlap-inline-end",
125
+ "default": "0px"
126
+ },
108
127
  {
109
128
  "description": "Disabled button opacity (defers to native disabled styling when unset).",
110
129
  "name": "--rc-button-disabled-opacity"
@@ -187,7 +206,7 @@
187
206
  ],
188
207
  "slots": [
189
208
  {
190
- "description": "A direct native `<button>` child.",
209
+ "description": "A direct native `<button>` child, or a direct `<a href>` for a navigation action (visual/layout styling only — see above).",
191
210
  "name": ""
192
211
  }
193
212
  ],
@@ -198,7 +217,7 @@
198
217
  "privacy": "protected",
199
218
  "static": true,
200
219
  "readonly": true,
201
- "default": "new Set<Document | ShadowRoot>()"
220
+ "default": "new WeakSet<Document | ShadowRoot>()"
202
221
  },
203
222
  {
204
223
  "kind": "method",
@@ -463,7 +482,13 @@
463
482
  "type": {
464
483
  "text": "void"
465
484
  }
466
- }
485
+ },
486
+ "parameters": [
487
+ {
488
+ "name": "deferClassification",
489
+ "default": "false"
490
+ }
491
+ ]
467
492
  },
468
493
  {
469
494
  "kind": "method",
@@ -475,6 +500,24 @@
475
500
  }
476
501
  }
477
502
  },
503
+ {
504
+ "kind": "method",
505
+ "name": "_hasAnyElementChild",
506
+ "privacy": "protected",
507
+ "return": {
508
+ "type": {
509
+ "text": "boolean"
510
+ }
511
+ },
512
+ "parameters": [
513
+ {
514
+ "name": "$button",
515
+ "type": {
516
+ "text": "HTMLButtonElement | null"
517
+ }
518
+ }
519
+ ]
520
+ },
478
521
  {
479
522
  "kind": "method",
480
523
  "name": "_hasLabel",
@@ -486,7 +529,7 @@
486
529
  },
487
530
  "parameters": [
488
531
  {
489
- "name": "button",
532
+ "name": "$button",
490
533
  "type": {
491
534
  "text": "HTMLButtonElement | null"
492
535
  }
@@ -713,7 +756,7 @@
713
756
  "fieldName": "fullWidth"
714
757
  },
715
758
  {
716
- "description": "Present when the native button has a direct `[data-rc-button-icon]` child. Use with CSS selectors (e.g. `rc-button[has-icon]`).",
759
+ "description": "Present when the native button has a direct marked icon or an inferred icon element without visible label content. Use with CSS selectors (e.g. `rc-button[has-icon]`).",
717
760
  "name": "has-icon"
718
761
  },
719
762
  {
@@ -1,9 +1,10 @@
1
- import { css as p, LitElement as h, html as b } from "lit";
1
+ import { css as h, LitElement as p, html as b } from "lit";
2
2
  import { property as a, query as u } from "lit/decorators.js";
3
- const y = p`
3
+ const y = h`
4
4
  :host {
5
5
  position: relative;
6
6
  display: inline-grid;
7
+ place-items: center;
7
8
  vertical-align: middle;
8
9
  }
9
10
 
@@ -16,7 +17,55 @@ const y = p`
16
17
  inline-size: 100%;
17
18
  }
18
19
 
19
- ::slotted(button) {
20
+ /*
21
+ * Icon-only buttons can render visually smaller than the accessible
22
+ * minimum touch target (e.g. MD3's 40dp "small" icon button, or a 32dp
23
+ * expressive extra-small size). These are floors, not fixed sizes: a
24
+ * larger visual button (medium/large/extra-large sizes) already clears
25
+ * them and is unaffected. The host grows to reserve the space; the
26
+ * visible child button stays centered at its own size via place-items
27
+ * above, and the actual larger hit region comes from the light-DOM
28
+ * hit-slop pseudo-element in rc-button.ts. Mirrors rc-chip's
29
+ * touch-target-block-size host-affordance pattern.
30
+ */
31
+ :host([icon-only]) {
32
+ min-block-size: var(--rc-button-touch-target-block-size, 3rem);
33
+ min-inline-size: var(
34
+ --rc-button-touch-target-inline-size,
35
+ var(--rc-button-touch-target-block-size, 3rem)
36
+ );
37
+ /*
38
+ * Zero by default: an icon-only button's touch-target inflation reserves
39
+ * real layout space on both sides, correct when it has real neighbors.
40
+ * At an actual edge (e.g. a toolbar's trailing-most icon), a theme or
41
+ * consumer sets one or both of these to let that reserved space overlap
42
+ * into whatever sits just outside the host (typically a container's own
43
+ * edge padding) instead of adding to it — a plain token, not something
44
+ * this component decides on its own or exposes as a mode to switch. A
45
+ * negative margin on that side pulls the host's own footprint back by
46
+ * the given amount; the light-DOM hit-slop pseudo-element below is
47
+ * unaffected by host margin and still extends the full touch target, so
48
+ * the actual clickable region keeps its accessible size, just visually
49
+ * overlapping the reclaimed space rather than adding to it. Only
50
+ * sensible at an edge — setting this on a button with a real neighbor
51
+ * on that side overlaps its touch target too.
52
+ */
53
+ margin-inline-start: calc(-1 * var(--rc-button-touch-target-overlap-inline-start, 0px));
54
+ margin-inline-end: calc(-1 * var(--rc-button-touch-target-overlap-inline-end, 0px));
55
+ }
56
+
57
+ /*
58
+ * a[href] alongside button: a real navigation link is a legitimate direct
59
+ * child too (rc-menu-button and rc-adaptive-menu already treat "native
60
+ * button or link" as one contract; rc-button's own JSDoc undersold this
61
+ * as button-only). Scoped to this rule and the full-width one below,
62
+ * both pure visual/layout styling with no dependency on button-specific
63
+ * IDL state — icon-only sizing, the touch-target hit-slop pseudo, and
64
+ * the disabled/pending/progress treatments below stay button-only since
65
+ * those assume a real HTMLButtonElement (disabled, a live progress
66
+ * takeover) an anchor doesn't have.
67
+ */
68
+ ::slotted(:is(button, a[href])) {
20
69
  position: relative;
21
70
  display: inline-flex;
22
71
  align-items: center;
@@ -40,7 +89,7 @@ const y = p`
40
89
  transition: var(--rc-button-transition, revert);
41
90
  }
42
91
 
43
- :host([full-width]) ::slotted(button) {
92
+ :host([full-width]) ::slotted(:is(button, a[href])) {
44
93
  inline-size: 100%;
45
94
  }
46
95
 
@@ -54,6 +103,7 @@ const y = p`
54
103
  var(--rc-button-block-size, var(--rc-control-block-size, 2.5rem))
55
104
  );
56
105
  padding-inline: 0;
106
+ overflow: visible;
57
107
  }
58
108
 
59
109
  :host([disabled]) ::slotted(button),
@@ -189,7 +239,7 @@ const y = p`
189
239
  }
190
240
 
191
241
  @media (forced-colors: active) {
192
- ::slotted(button) {
242
+ ::slotted(:is(button, a[href])) {
193
243
  border-color: ButtonBorder;
194
244
  background: ButtonFace;
195
245
  color: ButtonText;
@@ -200,12 +250,12 @@ const y = p`
200
250
  }
201
251
  }
202
252
  `;
203
- var f = Object.defineProperty, _ = Object.getOwnPropertyDescriptor, o = (c, t, e, s) => {
204
- for (var r = s > 1 ? void 0 : s ? _(t, e) : t, l = c.length - 1, d; l >= 0; l--)
205
- (d = c[l]) && (r = (s ? d(t, e, r) : d(r)) || r);
206
- return s && r && f(t, e, r), r;
253
+ var g = Object.defineProperty, f = Object.getOwnPropertyDescriptor, o = (d, t, e, s) => {
254
+ for (var r = s > 1 ? void 0 : s ? f(t, e) : t, l = d.length - 1, c; l >= 0; l--)
255
+ (c = d[l]) && (r = (s ? c(t, e, r) : c(r)) || r);
256
+ return s && r && g(t, e, r), r;
207
257
  };
208
- const g = `
258
+ const v = `
209
259
  @layer rc-base {
210
260
  /*
211
261
  * Icon-font utility classes commonly set display outside cascade layers.
@@ -218,15 +268,42 @@ const g = `
218
268
  rc-button:not([selected]) > button > [data-rc-button-selected-icon] {
219
269
  display: none !important;
220
270
  }
271
+
272
+ /*
273
+ * Expands an icon-only button's clickable region beyond its visible box
274
+ * up to the accessible touch-target minimum, the same way rc-chip does:
275
+ * an absolutely-positioned, invisible pseudo-element on the actual
276
+ * interactive element (not the host) extends the hit area symmetrically.
277
+ * The button already has position: relative from the shadow-scoped
278
+ * ::slotted(button) rule. max(100%, size) keeps this a no-op once the
279
+ * visible button already meets or exceeds the touch target (medium,
280
+ * large, and extra-large icon-button sizes), rather than shrinking it.
281
+ */
282
+ rc-button[icon-only] > button::before {
283
+ content: '';
284
+ position: absolute;
285
+ inset-block: calc(
286
+ (
287
+ var(--rc-button-touch-target-block-size, 3rem) -
288
+ max(100%, var(--rc-button-block-size, 0px))
289
+ ) / -2
290
+ );
291
+ inset-inline: calc(
292
+ (
293
+ var(--rc-button-touch-target-inline-size, var(--rc-button-touch-target-block-size, 3rem)) -
294
+ max(100%, var(--rc-button-icon-size, var(--rc-button-block-size, 0px)))
295
+ ) / -2
296
+ );
297
+ }
221
298
  }
222
- `, v = /* @__PURE__ */ new Set(["Enter", " "]);
299
+ `, _ = /* @__PURE__ */ new Set(["Enter", " "]);
223
300
  var n;
224
- const i = (n = class extends h {
301
+ const i = (n = class extends p {
225
302
  constructor() {
226
303
  super(), this.disabled = !1, this.pending = !1, this.progress = !1, this.toggle = !1, this.iconOnly = !1, this.fullWidth = !1, this._defaultSelected = !1, this._selectedInitialized = !1, this._$button = null, this._buttonObserver = null, this._disabledOwned = !1, this._ariaBusyOwned = !1, this._pressedOwned = !1, this._authorPressed = null, this._iconOnlyOwned = !1, this._slotMicrotaskQueued = !1, this.addEventListener("pointerdown", this._startRipple, { capture: !0 }), this.addEventListener("click", this._blockActivation, { capture: !0 }), this.addEventListener("click", this._handleToggleActivation), this.addEventListener(
227
304
  "keydown",
228
305
  (t) => {
229
- v.has(t.key) && this._blockActivation(t);
306
+ _.has(t.key) && this._blockActivation(t);
230
307
  },
231
308
  { capture: !0 }
232
309
  );
@@ -235,8 +312,8 @@ const i = (n = class extends h {
235
312
  if (n._styledRoots.has(t))
236
313
  return;
237
314
  n._styledRoots.add(t);
238
- const e = document.createElement("style");
239
- e.setAttribute("data-rc-light-dom-base", "rc-button"), e.textContent = g, t instanceof Document ? t.head.append(e) : t.append(e);
315
+ const e = (t instanceof Document ? t : t.ownerDocument).createElement("style");
316
+ e.setAttribute("data-rc-light-dom-base", "rc-button"), e.textContent = v, t instanceof Document ? t.head.append(e) : t.append(e);
240
317
  }
241
318
  get selected() {
242
319
  return this._selected ?? this._uncontrolledSelected ?? this._defaultSelected;
@@ -259,7 +336,7 @@ const i = (n = class extends h {
259
336
  this._buttonObserver?.disconnect(), this._buttonObserver = null, super.disconnectedCallback();
260
337
  }
261
338
  firstUpdated() {
262
- this._syncSlottedButton();
339
+ this._syncSlottedButton(!0);
263
340
  }
264
341
  updated(t) {
265
342
  (t.has("disabled") || t.has("pending") || t.has("progress")) && this._syncNativeState(), (t.has("toggle") || t.has("selected")) && this._syncPressedState();
@@ -287,26 +364,38 @@ const i = (n = class extends h {
287
364
  this._slotMicrotaskQueued = !1, this.isConnected && this._syncSlottedButton();
288
365
  }));
289
366
  }
290
- _syncSlottedButton() {
291
- const t = this._$slot?.assignedElements({ flatten: !0 }).find(
292
- (e) => e instanceof HTMLButtonElement && e.parentElement === this
367
+ _syncSlottedButton(t = !1) {
368
+ const e = this._$slot?.assignedElements({ flatten: !0 }).find(
369
+ (s) => s instanceof HTMLButtonElement && s.parentElement === this
293
370
  ) ?? null;
294
- if (t === this._$button) {
371
+ if (this._$slot?.assignedElements({ flatten: !0 }).find(
372
+ (s) => s instanceof HTMLAnchorElement && s.hasAttribute("href") && s.parentElement === this
373
+ ), e === this._$button) {
295
374
  this._classifyButton(), this._syncNativeState(), this._syncPressedState();
296
375
  return;
297
376
  }
298
- this._restorePressedState(), this._buttonObserver?.disconnect(), this._buttonObserver = null, this._$button = t, this._disabledOwned = !1, this._ariaBusyOwned = !1, this._pressedOwned = !1, this._authorPressed = t?.getAttribute("aria-pressed") ?? null, t && (this._buttonObserver = new MutationObserver(() => {
377
+ this._restorePressedState(), this._buttonObserver?.disconnect(), this._buttonObserver = null, this._$button = e, this._disabledOwned = !1, this._ariaBusyOwned = !1, this._pressedOwned = !1, this._authorPressed = e?.getAttribute("aria-pressed") ?? null, e && (this._buttonObserver = new MutationObserver(() => {
299
378
  this._classifyButton(), this._syncNativeState(), this._syncPressedState();
300
- }), this._buttonObserver.observe(t, {
379
+ }), this._buttonObserver.observe(e, {
301
380
  attributes: !0,
302
381
  childList: !0,
303
382
  subtree: !0,
304
383
  characterData: !0
305
- })), this._classifyButton(), this._syncNativeState(), this._syncPressedState();
384
+ })), t ? queueMicrotask(() => {
385
+ this.isConnected && this._$button === e && this._classifyButton();
386
+ }) : this._classifyButton(), this._syncNativeState(), this._syncPressedState();
306
387
  }
307
388
  _classifyButton() {
308
- const t = this._$button, e = !!t?.querySelector(":scope > [data-rc-button-icon]"), s = !!t?.querySelector(":scope > [data-rc-button-selected-icon]"), r = this._hasLabel(t);
309
- this.toggleAttribute("has-icon", e), this.toggleAttribute("has-selected-icon", s), this.toggleAttribute("has-label", r), t && e && !r ? this.iconOnly || (this._iconOnlyOwned = !0, this.iconOnly = !0) : this._iconOnlyOwned && (this._iconOnlyOwned = !1, this.iconOnly = !1);
389
+ const t = this._$button, e = !!t?.querySelector(":scope > [data-rc-button-icon]"), s = !!t?.querySelector(":scope > [data-rc-button-selected-icon]"), r = this._hasLabel(t), l = !r && this._hasAnyElementChild(t), c = e || l;
390
+ this.toggleAttribute("has-icon", c), this.toggleAttribute("has-selected-icon", s), this.toggleAttribute("has-label", r), t && c && !r ? this.iconOnly || (this._iconOnlyOwned = !0, this.iconOnly = !0) : this._iconOnlyOwned && (this._iconOnlyOwned = !1, this.iconOnly = !1);
391
+ }
392
+ _hasAnyElementChild(t) {
393
+ if (!t)
394
+ return !1;
395
+ for (const e of t.childNodes)
396
+ if (e instanceof Element)
397
+ return !0;
398
+ return !1;
310
399
  }
311
400
  _hasLabel(t) {
312
401
  if (!t)
@@ -316,7 +405,7 @@ const i = (n = class extends h {
316
405
  for (const e of t.childNodes) {
317
406
  if (e.nodeType === Node.TEXT_NODE && e.textContent?.trim())
318
407
  return !0;
319
- if (e instanceof HTMLElement && !e.matches(
408
+ if (e instanceof Element && !e.matches(
320
409
  '[data-rc-button-icon], [data-rc-button-selected-icon], [data-rc-button-progress], [aria-hidden="true"]'
321
410
  ) && e.textContent?.trim())
322
411
  return !0;
@@ -372,7 +461,7 @@ const i = (n = class extends h {
372
461
  _handleRippleAnimationEnd(t) {
373
462
  t.animationName === "rc-button-ripple" && this._$stateLayer.removeAttribute("data-rippling");
374
463
  }
375
- }, n.styles = y, n._styledRoots = /* @__PURE__ */ new Set(), n);
464
+ }, n.styles = y, n._styledRoots = /* @__PURE__ */ new WeakSet(), n);
376
465
  o([
377
466
  a({ type: Boolean, reflect: !0 })
378
467
  ], i.prototype, "disabled", 2);
@@ -406,8 +495,8 @@ o([
406
495
  o([
407
496
  u('[part="state-layer"]')
408
497
  ], i.prototype, "_$stateLayer", 2);
409
- let O = i;
498
+ let k = i;
410
499
  export {
411
- O as R
500
+ k as R
412
501
  };
413
- //# sourceMappingURL=rc-button-Ct5C14lN.js.map
502
+ //# sourceMappingURL=rc-button-C3zFF_QR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-button-C3zFF_QR.js","sources":["../src/rc-button.styles.ts","../src/rc-button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const buttonStyles = css`\n :host {\n position: relative;\n display: inline-grid;\n place-items: center;\n vertical-align: middle;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host([full-width]) {\n display: grid;\n inline-size: 100%;\n }\n\n /*\n * Icon-only buttons can render visually smaller than the accessible\n * minimum touch target (e.g. MD3's 40dp \"small\" icon button, or a 32dp\n * expressive extra-small size). These are floors, not fixed sizes: a\n * larger visual button (medium/large/extra-large sizes) already clears\n * them and is unaffected. The host grows to reserve the space; the\n * visible child button stays centered at its own size via place-items\n * above, and the actual larger hit region comes from the light-DOM\n * hit-slop pseudo-element in rc-button.ts. Mirrors rc-chip's\n * touch-target-block-size host-affordance pattern.\n */\n :host([icon-only]) {\n min-block-size: var(--rc-button-touch-target-block-size, 3rem);\n min-inline-size: var(\n --rc-button-touch-target-inline-size,\n var(--rc-button-touch-target-block-size, 3rem)\n );\n /*\n * Zero by default: an icon-only button's touch-target inflation reserves\n * real layout space on both sides, correct when it has real neighbors.\n * At an actual edge (e.g. a toolbar's trailing-most icon), a theme or\n * consumer sets one or both of these to let that reserved space overlap\n * into whatever sits just outside the host (typically a container's own\n * edge padding) instead of adding to it — a plain token, not something\n * this component decides on its own or exposes as a mode to switch. A\n * negative margin on that side pulls the host's own footprint back by\n * the given amount; the light-DOM hit-slop pseudo-element below is\n * unaffected by host margin and still extends the full touch target, so\n * the actual clickable region keeps its accessible size, just visually\n * overlapping the reclaimed space rather than adding to it. Only\n * sensible at an edge — setting this on a button with a real neighbor\n * on that side overlaps its touch target too.\n */\n margin-inline-start: calc(-1 * var(--rc-button-touch-target-overlap-inline-start, 0px));\n margin-inline-end: calc(-1 * var(--rc-button-touch-target-overlap-inline-end, 0px));\n }\n\n /*\n * a[href] alongside button: a real navigation link is a legitimate direct\n * child too (rc-menu-button and rc-adaptive-menu already treat \"native\n * button or link\" as one contract; rc-button's own JSDoc undersold this\n * as button-only). Scoped to this rule and the full-width one below,\n * both pure visual/layout styling with no dependency on button-specific\n * IDL state — icon-only sizing, the touch-target hit-slop pseudo, and\n * the disabled/pending/progress treatments below stay button-only since\n * those assume a real HTMLButtonElement (disabled, a live progress\n * takeover) an anchor doesn't have.\n */\n ::slotted(:is(button, a[href])) {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--rc-button-gap);\n min-block-size: var(--rc-button-block-size);\n min-inline-size: var(--rc-button-min-inline-size);\n inline-size: var(--rc-button-inline-size);\n padding-block: var(--rc-button-padding-block, revert);\n padding-inline: var(--rc-button-padding-inline, revert);\n border: var(--rc-button-border, revert);\n border-radius: var(--rc-button-radius, revert);\n background: var(--rc-button-bg, revert);\n color: var(--rc-button-color, revert);\n box-shadow: var(--rc-button-shadow, revert);\n font: var(--rc-button-font, revert);\n white-space: nowrap;\n overflow: hidden;\n box-sizing: border-box;\n z-index: 0;\n transition: var(--rc-button-transition, revert);\n }\n\n :host([full-width]) ::slotted(:is(button, a[href])) {\n inline-size: 100%;\n }\n\n :host([icon-only]) ::slotted(button) {\n inline-size: var(\n --rc-button-icon-size,\n var(--rc-button-block-size, var(--rc-control-block-size, 2.5rem))\n );\n min-inline-size: var(\n --rc-button-icon-size,\n var(--rc-button-block-size, var(--rc-control-block-size, 2.5rem))\n );\n padding-inline: 0;\n overflow: visible;\n }\n\n :host([disabled]) ::slotted(button),\n :host([pending]) ::slotted(button),\n :host([progress]) ::slotted(button) {\n cursor: default;\n }\n\n :host([pending]) ::slotted(button),\n :host([progress]) ::slotted(button) {\n color: var(--rc-button-busy-content-color, transparent);\n }\n\n :host([disabled]) ::slotted(button) {\n opacity: var(--rc-button-disabled-opacity, revert);\n }\n\n [part='state-layer'],\n [part='progress'] {\n position: absolute;\n inset: 0;\n border-radius: var(--rc-button-radius, 0);\n pointer-events: none;\n }\n\n [part='state-layer'] {\n overflow: hidden;\n z-index: 1;\n }\n\n [part='state-layer']::before {\n content: '';\n position: absolute;\n inset: 0;\n background: var(--rc-button-state-layer-bg, currentColor);\n opacity: 0;\n transition: opacity var(--rc-button-state-layer-duration, 150ms)\n var(--rc-button-state-layer-easing, ease);\n }\n\n [part='state-layer']::after {\n content: '';\n position: absolute;\n inset-inline-start: var(--_rc-button-ripple-x, 50%);\n inset-block-start: var(--_rc-button-ripple-y, 50%);\n inline-size: var(--_rc-button-ripple-size, 0);\n block-size: var(--_rc-button-ripple-size, 0);\n border-radius: 50%;\n background: var(--_rc-button-ripple-color, currentColor);\n opacity: 0;\n pointer-events: none;\n translate: -50% -50%;\n scale: 0;\n }\n\n [part='state-layer'][data-rippling]::after {\n animation: rc-button-ripple var(--_rc-button-ripple-duration, 0ms)\n var(--_rc-button-ripple-easing, ease-out);\n }\n\n :host(:hover:not([disabled]):not([pending]):not([progress])) [part='state-layer']::before {\n opacity: var(--rc-button-hover-state-layer-opacity, 0);\n }\n\n :host(:focus-within:not([disabled])) [part='state-layer']::before {\n opacity: var(--rc-button-focus-state-layer-opacity, 0);\n }\n\n :host(:active:not([disabled]):not([pending]):not([progress])) [part='state-layer']::before {\n opacity: var(--rc-button-pressed-state-layer-opacity, 0);\n }\n\n [part='progress'] {\n display: none;\n place-items: center;\n color: var(--rc-button-progress-color, currentColor);\n font: var(--rc-button-progress-font, 600 0.75rem / 1 sans-serif);\n font-variant-numeric: tabular-nums;\n z-index: 2;\n }\n\n :host([pending]) [part='progress'],\n :host([progress]) [part='progress'] {\n display: grid;\n }\n\n [part='progress']::before {\n content: '';\n box-sizing: border-box;\n inline-size: var(--rc-button-progress-size, 1.25rem);\n block-size: var(--rc-button-progress-size, 1.25rem);\n border: var(--rc-button-progress-track-width, 2px) solid\n var(--rc-button-progress-track-color, color-mix(in srgb, currentColor 24%, transparent));\n border-block-start-color: var(--rc-button-progress-active-color, currentColor);\n border-radius: 9999px;\n animation: rc-button-progress-spin 900ms linear infinite;\n }\n\n [part='progress'][data-determinate]::before {\n display: none;\n }\n\n @keyframes rc-button-progress-spin {\n to {\n rotate: 1turn;\n }\n }\n\n @keyframes rc-button-ripple {\n from {\n opacity: var(--_rc-button-ripple-opacity, 0);\n scale: 0;\n }\n\n 70% {\n opacity: var(--_rc-button-ripple-opacity, 0);\n }\n\n to {\n opacity: 0;\n scale: 1;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n [part='state-layer'][data-rippling]::after {\n animation-duration: 0ms;\n }\n\n [part='progress']::before {\n animation-duration: 2s;\n }\n }\n\n @media (forced-colors: active) {\n ::slotted(:is(button, a[href])) {\n border-color: ButtonBorder;\n background: ButtonFace;\n color: ButtonText;\n }\n\n [part='state-layer']::before {\n background: Highlight;\n }\n }\n`;\n\nexport default buttonStyles;\n","import { LitElement, html } from 'lit';\nimport type { PropertyValues } from 'lit';\nimport { property, query } from 'lit/decorators.js';\n\nimport buttonStyles from './rc-button.styles.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-button': RCButton;\n }\n\n interface HTMLElementEventMap {\n 'rc-button-toggle': CustomEvent<RCButtonToggleDetail>;\n }\n}\n\nconst LIGHT_DOM_CSS = `\n@layer rc-base {\n /*\n * Icon-font utility classes commonly set display outside cascade layers.\n * Hiding the inactive icon is structural, so it must win that declaration.\n */\n rc-button[selected] > button > [data-rc-button-icon] {\n display: none !important;\n }\n\n rc-button:not([selected]) > button > [data-rc-button-selected-icon] {\n display: none !important;\n }\n\n /*\n * Expands an icon-only button's clickable region beyond its visible box\n * up to the accessible touch-target minimum, the same way rc-chip does:\n * an absolutely-positioned, invisible pseudo-element on the actual\n * interactive element (not the host) extends the hit area symmetrically.\n * The button already has position: relative from the shadow-scoped\n * ::slotted(button) rule. max(100%, size) keeps this a no-op once the\n * visible button already meets or exceeds the touch target (medium,\n * large, and extra-large icon-button sizes), rather than shrinking it.\n */\n rc-button[icon-only] > button::before {\n content: '';\n position: absolute;\n inset-block: calc(\n (\n var(--rc-button-touch-target-block-size, 3rem) -\n max(100%, var(--rc-button-block-size, 0px))\n ) / -2\n );\n inset-inline: calc(\n (\n var(--rc-button-touch-target-inline-size, var(--rc-button-touch-target-block-size, 3rem)) -\n max(100%, var(--rc-button-icon-size, var(--rc-button-block-size, 0px)))\n ) / -2\n );\n }\n}\n`;\n\nconst ACTIVATION_KEYS = new Set(['Enter', ' ']);\n\n/** Detail payload for `rc-button-toggle`. */\nexport interface RCButtonToggleDetail {\n /** Requested selected state after user activation. */\n selected: boolean;\n}\n\n/**\n * Structural action wrapper that preserves a direct native `<button>` or\n * `<a href>` child for progressive enhancement and native interaction.\n *\n * A direct native `<a href>` child gets the same visual/layout styling\n * (border, padding, background, `full-width`) for a genuine navigation\n * action styled as a button. `icon-only` sizing, the touch-target hit-slop,\n * and the `disabled`/`pending`/`progress` states stay button-only — those\n * assume a real `HTMLButtonElement` (a native `disabled` state, a progress\n * affordance that takes over the button's content) an anchor doesn't have.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-button rc-button docs}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/button/ WAI-ARIA button pattern}\n *\n * @slot - A direct native `<button>` child, or a direct `<a href>` for a\n * navigation action (visual/layout styling only — see above).\n *\n * @fires rc-button-toggle - Fired when a user activates a button with `toggle`.\n *\n * @csspart state-layer - Overlay layer for hover, focus, pressed, ripple, or design-system effects.\n * @csspart progress - Non-interactive progress affordance overlay shown for `pending` or `progress`.\n *\n * @attr disabled - Mirrors disabled state to the native child button.\n * @attr pending - Disables the native button and exposes an indeterminate progress affordance.\n * @attr progress - Disables the native button and exposes a progress affordance.\n * @attr progress-value - Optional determinate progress percentage, clamped from 0 through 100.\n * @attr toggle - Opts the native child into APG toggle-button behavior.\n * @attr selected - Controlled selected state for toggle semantics, styling, and icon switching.\n * @attr default-selected - Initial selected state for uncontrolled toggle usage.\n * @attr icon-only - Removes label-oriented inline padding in supporting themes.\n * @attr full-width - Stretches the native child button to the host inline size.\n * @attr [has-icon] - Present when the native button has a direct marked icon or an inferred\n * icon element without visible label content. Use with CSS selectors (e.g.\n * `rc-button[has-icon]`).\n * @attr [has-selected-icon] - Present when the native button has a direct\n * `[data-rc-button-selected-icon]` child.\n * @attr [has-label] - Present when the native button has visible label content, from either a\n * `[data-rc-button-label]` child or bare text.\n *\n * @cssprop [--rc-button-gap] - Gap between icon and label content.\n * @cssprop [--rc-button-block-size] - Minimum button block size.\n * @cssprop [--rc-button-min-inline-size] - Minimum button inline size.\n * @cssprop [--rc-button-inline-size] - Button inline size.\n * @cssprop [--rc-button-padding-block] - Button block-axis padding (defers to native button\n * padding when unset).\n * @cssprop [--rc-button-padding-inline] - Button inline-axis padding (defers to native button\n * padding when unset).\n * @cssprop [--rc-button-border] - Button border (defers to native button border when unset).\n * @cssprop [--rc-button-radius] - Button and overlay border radius (defers to native button\n * radius when unset).\n * @cssprop [--rc-button-bg] - Button background (defers to native button background when unset).\n * @cssprop [--rc-button-color] - Button text color (defers to native button color when unset).\n * @cssprop [--rc-button-shadow] - Button box shadow (defers to native button shadow when unset).\n * @cssprop [--rc-button-font] - Button font shorthand (defers to native button font when unset).\n * @cssprop [--rc-button-transition] - Button transition shorthand (defers to native button\n * transition when unset).\n * @cssprop [--rc-button-icon-size] - Button inline and min-inline size when `icon-only`. Falls\n * back to `--rc-button-block-size`, then `--rc-control-block-size`, then `2.5rem`.\n * @cssprop [--rc-button-touch-target-block-size=3rem] - Minimum accessible touch target block\n * size for an `icon-only` button. A floor, not a fixed size: has no effect once\n * `--rc-button-block-size` already meets or exceeds it. Grows the host (reserving layout\n * space) and the light-DOM hit-slop (the actual larger clickable region) together; the\n * visible child button itself stays at its own size, centered.\n * @cssprop [--rc-button-touch-target-inline-size] - Minimum accessible touch target inline size\n * for an `icon-only` button. Defers to `--rc-button-touch-target-block-size` when unset.\n * @cssprop [--rc-button-touch-target-overlap-inline-start=0px] - Zero by default. A theme or\n * consumer sets this on an `icon-only` button that sits at a real leading edge (no neighbor on\n * that side) to let its touch-target inflation overlap into whatever sits just outside the\n * host, such as a container's own edge padding, instead of also reserving layout space there.\n * @cssprop [--rc-button-touch-target-overlap-inline-end=0px] - The trailing-edge counterpart to\n * `--rc-button-touch-target-overlap-inline-start`.\n * @cssprop [--rc-button-disabled-opacity] - Disabled button opacity (defers to native disabled\n * styling when unset).\n * @cssprop [--rc-button-busy-content-color=transparent] - Button text color while `pending` or\n * `progress`.\n * @cssprop [--rc-button-state-layer-bg=currentColor] - State-layer overlay color.\n * @cssprop [--rc-button-state-layer-duration=150ms] - State-layer opacity transition duration.\n * @cssprop [--rc-button-state-layer-easing=ease] - State-layer opacity transition easing.\n * @cssprop [--rc-button-hover-state-layer-opacity=0] - State-layer opacity on hover.\n * @cssprop [--rc-button-focus-state-layer-opacity=0] - State-layer opacity on focus-within.\n * @cssprop [--rc-button-pressed-state-layer-opacity=0] - State-layer opacity on active press.\n * @cssprop [--rc-button-progress-color=currentColor] - Progress affordance color.\n * @cssprop [--rc-button-progress-font=600 0.75rem / 1 sans-serif] - Determinate progress\n * percentage font.\n * @cssprop [--rc-button-progress-size=1.25rem] - Indeterminate progress spinner diameter.\n * @cssprop [--rc-button-progress-track-width=2px] - Indeterminate progress spinner stroke width.\n * @cssprop [--rc-button-progress-track-color=color-mix(in srgb, currentColor 24%, transparent)] -\n * Indeterminate progress spinner track color.\n * @cssprop [--rc-button-progress-active-color=currentColor] - Indeterminate progress spinner\n * active arc color.\n */\nexport class RCButton extends LitElement {\n static override styles = buttonStyles;\n\n protected static readonly _styledRoots = new WeakSet<Document | ShadowRoot>();\n\n protected static _ensureBaseStyles(root: Document | ShadowRoot): void {\n if (RCButton._styledRoots.has(root)) {\n return;\n }\n\n RCButton._styledRoots.add(root);\n\n const $style = (root instanceof Document ? root : root.ownerDocument).createElement('style');\n\n $style.setAttribute('data-rc-light-dom-base', 'rc-button');\n $style.textContent = LIGHT_DOM_CSS;\n\n if (root instanceof Document) {\n root.head.append($style);\n } else {\n root.append($style);\n }\n }\n\n /** Mirror disabled state to the native child. */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /** Show an indeterminate progress affordance and disable the native button. */\n @property({ type: Boolean, reflect: true })\n pending = false;\n\n /** Show a progress affordance and disable the native button. */\n @property({ type: Boolean, reflect: true })\n progress = false;\n\n /** Optional determinate progress percentage, clamped from 0 through 100. */\n @property({ type: Number, attribute: 'progress-value', reflect: true })\n progressValue: number | undefined;\n\n /** Opt the native child into APG toggle-button behavior. */\n @property({ type: Boolean, reflect: true })\n toggle = false;\n\n /** Controlled selected state. Host writes are silent. */\n @property({ type: Boolean, reflect: true })\n get selected(): boolean {\n return this._selected ?? this._uncontrolledSelected ?? this._defaultSelected;\n }\n\n set selected(value: boolean | undefined) {\n const oldValue = this.selected;\n\n this._selected = value;\n this._selectedInitialized = true;\n this._syncPressedState();\n this.requestUpdate('selected', oldValue);\n }\n\n /** Initial selected state for uncontrolled toggle usage. */\n @property({ type: Boolean, attribute: 'default-selected' })\n get defaultSelected(): boolean {\n return this._defaultSelected;\n }\n\n set defaultSelected(value: boolean) {\n const oldValue = this._defaultSelected;\n\n this._defaultSelected = value;\n\n if (!this._selectedInitialized && this._selected === undefined) {\n this._syncPressedState();\n this.requestUpdate('selected', oldValue);\n }\n\n this.requestUpdate('defaultSelected', oldValue);\n }\n\n /** Icon-only layout hint. May also be reflected by child classification. */\n @property({ type: Boolean, attribute: 'icon-only', reflect: true })\n iconOnly = false;\n\n /** Stretch the native child button to the host inline size. */\n @property({ type: Boolean, attribute: 'full-width', reflect: true })\n fullWidth = false;\n\n @query('slot') protected _$slot!: HTMLSlotElement;\n\n @query('[part=\"state-layer\"]') protected _$stateLayer!: HTMLElement;\n\n private _selected: boolean | undefined;\n private _defaultSelected = false;\n private _uncontrolledSelected: boolean | undefined;\n private _selectedInitialized = false;\n protected _$button: HTMLButtonElement | null = null;\n protected _buttonObserver: MutationObserver | null = null;\n protected _disabledOwned = false;\n protected _ariaBusyOwned = false;\n protected _pressedOwned = false;\n protected _authorPressed: string | null = null;\n protected _iconOnlyOwned = false;\n protected _slotMicrotaskQueued = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n RCButton._ensureBaseStyles(this.getRootNode() as Document | ShadowRoot);\n }\n\n override disconnectedCallback(): void {\n this._buttonObserver?.disconnect();\n this._buttonObserver = null;\n super.disconnectedCallback();\n }\n\n protected override firstUpdated(): void {\n this._syncSlottedButton(true);\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n if (changed.has('disabled') || changed.has('pending') || changed.has('progress')) {\n this._syncNativeState();\n }\n\n if (changed.has('toggle') || changed.has('selected')) {\n this._syncPressedState();\n }\n }\n\n protected override render() {\n const progressPercentage = this._progressPercentage;\n\n return html`\n <slot @slotchange=${this._handleSlotChange}></slot>\n <span\n part=\"state-layer\"\n aria-hidden=\"true\"\n @animationend=${this._handleRippleAnimationEnd}\n ></span>\n <span part=\"progress\" aria-hidden=\"true\" ?data-determinate=${progressPercentage !== undefined}\n >${progressPercentage === undefined ? '' : `${progressPercentage}%`}</span\n >\n `;\n }\n\n protected get _progressPercentage(): number | undefined {\n if (!this.progress || !Number.isFinite(this.progressValue)) {\n return undefined;\n }\n\n return Math.round(Math.min(100, Math.max(0, this.progressValue!)));\n }\n\n protected _handleSlotChange(): void {\n if (this._slotMicrotaskQueued) {\n return;\n }\n\n this._slotMicrotaskQueued = true;\n\n queueMicrotask(() => {\n this._slotMicrotaskQueued = false;\n\n if (!this.isConnected) {\n return;\n }\n\n this._syncSlottedButton();\n });\n }\n\n protected _syncSlottedButton(deferClassification = false): void {\n const $nextButton =\n this._$slot\n ?.assignedElements({ flatten: true })\n .find(\n (element): element is HTMLButtonElement =>\n element instanceof HTMLButtonElement && element.parentElement === this,\n ) ?? null;\n const $nextAnchor =\n this._$slot\n ?.assignedElements({ flatten: true })\n .find(\n ($element): $element is HTMLAnchorElement =>\n $element instanceof HTMLAnchorElement &&\n $element.hasAttribute('href') &&\n $element.parentElement === this,\n ) ?? null;\n\n if (!$nextButton && !$nextAnchor && import.meta.env.DEV) {\n const $misplaced = this.querySelector(\n ':scope > [data-rc-button-icon], :scope > [data-rc-button-selected-icon], :scope > [data-rc-button-label]',\n );\n\n console.warn(\n $misplaced\n ? '[rc-button] Place icon and label markers inside the direct child <button>; rc-button will not move author nodes.'\n : '[rc-button] No supported direct child found. Place a native <button> or <a href> inside <rc-button>.',\n this,\n );\n }\n\n if ($nextButton === this._$button) {\n this._classifyButton();\n this._syncNativeState();\n this._syncPressedState();\n\n return;\n }\n\n this._restorePressedState();\n this._buttonObserver?.disconnect();\n this._buttonObserver = null;\n this._$button = $nextButton;\n this._disabledOwned = false;\n this._ariaBusyOwned = false;\n this._pressedOwned = false;\n this._authorPressed = $nextButton?.getAttribute('aria-pressed') ?? null;\n\n if ($nextButton) {\n this._buttonObserver = new MutationObserver(() => {\n this._classifyButton();\n this._syncNativeState();\n this._syncPressedState();\n });\n\n this._buttonObserver.observe($nextButton, {\n attributes: true,\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n if (deferClassification) {\n queueMicrotask(() => {\n if (this.isConnected && this._$button === $nextButton) {\n this._classifyButton();\n }\n });\n } else {\n this._classifyButton();\n }\n\n this._syncNativeState();\n this._syncPressedState();\n }\n\n protected _classifyButton(): void {\n const $button = this._$button;\n const hasMarkedIcon = !!$button?.querySelector(':scope > [data-rc-button-icon]');\n const hasSelectedIcon = !!$button?.querySelector(':scope > [data-rc-button-selected-icon]');\n const hasLabel = this._hasLabel($button);\n // `data-rc-button-icon` is an opt-in marker, easy for a consumer's own\n // icon wrapper to omit; a labelless button with element content that\n // carries no visible text (an <svg>, an <iconify-icon>, and similar) is\n // unambiguously icon-only. Falling back to this keeps classification\n // (and, downstream, icon-only sizing) working without requiring the\n // marker. Text-backed icon fonts remain ambiguous and should use the\n // marker or an explicit `icon-only` attribute.\n const hasUnmarkedIcon = !hasLabel && this._hasAnyElementChild($button);\n const hasIcon = hasMarkedIcon || hasUnmarkedIcon;\n\n this.toggleAttribute('has-icon', hasIcon);\n this.toggleAttribute('has-selected-icon', hasSelectedIcon);\n this.toggleAttribute('has-label', hasLabel);\n\n if ($button && hasIcon && !hasLabel) {\n if (!this.iconOnly) {\n this._iconOnlyOwned = true;\n this.iconOnly = true;\n }\n } else if (this._iconOnlyOwned) {\n this._iconOnlyOwned = false;\n this.iconOnly = false;\n }\n }\n\n protected _hasAnyElementChild($button: HTMLButtonElement | null): boolean {\n if (!$button) {\n return false;\n }\n\n for (const $node of $button.childNodes) {\n if ($node instanceof Element) {\n return true;\n }\n }\n\n return false;\n }\n\n protected _hasLabel($button: HTMLButtonElement | null): boolean {\n if (!$button) {\n return false;\n }\n\n if ($button.querySelector(':scope > [data-rc-button-label]')) {\n return true;\n }\n\n for (const $node of $button.childNodes) {\n if ($node.nodeType === Node.TEXT_NODE && $node.textContent?.trim()) {\n return true;\n }\n\n if (!($node instanceof Element)) {\n continue;\n }\n\n // data-rc-button-progress marks consumer content that must not count as a label.\n if (\n $node.matches(\n '[data-rc-button-icon], [data-rc-button-selected-icon], [data-rc-button-progress], [aria-hidden=\"true\"]',\n )\n ) {\n continue;\n }\n\n if ($node.textContent?.trim()) {\n return true;\n }\n }\n\n return false;\n }\n\n protected _syncNativeState(): void {\n const button = this._$button;\n\n if (!button) {\n return;\n }\n\n const shouldDisable = this.disabled || this.pending || this.progress;\n\n if (shouldDisable && !button.disabled) {\n this._disabledOwned = true;\n\n // Idempotent disabled writes queue mutation records and re-enter the button observer.\n button.disabled = true;\n } else if (!shouldDisable && this._disabledOwned) {\n button.disabled = false;\n this._disabledOwned = false;\n }\n\n const busy = this.pending || this.progress;\n\n if (busy) {\n if (!button.hasAttribute('aria-busy')) {\n button.setAttribute('aria-busy', 'true');\n this._ariaBusyOwned = true;\n }\n } else if (this._ariaBusyOwned) {\n button.removeAttribute('aria-busy');\n this._ariaBusyOwned = false;\n }\n }\n\n protected _syncPressedState(): void {\n const button = this._$button;\n\n if (!button) {\n return;\n }\n\n if (this.toggle) {\n const pressed = this.selected ? 'true' : 'false';\n\n if (button.getAttribute('aria-pressed') !== pressed) {\n button.setAttribute('aria-pressed', pressed);\n }\n\n this._pressedOwned = true;\n } else if (this._pressedOwned) {\n this._restorePressedState();\n }\n }\n\n protected _restorePressedState(): void {\n const button = this._$button;\n\n if (!button || !this._pressedOwned) {\n return;\n }\n\n if (this._authorPressed === null) {\n button.removeAttribute('aria-pressed');\n } else {\n button.setAttribute('aria-pressed', this._authorPressed);\n }\n\n this._pressedOwned = false;\n }\n\n protected _handleToggleActivation(event: MouseEvent): void {\n if (\n !this.toggle ||\n this.disabled ||\n this.pending ||\n this.progress ||\n event.defaultPrevented ||\n !this._isChildButtonEvent(event)\n ) {\n return;\n }\n\n const oldValue = this.selected;\n const nextSelected = !oldValue;\n\n if (this._selected === undefined) {\n this._uncontrolledSelected = nextSelected;\n this._selectedInitialized = true;\n this._syncPressedState();\n this.requestUpdate('selected', oldValue);\n }\n\n this.dispatchEvent(\n new CustomEvent<RCButtonToggleDetail>('rc-button-toggle', {\n bubbles: true,\n composed: true,\n detail: { selected: nextSelected },\n }),\n );\n }\n\n protected _shouldBlockActivation(): boolean {\n return this.pending || this.progress;\n }\n\n protected _isChildButtonEvent(event: Event): boolean {\n return !!this._$button && event.composedPath().includes(this._$button);\n }\n\n protected _blockActivation(event: Event): void {\n if (!this._shouldBlockActivation() || !this._isChildButtonEvent(event)) {\n return;\n }\n\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n\n protected _startRipple(event: PointerEvent): void {\n if (\n !event.isPrimary ||\n event.button !== 0 ||\n this.disabled ||\n this.pending ||\n this.progress ||\n !this._isChildButtonEvent(event) ||\n !this._$stateLayer ||\n getComputedStyle(this).getPropertyValue('--_rc-button-ripple-enabled').trim() !== '1'\n ) {\n return;\n }\n\n const bounds = this._$button!.getBoundingClientRect();\n const x = event.clientX - bounds.left;\n const y = event.clientY - bounds.top;\n const radius = Math.hypot(Math.max(x, bounds.width - x), Math.max(y, bounds.height - y));\n\n this._$stateLayer.style.setProperty('--_rc-button-ripple-x', `${x}px`);\n this._$stateLayer.style.setProperty('--_rc-button-ripple-y', `${y}px`);\n this._$stateLayer.style.setProperty('--_rc-button-ripple-size', `${radius * 2}px`);\n this._$stateLayer.removeAttribute('data-rippling');\n this._$stateLayer.getBoundingClientRect();\n this._$stateLayer.setAttribute('data-rippling', '');\n }\n\n protected _handleRippleAnimationEnd(event: AnimationEvent): void {\n if (event.animationName === 'rc-button-ripple') {\n this._$stateLayer.removeAttribute('data-rippling');\n }\n }\n\n constructor() {\n super();\n\n this.addEventListener('pointerdown', this._startRipple, { capture: true });\n this.addEventListener('click', this._blockActivation, { capture: true });\n this.addEventListener('click', this._handleToggleActivation);\n\n this.addEventListener(\n 'keydown',\n (event) => {\n if (ACTIVATION_KEYS.has(event.key)) {\n this._blockActivation(event);\n }\n },\n { capture: true },\n );\n }\n}\n\nexport default RCButton;\n"],"names":["buttonStyles","css","LIGHT_DOM_CSS","ACTIVATION_KEYS","_RCButton","_a","LitElement","event","root","$style","value","oldValue","changed","progressPercentage","html","deferClassification","$nextButton","element","$element","$button","hasMarkedIcon","hasSelectedIcon","hasLabel","hasUnmarkedIcon","hasIcon","$node","button","shouldDisable","pressed","nextSelected","bounds","x","y","radius","__decorateClass","property","query","RCButton"],"mappings":";;AAEO,MAAMA,IAAeC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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"}
@@ -1,4 +1,4 @@
1
- import { R as t } from "./rc-button-Ct5C14lN.js";
1
+ import { R as t } from "./rc-button-C3zFF_QR.js";
2
2
  customElements.get("rc-button") || customElements.define("rc-button", t);
3
3
  export {
4
4
  t as RCButton
package/dist/rc-button.js CHANGED
@@ -1,4 +1,4 @@
1
- import { R as r } from "./rc-button-Ct5C14lN.js";
1
+ import { R as r } from "./rc-button-C3zFF_QR.js";
2
2
  export {
3
3
  r as RCButton
4
4
  };
@@ -13,13 +13,21 @@ export interface RCButtonToggleDetail {
13
13
  selected: boolean;
14
14
  }
15
15
  /**
16
- * Structural button wrapper that preserves a direct native `<button>` child for
17
- * progressive enhancement, forms, labels, and keyboard behavior.
16
+ * Structural action wrapper that preserves a direct native `<button>` or
17
+ * `<a href>` child for progressive enhancement and native interaction.
18
+ *
19
+ * A direct native `<a href>` child gets the same visual/layout styling
20
+ * (border, padding, background, `full-width`) for a genuine navigation
21
+ * action styled as a button. `icon-only` sizing, the touch-target hit-slop,
22
+ * and the `disabled`/`pending`/`progress` states stay button-only — those
23
+ * assume a real `HTMLButtonElement` (a native `disabled` state, a progress
24
+ * affordance that takes over the button's content) an anchor doesn't have.
18
25
  *
19
26
  * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-button rc-button docs}
20
27
  * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/button/ WAI-ARIA button pattern}
21
28
  *
22
- * @slot - A direct native `<button>` child.
29
+ * @slot - A direct native `<button>` child, or a direct `<a href>` for a
30
+ * navigation action (visual/layout styling only — see above).
23
31
  *
24
32
  * @fires rc-button-toggle - Fired when a user activates a button with `toggle`.
25
33
  *
@@ -35,8 +43,9 @@ export interface RCButtonToggleDetail {
35
43
  * @attr default-selected - Initial selected state for uncontrolled toggle usage.
36
44
  * @attr icon-only - Removes label-oriented inline padding in supporting themes.
37
45
  * @attr full-width - Stretches the native child button to the host inline size.
38
- * @attr [has-icon] - Present when the native button has a direct `[data-rc-button-icon]` child.
39
- * Use with CSS selectors (e.g. `rc-button[has-icon]`).
46
+ * @attr [has-icon] - Present when the native button has a direct marked icon or an inferred
47
+ * icon element without visible label content. Use with CSS selectors (e.g.
48
+ * `rc-button[has-icon]`).
40
49
  * @attr [has-selected-icon] - Present when the native button has a direct
41
50
  * `[data-rc-button-selected-icon]` child.
42
51
  * @attr [has-label] - Present when the native button has visible label content, from either a
@@ -61,6 +70,19 @@ export interface RCButtonToggleDetail {
61
70
  * transition when unset).
62
71
  * @cssprop [--rc-button-icon-size] - Button inline and min-inline size when `icon-only`. Falls
63
72
  * back to `--rc-button-block-size`, then `--rc-control-block-size`, then `2.5rem`.
73
+ * @cssprop [--rc-button-touch-target-block-size=3rem] - Minimum accessible touch target block
74
+ * size for an `icon-only` button. A floor, not a fixed size: has no effect once
75
+ * `--rc-button-block-size` already meets or exceeds it. Grows the host (reserving layout
76
+ * space) and the light-DOM hit-slop (the actual larger clickable region) together; the
77
+ * visible child button itself stays at its own size, centered.
78
+ * @cssprop [--rc-button-touch-target-inline-size] - Minimum accessible touch target inline size
79
+ * for an `icon-only` button. Defers to `--rc-button-touch-target-block-size` when unset.
80
+ * @cssprop [--rc-button-touch-target-overlap-inline-start=0px] - Zero by default. A theme or
81
+ * consumer sets this on an `icon-only` button that sits at a real leading edge (no neighbor on
82
+ * that side) to let its touch-target inflation overlap into whatever sits just outside the
83
+ * host, such as a container's own edge padding, instead of also reserving layout space there.
84
+ * @cssprop [--rc-button-touch-target-overlap-inline-end=0px] - The trailing-edge counterpart to
85
+ * `--rc-button-touch-target-overlap-inline-start`.
64
86
  * @cssprop [--rc-button-disabled-opacity] - Disabled button opacity (defers to native disabled
65
87
  * styling when unset).
66
88
  * @cssprop [--rc-button-busy-content-color=transparent] - Button text color while `pending` or
@@ -83,7 +105,7 @@ export interface RCButtonToggleDetail {
83
105
  */
84
106
  export declare class RCButton extends LitElement {
85
107
  static styles: import('lit').CSSResult;
86
- protected static readonly _styledRoots: Set<Document | ShadowRoot>;
108
+ protected static readonly _styledRoots: WeakSet<Document | ShadowRoot>;
87
109
  protected static _ensureBaseStyles(root: Document | ShadowRoot): void;
88
110
  /** Mirror disabled state to the native child. */
89
111
  disabled: boolean;
@@ -126,9 +148,10 @@ export declare class RCButton extends LitElement {
126
148
  protected render(): import('lit').TemplateResult<1>;
127
149
  protected get _progressPercentage(): number | undefined;
128
150
  protected _handleSlotChange(): void;
129
- protected _syncSlottedButton(): void;
151
+ protected _syncSlottedButton(deferClassification?: boolean): void;
130
152
  protected _classifyButton(): void;
131
- protected _hasLabel(button: HTMLButtonElement | null): boolean;
153
+ protected _hasAnyElementChild($button: HTMLButtonElement | null): boolean;
154
+ protected _hasLabel($button: HTMLButtonElement | null): boolean;
132
155
  protected _syncNativeState(): void;
133
156
  protected _syncPressedState(): void;
134
157
  protected _restorePressedState(): void;
package/package.json CHANGED
@@ -3,8 +3,8 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.5.0",
7
- "description": "Progressive-enhancement button wrapper with structural state affordances.",
6
+ "version": "0.6.0",
7
+ "description": "Progressive-enhancement action wrapper for a native button or anchor.",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
@@ -1 +0,0 @@
1
- {"version":3,"file":"rc-button-Ct5C14lN.js","sources":["../src/rc-button.styles.ts","../src/rc-button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const buttonStyles = css`\n :host {\n position: relative;\n display: inline-grid;\n vertical-align: middle;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host([full-width]) {\n display: grid;\n inline-size: 100%;\n }\n\n ::slotted(button) {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--rc-button-gap);\n min-block-size: var(--rc-button-block-size);\n min-inline-size: var(--rc-button-min-inline-size);\n inline-size: var(--rc-button-inline-size);\n padding-block: var(--rc-button-padding-block, revert);\n padding-inline: var(--rc-button-padding-inline, revert);\n border: var(--rc-button-border, revert);\n border-radius: var(--rc-button-radius, revert);\n background: var(--rc-button-bg, revert);\n color: var(--rc-button-color, revert);\n box-shadow: var(--rc-button-shadow, revert);\n font: var(--rc-button-font, revert);\n white-space: nowrap;\n overflow: hidden;\n box-sizing: border-box;\n z-index: 0;\n transition: var(--rc-button-transition, revert);\n }\n\n :host([full-width]) ::slotted(button) {\n inline-size: 100%;\n }\n\n :host([icon-only]) ::slotted(button) {\n inline-size: var(\n --rc-button-icon-size,\n var(--rc-button-block-size, var(--rc-control-block-size, 2.5rem))\n );\n min-inline-size: var(\n --rc-button-icon-size,\n var(--rc-button-block-size, var(--rc-control-block-size, 2.5rem))\n );\n padding-inline: 0;\n }\n\n :host([disabled]) ::slotted(button),\n :host([pending]) ::slotted(button),\n :host([progress]) ::slotted(button) {\n cursor: default;\n }\n\n :host([pending]) ::slotted(button),\n :host([progress]) ::slotted(button) {\n color: var(--rc-button-busy-content-color, transparent);\n }\n\n :host([disabled]) ::slotted(button) {\n opacity: var(--rc-button-disabled-opacity, revert);\n }\n\n [part='state-layer'],\n [part='progress'] {\n position: absolute;\n inset: 0;\n border-radius: var(--rc-button-radius, 0);\n pointer-events: none;\n }\n\n [part='state-layer'] {\n overflow: hidden;\n z-index: 1;\n }\n\n [part='state-layer']::before {\n content: '';\n position: absolute;\n inset: 0;\n background: var(--rc-button-state-layer-bg, currentColor);\n opacity: 0;\n transition: opacity var(--rc-button-state-layer-duration, 150ms)\n var(--rc-button-state-layer-easing, ease);\n }\n\n [part='state-layer']::after {\n content: '';\n position: absolute;\n inset-inline-start: var(--_rc-button-ripple-x, 50%);\n inset-block-start: var(--_rc-button-ripple-y, 50%);\n inline-size: var(--_rc-button-ripple-size, 0);\n block-size: var(--_rc-button-ripple-size, 0);\n border-radius: 50%;\n background: var(--_rc-button-ripple-color, currentColor);\n opacity: 0;\n pointer-events: none;\n translate: -50% -50%;\n scale: 0;\n }\n\n [part='state-layer'][data-rippling]::after {\n animation: rc-button-ripple var(--_rc-button-ripple-duration, 0ms)\n var(--_rc-button-ripple-easing, ease-out);\n }\n\n :host(:hover:not([disabled]):not([pending]):not([progress])) [part='state-layer']::before {\n opacity: var(--rc-button-hover-state-layer-opacity, 0);\n }\n\n :host(:focus-within:not([disabled])) [part='state-layer']::before {\n opacity: var(--rc-button-focus-state-layer-opacity, 0);\n }\n\n :host(:active:not([disabled]):not([pending]):not([progress])) [part='state-layer']::before {\n opacity: var(--rc-button-pressed-state-layer-opacity, 0);\n }\n\n [part='progress'] {\n display: none;\n place-items: center;\n color: var(--rc-button-progress-color, currentColor);\n font: var(--rc-button-progress-font, 600 0.75rem / 1 sans-serif);\n font-variant-numeric: tabular-nums;\n z-index: 2;\n }\n\n :host([pending]) [part='progress'],\n :host([progress]) [part='progress'] {\n display: grid;\n }\n\n [part='progress']::before {\n content: '';\n box-sizing: border-box;\n inline-size: var(--rc-button-progress-size, 1.25rem);\n block-size: var(--rc-button-progress-size, 1.25rem);\n border: var(--rc-button-progress-track-width, 2px) solid\n var(--rc-button-progress-track-color, color-mix(in srgb, currentColor 24%, transparent));\n border-block-start-color: var(--rc-button-progress-active-color, currentColor);\n border-radius: 9999px;\n animation: rc-button-progress-spin 900ms linear infinite;\n }\n\n [part='progress'][data-determinate]::before {\n display: none;\n }\n\n @keyframes rc-button-progress-spin {\n to {\n rotate: 1turn;\n }\n }\n\n @keyframes rc-button-ripple {\n from {\n opacity: var(--_rc-button-ripple-opacity, 0);\n scale: 0;\n }\n\n 70% {\n opacity: var(--_rc-button-ripple-opacity, 0);\n }\n\n to {\n opacity: 0;\n scale: 1;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n [part='state-layer'][data-rippling]::after {\n animation-duration: 0ms;\n }\n\n [part='progress']::before {\n animation-duration: 2s;\n }\n }\n\n @media (forced-colors: active) {\n ::slotted(button) {\n border-color: ButtonBorder;\n background: ButtonFace;\n color: ButtonText;\n }\n\n [part='state-layer']::before {\n background: Highlight;\n }\n }\n`;\n\nexport default buttonStyles;\n","import { LitElement, html } from 'lit';\nimport type { PropertyValues } from 'lit';\nimport { property, query } from 'lit/decorators.js';\n\nimport buttonStyles from './rc-button.styles.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-button': RCButton;\n }\n\n interface HTMLElementEventMap {\n 'rc-button-toggle': CustomEvent<RCButtonToggleDetail>;\n }\n}\n\nconst LIGHT_DOM_CSS = `\n@layer rc-base {\n /*\n * Icon-font utility classes commonly set display outside cascade layers.\n * Hiding the inactive icon is structural, so it must win that declaration.\n */\n rc-button[selected] > button > [data-rc-button-icon] {\n display: none !important;\n }\n\n rc-button:not([selected]) > button > [data-rc-button-selected-icon] {\n display: none !important;\n }\n}\n`;\n\nconst ACTIVATION_KEYS = new Set(['Enter', ' ']);\n\n/** Detail payload for `rc-button-toggle`. */\nexport interface RCButtonToggleDetail {\n /** Requested selected state after user activation. */\n selected: boolean;\n}\n\n/**\n * Structural button wrapper that preserves a direct native `<button>` child for\n * progressive enhancement, forms, labels, and keyboard behavior.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-button rc-button docs}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/button/ WAI-ARIA button pattern}\n *\n * @slot - A direct native `<button>` child.\n *\n * @fires rc-button-toggle - Fired when a user activates a button with `toggle`.\n *\n * @csspart state-layer - Overlay layer for hover, focus, pressed, ripple, or design-system effects.\n * @csspart progress - Non-interactive progress affordance overlay shown for `pending` or `progress`.\n *\n * @attr disabled - Mirrors disabled state to the native child button.\n * @attr pending - Disables the native button and exposes an indeterminate progress affordance.\n * @attr progress - Disables the native button and exposes a progress affordance.\n * @attr progress-value - Optional determinate progress percentage, clamped from 0 through 100.\n * @attr toggle - Opts the native child into APG toggle-button behavior.\n * @attr selected - Controlled selected state for toggle semantics, styling, and icon switching.\n * @attr default-selected - Initial selected state for uncontrolled toggle usage.\n * @attr icon-only - Removes label-oriented inline padding in supporting themes.\n * @attr full-width - Stretches the native child button to the host inline size.\n * @attr [has-icon] - Present when the native button has a direct `[data-rc-button-icon]` child.\n * Use with CSS selectors (e.g. `rc-button[has-icon]`).\n * @attr [has-selected-icon] - Present when the native button has a direct\n * `[data-rc-button-selected-icon]` child.\n * @attr [has-label] - Present when the native button has visible label content, from either a\n * `[data-rc-button-label]` child or bare text.\n *\n * @cssprop [--rc-button-gap] - Gap between icon and label content.\n * @cssprop [--rc-button-block-size] - Minimum button block size.\n * @cssprop [--rc-button-min-inline-size] - Minimum button inline size.\n * @cssprop [--rc-button-inline-size] - Button inline size.\n * @cssprop [--rc-button-padding-block] - Button block-axis padding (defers to native button\n * padding when unset).\n * @cssprop [--rc-button-padding-inline] - Button inline-axis padding (defers to native button\n * padding when unset).\n * @cssprop [--rc-button-border] - Button border (defers to native button border when unset).\n * @cssprop [--rc-button-radius] - Button and overlay border radius (defers to native button\n * radius when unset).\n * @cssprop [--rc-button-bg] - Button background (defers to native button background when unset).\n * @cssprop [--rc-button-color] - Button text color (defers to native button color when unset).\n * @cssprop [--rc-button-shadow] - Button box shadow (defers to native button shadow when unset).\n * @cssprop [--rc-button-font] - Button font shorthand (defers to native button font when unset).\n * @cssprop [--rc-button-transition] - Button transition shorthand (defers to native button\n * transition when unset).\n * @cssprop [--rc-button-icon-size] - Button inline and min-inline size when `icon-only`. Falls\n * back to `--rc-button-block-size`, then `--rc-control-block-size`, then `2.5rem`.\n * @cssprop [--rc-button-disabled-opacity] - Disabled button opacity (defers to native disabled\n * styling when unset).\n * @cssprop [--rc-button-busy-content-color=transparent] - Button text color while `pending` or\n * `progress`.\n * @cssprop [--rc-button-state-layer-bg=currentColor] - State-layer overlay color.\n * @cssprop [--rc-button-state-layer-duration=150ms] - State-layer opacity transition duration.\n * @cssprop [--rc-button-state-layer-easing=ease] - State-layer opacity transition easing.\n * @cssprop [--rc-button-hover-state-layer-opacity=0] - State-layer opacity on hover.\n * @cssprop [--rc-button-focus-state-layer-opacity=0] - State-layer opacity on focus-within.\n * @cssprop [--rc-button-pressed-state-layer-opacity=0] - State-layer opacity on active press.\n * @cssprop [--rc-button-progress-color=currentColor] - Progress affordance color.\n * @cssprop [--rc-button-progress-font=600 0.75rem / 1 sans-serif] - Determinate progress\n * percentage font.\n * @cssprop [--rc-button-progress-size=1.25rem] - Indeterminate progress spinner diameter.\n * @cssprop [--rc-button-progress-track-width=2px] - Indeterminate progress spinner stroke width.\n * @cssprop [--rc-button-progress-track-color=color-mix(in srgb, currentColor 24%, transparent)] -\n * Indeterminate progress spinner track color.\n * @cssprop [--rc-button-progress-active-color=currentColor] - Indeterminate progress spinner\n * active arc color.\n */\nexport class RCButton extends LitElement {\n static override styles = buttonStyles;\n\n protected static readonly _styledRoots = new Set<Document | ShadowRoot>();\n\n protected static _ensureBaseStyles(root: Document | ShadowRoot): void {\n if (RCButton._styledRoots.has(root)) {\n return;\n }\n\n RCButton._styledRoots.add(root);\n\n const style = document.createElement('style');\n\n style.setAttribute('data-rc-light-dom-base', 'rc-button');\n style.textContent = LIGHT_DOM_CSS;\n\n if (root instanceof Document) {\n root.head.append(style);\n } else {\n root.append(style);\n }\n }\n\n /** Mirror disabled state to the native child. */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /** Show an indeterminate progress affordance and disable the native button. */\n @property({ type: Boolean, reflect: true })\n pending = false;\n\n /** Show a progress affordance and disable the native button. */\n @property({ type: Boolean, reflect: true })\n progress = false;\n\n /** Optional determinate progress percentage, clamped from 0 through 100. */\n @property({ type: Number, attribute: 'progress-value', reflect: true })\n progressValue: number | undefined;\n\n /** Opt the native child into APG toggle-button behavior. */\n @property({ type: Boolean, reflect: true })\n toggle = false;\n\n /** Controlled selected state. Host writes are silent. */\n @property({ type: Boolean, reflect: true })\n get selected(): boolean {\n return this._selected ?? this._uncontrolledSelected ?? this._defaultSelected;\n }\n\n set selected(value: boolean | undefined) {\n const oldValue = this.selected;\n\n this._selected = value;\n this._selectedInitialized = true;\n this._syncPressedState();\n this.requestUpdate('selected', oldValue);\n }\n\n /** Initial selected state for uncontrolled toggle usage. */\n @property({ type: Boolean, attribute: 'default-selected' })\n get defaultSelected(): boolean {\n return this._defaultSelected;\n }\n\n set defaultSelected(value: boolean) {\n const oldValue = this._defaultSelected;\n\n this._defaultSelected = value;\n\n if (!this._selectedInitialized && this._selected === undefined) {\n this._syncPressedState();\n this.requestUpdate('selected', oldValue);\n }\n\n this.requestUpdate('defaultSelected', oldValue);\n }\n\n /** Icon-only layout hint. May also be reflected by child classification. */\n @property({ type: Boolean, attribute: 'icon-only', reflect: true })\n iconOnly = false;\n\n /** Stretch the native child button to the host inline size. */\n @property({ type: Boolean, attribute: 'full-width', reflect: true })\n fullWidth = false;\n\n @query('slot') protected _$slot!: HTMLSlotElement;\n\n @query('[part=\"state-layer\"]') protected _$stateLayer!: HTMLElement;\n\n private _selected: boolean | undefined;\n private _defaultSelected = false;\n private _uncontrolledSelected: boolean | undefined;\n private _selectedInitialized = false;\n protected _$button: HTMLButtonElement | null = null;\n protected _buttonObserver: MutationObserver | null = null;\n protected _disabledOwned = false;\n protected _ariaBusyOwned = false;\n protected _pressedOwned = false;\n protected _authorPressed: string | null = null;\n protected _iconOnlyOwned = false;\n protected _slotMicrotaskQueued = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n RCButton._ensureBaseStyles(this.getRootNode() as Document | ShadowRoot);\n }\n\n override disconnectedCallback(): void {\n this._buttonObserver?.disconnect();\n this._buttonObserver = null;\n super.disconnectedCallback();\n }\n\n protected override firstUpdated(): void {\n this._syncSlottedButton();\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n if (changed.has('disabled') || changed.has('pending') || changed.has('progress')) {\n this._syncNativeState();\n }\n\n if (changed.has('toggle') || changed.has('selected')) {\n this._syncPressedState();\n }\n }\n\n protected override render() {\n const progressPercentage = this._progressPercentage;\n\n return html`\n <slot @slotchange=${this._handleSlotChange}></slot>\n <span\n part=\"state-layer\"\n aria-hidden=\"true\"\n @animationend=${this._handleRippleAnimationEnd}\n ></span>\n <span part=\"progress\" aria-hidden=\"true\" ?data-determinate=${progressPercentage !== undefined}\n >${progressPercentage === undefined ? '' : `${progressPercentage}%`}</span\n >\n `;\n }\n\n protected get _progressPercentage(): number | undefined {\n if (!this.progress || !Number.isFinite(this.progressValue)) {\n return undefined;\n }\n\n return Math.round(Math.min(100, Math.max(0, this.progressValue!)));\n }\n\n protected _handleSlotChange(): void {\n if (this._slotMicrotaskQueued) {\n return;\n }\n\n this._slotMicrotaskQueued = true;\n\n queueMicrotask(() => {\n this._slotMicrotaskQueued = false;\n\n if (!this.isConnected) {\n return;\n }\n\n this._syncSlottedButton();\n });\n }\n\n protected _syncSlottedButton(): void {\n const nextButton =\n this._$slot\n ?.assignedElements({ flatten: true })\n .find(\n (element): element is HTMLButtonElement =>\n element instanceof HTMLButtonElement && element.parentElement === this,\n ) ?? null;\n\n if (!nextButton && import.meta.env.DEV) {\n const misplaced = this.querySelector(\n ':scope > [data-rc-button-icon], :scope > [data-rc-button-selected-icon], :scope > [data-rc-button-label]',\n );\n\n console.warn(\n misplaced\n ? '[rc-button] Place icon and label markers inside the direct child <button>; rc-button will not move author nodes.'\n : '[rc-button] No direct child <button> found. Place a native <button> inside <rc-button>.',\n );\n }\n\n if (nextButton === this._$button) {\n this._classifyButton();\n this._syncNativeState();\n this._syncPressedState();\n\n return;\n }\n\n this._restorePressedState();\n this._buttonObserver?.disconnect();\n this._buttonObserver = null;\n this._$button = nextButton;\n this._disabledOwned = false;\n this._ariaBusyOwned = false;\n this._pressedOwned = false;\n this._authorPressed = nextButton?.getAttribute('aria-pressed') ?? null;\n\n if (nextButton) {\n this._buttonObserver = new MutationObserver(() => {\n this._classifyButton();\n this._syncNativeState();\n this._syncPressedState();\n });\n\n this._buttonObserver.observe(nextButton, {\n attributes: true,\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n this._classifyButton();\n this._syncNativeState();\n this._syncPressedState();\n }\n\n protected _classifyButton(): void {\n const button = this._$button;\n const hasIcon = !!button?.querySelector(':scope > [data-rc-button-icon]');\n const hasSelectedIcon = !!button?.querySelector(':scope > [data-rc-button-selected-icon]');\n const hasLabel = this._hasLabel(button);\n\n this.toggleAttribute('has-icon', hasIcon);\n this.toggleAttribute('has-selected-icon', hasSelectedIcon);\n this.toggleAttribute('has-label', hasLabel);\n\n if (button && hasIcon && !hasLabel) {\n if (!this.iconOnly) {\n this._iconOnlyOwned = true;\n this.iconOnly = true;\n }\n } else if (this._iconOnlyOwned) {\n this._iconOnlyOwned = false;\n this.iconOnly = false;\n }\n }\n\n protected _hasLabel(button: HTMLButtonElement | null): boolean {\n if (!button) {\n return false;\n }\n\n if (button.querySelector(':scope > [data-rc-button-label]')) {\n return true;\n }\n\n for (const node of button.childNodes) {\n if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {\n return true;\n }\n\n if (!(node instanceof HTMLElement)) {\n continue;\n }\n\n // data-rc-button-progress marks consumer content that must not count as a label.\n if (\n node.matches(\n '[data-rc-button-icon], [data-rc-button-selected-icon], [data-rc-button-progress], [aria-hidden=\"true\"]',\n )\n ) {\n continue;\n }\n\n if (node.textContent?.trim()) {\n return true;\n }\n }\n\n return false;\n }\n\n protected _syncNativeState(): void {\n const button = this._$button;\n\n if (!button) {\n return;\n }\n\n const shouldDisable = this.disabled || this.pending || this.progress;\n\n if (shouldDisable && !button.disabled) {\n this._disabledOwned = true;\n\n // Idempotent disabled writes queue mutation records and re-enter the button observer.\n button.disabled = true;\n } else if (!shouldDisable && this._disabledOwned) {\n button.disabled = false;\n this._disabledOwned = false;\n }\n\n const busy = this.pending || this.progress;\n\n if (busy) {\n if (!button.hasAttribute('aria-busy')) {\n button.setAttribute('aria-busy', 'true');\n this._ariaBusyOwned = true;\n }\n } else if (this._ariaBusyOwned) {\n button.removeAttribute('aria-busy');\n this._ariaBusyOwned = false;\n }\n }\n\n protected _syncPressedState(): void {\n const button = this._$button;\n\n if (!button) {\n return;\n }\n\n if (this.toggle) {\n const pressed = this.selected ? 'true' : 'false';\n\n if (button.getAttribute('aria-pressed') !== pressed) {\n button.setAttribute('aria-pressed', pressed);\n }\n\n this._pressedOwned = true;\n } else if (this._pressedOwned) {\n this._restorePressedState();\n }\n }\n\n protected _restorePressedState(): void {\n const button = this._$button;\n\n if (!button || !this._pressedOwned) {\n return;\n }\n\n if (this._authorPressed === null) {\n button.removeAttribute('aria-pressed');\n } else {\n button.setAttribute('aria-pressed', this._authorPressed);\n }\n\n this._pressedOwned = false;\n }\n\n protected _handleToggleActivation(event: MouseEvent): void {\n if (\n !this.toggle ||\n this.disabled ||\n this.pending ||\n this.progress ||\n event.defaultPrevented ||\n !this._isChildButtonEvent(event)\n ) {\n return;\n }\n\n const oldValue = this.selected;\n const nextSelected = !oldValue;\n\n if (this._selected === undefined) {\n this._uncontrolledSelected = nextSelected;\n this._selectedInitialized = true;\n this._syncPressedState();\n this.requestUpdate('selected', oldValue);\n }\n\n this.dispatchEvent(\n new CustomEvent<RCButtonToggleDetail>('rc-button-toggle', {\n bubbles: true,\n composed: true,\n detail: { selected: nextSelected },\n }),\n );\n }\n\n protected _shouldBlockActivation(): boolean {\n return this.pending || this.progress;\n }\n\n protected _isChildButtonEvent(event: Event): boolean {\n return !!this._$button && event.composedPath().includes(this._$button);\n }\n\n protected _blockActivation(event: Event): void {\n if (!this._shouldBlockActivation() || !this._isChildButtonEvent(event)) {\n return;\n }\n\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n\n protected _startRipple(event: PointerEvent): void {\n if (\n !event.isPrimary ||\n event.button !== 0 ||\n this.disabled ||\n this.pending ||\n this.progress ||\n !this._isChildButtonEvent(event) ||\n !this._$stateLayer ||\n getComputedStyle(this).getPropertyValue('--_rc-button-ripple-enabled').trim() !== '1'\n ) {\n return;\n }\n\n const bounds = this._$button!.getBoundingClientRect();\n const x = event.clientX - bounds.left;\n const y = event.clientY - bounds.top;\n const radius = Math.hypot(Math.max(x, bounds.width - x), Math.max(y, bounds.height - y));\n\n this._$stateLayer.style.setProperty('--_rc-button-ripple-x', `${x}px`);\n this._$stateLayer.style.setProperty('--_rc-button-ripple-y', `${y}px`);\n this._$stateLayer.style.setProperty('--_rc-button-ripple-size', `${radius * 2}px`);\n this._$stateLayer.removeAttribute('data-rippling');\n this._$stateLayer.getBoundingClientRect();\n this._$stateLayer.setAttribute('data-rippling', '');\n }\n\n protected _handleRippleAnimationEnd(event: AnimationEvent): void {\n if (event.animationName === 'rc-button-ripple') {\n this._$stateLayer.removeAttribute('data-rippling');\n }\n }\n\n constructor() {\n super();\n\n this.addEventListener('pointerdown', this._startRipple, { capture: true });\n this.addEventListener('click', this._blockActivation, { capture: true });\n this.addEventListener('click', this._handleToggleActivation);\n\n this.addEventListener(\n 'keydown',\n (event) => {\n if (ACTIVATION_KEYS.has(event.key)) {\n this._blockActivation(event);\n }\n },\n { capture: true },\n );\n }\n}\n\nexport default RCButton;\n"],"names":["buttonStyles","css","LIGHT_DOM_CSS","ACTIVATION_KEYS","_RCButton","_a","LitElement","event","root","style","value","oldValue","changed","progressPercentage","html","nextButton","element","button","hasIcon","hasSelectedIcon","hasLabel","node","shouldDisable","pressed","nextSelected","bounds","x","y","radius","__decorateClass","property","query","RCButton"],"mappings":"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"}