@waggylabs/yumekit 0.5.1-beta.4 → 0.5.1-beta.5

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/components/y-appbar.js +328 -81
  3. package/dist/components/y-banner.js +328 -81
  4. package/dist/components/y-button/y-button.d.ts +12 -0
  5. package/dist/components/y-button.d.ts +12 -0
  6. package/dist/components/y-button.js +328 -81
  7. package/dist/components/y-code/y-code.d.ts +1 -0
  8. package/dist/components/y-code.d.ts +1 -0
  9. package/dist/components/y-code.js +20 -5
  10. package/dist/components/y-color/y-color.d.ts +6 -0
  11. package/dist/components/y-color.d.ts +6 -0
  12. package/dist/components/y-color.js +68 -0
  13. package/dist/components/y-colorpicker.js +47 -0
  14. package/dist/components/y-data-grid.js +408 -87
  15. package/dist/components/y-date/y-date.d.ts +6 -0
  16. package/dist/components/y-date.d.ts +6 -0
  17. package/dist/components/y-date.js +381 -87
  18. package/dist/components/y-datepicker.js +360 -87
  19. package/dist/components/y-gallery.js +1 -1
  20. package/dist/components/y-help.js +328 -81
  21. package/dist/components/y-input/y-input.d.ts +7 -0
  22. package/dist/components/y-input.d.ts +7 -0
  23. package/dist/components/y-input.js +24 -0
  24. package/dist/components/y-paginator.js +354 -81
  25. package/dist/components/y-select/y-select.d.ts +7 -0
  26. package/dist/components/y-select.d.ts +7 -0
  27. package/dist/components/y-select.js +23 -0
  28. package/dist/components/y-sidebar.js +328 -81
  29. package/dist/components/y-tabs/y-tabs.d.ts +7 -0
  30. package/dist/components/y-tabs.d.ts +7 -0
  31. package/dist/components/y-tabs.js +74 -14
  32. package/dist/components/y-textarea/y-textarea.d.ts +6 -0
  33. package/dist/components/y-textarea.d.ts +6 -0
  34. package/dist/components/y-textarea.js +21 -0
  35. package/dist/components/y-theme/y-theme.d.ts +1 -0
  36. package/dist/components/y-theme.d.ts +1 -0
  37. package/dist/components/y-theme.js +62 -32
  38. package/dist/index.js +607 -139
  39. package/dist/react.d.ts +7 -0
  40. package/dist/styles/blue-dark.css +6 -6
  41. package/dist/styles/blue-light.css +6 -6
  42. package/dist/styles/brown-dark.css +6 -6
  43. package/dist/styles/brown-light.css +6 -6
  44. package/dist/styles/carbon-dark.css +222 -0
  45. package/dist/styles/carbon-light.css +222 -0
  46. package/dist/styles/green-dark.css +6 -6
  47. package/dist/styles/green-light.css +6 -6
  48. package/dist/styles/indigo-dark.css +6 -6
  49. package/dist/styles/indigo-light.css +6 -6
  50. package/dist/styles/material-blue-dark.css +234 -0
  51. package/dist/styles/material-blue-light.css +234 -0
  52. package/dist/styles/olive-dark.css +6 -6
  53. package/dist/styles/olive-light.css +6 -6
  54. package/dist/styles/orange-dark.css +6 -6
  55. package/dist/styles/orange-light.css +6 -6
  56. package/dist/styles/pink-dark.css +6 -6
  57. package/dist/styles/pink-light.css +6 -6
  58. package/dist/styles/purple-dark.css +6 -6
  59. package/dist/styles/purple-light.css +6 -6
  60. package/dist/styles/red-dark.css +6 -6
  61. package/dist/styles/red-light.css +6 -6
  62. package/dist/styles/teal-dark.css +6 -6
  63. package/dist/styles/teal-light.css +6 -6
  64. package/dist/styles/variables.css +10 -8
  65. package/dist/styles/yellow-dark.css +6 -6
  66. package/dist/styles/yellow-light.css +6 -6
  67. package/dist/yumekit.min.js +1 -1
  68. package/llm.txt +63 -36
  69. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -35,6 +35,14 @@ Delete any empty sections before publishing.
35
35
 
36
36
  ### Added
37
37
 
38
+ - Two new built-in themes, `material-blue-light` and `material-blue-dark`, applying Material Design 3 — a Material palette (Blue primary, Material status colors, grey surfaces), Roboto typography, and the M3 shape scale (4/8/12/28px radii, pill buttons, 28px dialogs, 4px text fields, 1px borders) applied across all components. `y-theme` now loads the Roboto webfont automatically when one of these themes is active.
39
+
40
+ - Two new built-in themes, `carbon-light` and `carbon-dark`, applying IBM Carbon — the Carbon palette (Interactive Blue `#0f62fe`, IBM gray surfaces, Carbon status colors), IBM Plex Sans typography, and Carbon's sharp shape language (0px radii, 1px borders). `y-theme` loads the IBM Plex Sans webfont automatically when one of these themes is active.
41
+
42
+ - `y-tabs` — new `variant="accent"` style: minimal tabs where the active tab shows a primary-colored indicator border on its content-facing edge (bottom for top tabs, etc.). The default bordered style is unchanged. Adds `--component-tabs-accent-width` (indicator thickness). The border-width token is normalized to `--component-tabs-border-width` (matching `--component-tabs-border-color`); the legacy `--component-tab-border-width` is still honored as a fallback.
43
+
44
+ - Form fields (`y-input`, `y-textarea`, `y-select`, `y-color`, `y-date`) — new `variant="underline"` style: a bottom-only border with square bottom corners (the Material/Carbon text-field look) instead of the full-border `"default"`. Hover/focus/invalid states still color the underline. Helps match design systems that use underlined fields.
45
+
38
46
  - New `y-data-grid` component — interactive grid for large or dynamic datasets with client- or server-side sorting, filtering, and pagination, single/multi row selection, inline cell editing, nested row grouping with aggregates, multi-column header groups, virtual scrolling, an optional per-column header menu (filter, sort, visibility, reorder), and a sticky header.
39
47
 
40
48
  - New `y-popover` component — target-anchored floating panel with rich slotted content, composable triggers (`click` / `hover` / `focus` / `context-menu` / `manual`), flip-on-collision positioning, optional modal mode with focus trap, and an opt-in `portal` mode that renders into `<body>` to escape ancestor stacking contexts.
@@ -51,6 +59,8 @@ Delete any empty sections before publishing.
51
59
 
52
60
  ### Changed
53
61
 
62
+ - `y-button` — padding can now be set per-axis via `--component-button-padding-block-{size}` / `--component-button-padding-inline-{size}` (overriding, and falling back to, the all-sides `--component-button-padding-{size}`), plus a new `padding-mode` attribute (`auto` (default) / `square` / `wide`) controlling whether the inline axis collapses to the block value. `auto` makes icon-only buttons square automatically; `square`/`wide` force it. The Material themes use this for wide pill buttons that stay round when icon-only; `y-paginator` number buttons and `y-datepicker` day / month / year buttons set `padding-mode="square"` so they don't bloat under the wide Material padding.
63
+
54
64
  - Custom color support expanded to the browser-native color functions — `hwb()`, `lab()`, `lch()`, `oklab()`, `oklch()`, and `color()` — in addition to `#hex`, `rgb()`/`rgba()`, and `hsl()`/`hsla()`. The shared `isSafeCssColor` gate now also tightens its argument allowlist (rejecting semicolons, braces, angle brackets, and nested functions). Applies anywhere a `color` accepts a custom value, including `y-badge`, `y-select`, `y-popover`, `y-button`, `y-tag`, `y-icon`, and `y-rating`.
55
65
 
56
66
  - `y-rating` — selected icons now swap in the registered `filled` weight variant instead of just thickening the line stroke, falling back to the thick stroke when no filled variant is available.
@@ -63,6 +73,18 @@ Delete any empty sections before publishing.
63
73
 
64
74
  ### Fixed
65
75
 
76
+ - Form field components now share one field background. `y-select` (trigger and dropdown) used `base.background.app` while `y-input` / `y-textarea` / `y-color` / `y-date` used `base.background.component`; `select.background` is now `base.background.component` across all themes, so fields match when placed together on a form (most visible in the Material and Carbon themes). The select dropdown panel now also matches menus/popovers.
77
+
78
+ - `y-avatar` — the three `shape` values are now visually distinct. Added a `--component-avatar-border-radius-rounded` token (medium radius) and changed `square` to a zero radius (sharp corners); `circle` is unchanged.
79
+
80
+ - `y-tag` — Increased the inline padding size for all sizes of `y-tag` component.
81
+
82
+ - `y-tabs` — the tab panel no longer creates a stacking context (`z-index: 0` removed), which was trapping `position: fixed` overlays rendered by slotted components (e.g. `y-gallery`'s lightbox) underneath the tab strip and surrounding page content.
83
+
84
+ - `y-gallery` — the lightbox overlay's default z-index was raised from 1000 to 9000 so it layers above fixed chrome like `y-dock` (8000); override via `--component-gallery-expand-z-index`.
85
+
86
+ - `y-menu` — selected items now use the primary inverse content color for their text, so the label stays readable against the primary-colored selected background across all themes.
87
+
66
88
  - `y-button` — no longer throws when `color` is set to an unrecognized value that isn't a valid custom color; it now falls back to the `base` theme instead of crashing while reading the color-token map.
67
89
 
68
90
  - `y-droplist` — touch drag now works reliably on iOS Safari and Chrome Android. `touch-action: none` is applied to the press target at decoration time instead of from inside `pointerdown`, so mobile browsers stop preempting the press as a scroll gesture.
@@ -3,12 +3,32 @@ import { contrastTextColor, isSafeCssColor, createElement, resolveAnchor, isNavI
3
3
  class YumeButton extends HTMLElement {
4
4
  static get observedAttributes() {
5
5
  return [
6
- "left-icon", "right-icon", "color", "size", "style-type", "type",
7
- "disabled", "name", "value", "autofocus", "form", "formaction",
8
- "formenctype", "formmethod", "formnovalidate", "formtarget",
9
- "aria-label", "aria-pressed", "aria-hidden",
10
- "aria-haspopup", "aria-expanded", "aria-controls",
11
- "href", "target", "rel",
6
+ "left-icon",
7
+ "right-icon",
8
+ "color",
9
+ "size",
10
+ "style-type",
11
+ "type",
12
+ "padding-mode",
13
+ "disabled",
14
+ "name",
15
+ "value",
16
+ "autofocus",
17
+ "form",
18
+ "formaction",
19
+ "formenctype",
20
+ "formmethod",
21
+ "formnovalidate",
22
+ "formtarget",
23
+ "aria-label",
24
+ "aria-pressed",
25
+ "aria-hidden",
26
+ "aria-haspopup",
27
+ "aria-expanded",
28
+ "aria-controls",
29
+ "href",
30
+ "target",
31
+ "rel",
12
32
  ];
13
33
  }
14
34
 
@@ -51,65 +71,110 @@ class YumeButton extends HTMLElement {
51
71
  // -------------------------------------------------------------------------
52
72
 
53
73
  /** URL to navigate to. When set, the internal element renders as an <a> instead of <button>. */
54
- get href() { return this.getAttribute("href"); }
74
+ get href() {
75
+ return this.getAttribute("href");
76
+ }
55
77
  set href(val) {
56
78
  if (val != null) this.setAttribute("href", val);
57
79
  else this.removeAttribute("href");
58
80
  }
59
81
 
60
82
  /** Anchor target (e.g. "_blank"). Only applies when href is set. */
61
- get target() { return this.getAttribute("target"); }
83
+ get target() {
84
+ return this.getAttribute("target");
85
+ }
62
86
  set target(val) {
63
87
  if (val != null) this.setAttribute("target", val);
64
88
  else this.removeAttribute("target");
65
89
  }
66
90
 
67
91
  /** Anchor rel attribute (e.g. "noopener noreferrer"). Only applies when href is set. */
68
- get rel() { return this.getAttribute("rel"); }
92
+ get rel() {
93
+ return this.getAttribute("rel");
94
+ }
69
95
  set rel(val) {
70
96
  if (val != null) this.setAttribute("rel", val);
71
97
  else this.removeAttribute("rel");
72
98
  }
73
99
 
74
100
  /** Color theme for the button (default "base"). */
75
- get color() { return this.getAttribute("color") || "base"; }
76
- set color(val) { this.setAttribute("color", val); }
101
+ get color() {
102
+ return this.getAttribute("color") || "base";
103
+ }
104
+ set color(val) {
105
+ this.setAttribute("color", val);
106
+ }
77
107
 
78
108
  /** Whether the button is disabled. */
79
- get disabled() { return this.hasAttribute("disabled"); }
109
+ get disabled() {
110
+ return this.hasAttribute("disabled");
111
+ }
80
112
  set disabled(val) {
81
113
  if (val) this.setAttribute("disabled", "");
82
114
  else this.removeAttribute("disabled");
83
115
  }
84
116
 
85
117
  /** The form name of the button. */
86
- get name() { return this.getAttribute("name") || ""; }
87
- set name(val) { this.setAttribute("name", val); }
118
+ get name() {
119
+ return this.getAttribute("name") || "";
120
+ }
121
+ set name(val) {
122
+ this.setAttribute("name", val);
123
+ }
124
+
125
+ /**
126
+ * Padding mode: "auto" (default) uses square padding only for icon-only
127
+ * buttons; "square" forces equal block/inline padding (e.g. paginator
128
+ * number buttons); "wide" always uses the inline padding even when icon-only.
129
+ */
130
+ get paddingMode() {
131
+ const mode = this.getAttribute("padding-mode");
132
+ return ["auto", "square", "wide"].includes(mode) ? mode : "auto";
133
+ }
134
+ set paddingMode(val) {
135
+ this.setAttribute("padding-mode", val);
136
+ }
88
137
 
89
138
  /** Size: "small" | "medium" | "large" (default "medium"). */
90
- get size() { return this.getAttribute("size") || "medium"; }
91
- set size(val) { this.setAttribute("size", val); }
139
+ get size() {
140
+ return this.getAttribute("size") || "medium";
141
+ }
142
+ set size(val) {
143
+ this.setAttribute("size", val);
144
+ }
92
145
 
93
146
  /** Visual style: "filled" | "outlined" | "flat" (default "outlined"). */
94
- get styleType() { return this.getAttribute("style-type") || "outlined"; }
95
- set styleType(val) { this.setAttribute("style-type", val); }
147
+ get styleType() {
148
+ return this.getAttribute("style-type") || "outlined";
149
+ }
150
+ set styleType(val) {
151
+ this.setAttribute("style-type", val);
152
+ }
96
153
 
97
154
  /** Button type: "button" | "submit" | "reset" (default "button"). */
98
- get type() { return this.getAttribute("type") || "button"; }
99
- set type(val) { this.setAttribute("type", val); }
155
+ get type() {
156
+ return this.getAttribute("type") || "button";
157
+ }
158
+ set type(val) {
159
+ this.setAttribute("type", val);
160
+ }
100
161
 
101
162
  /** The current selected value(s), comma-separated when 'multiple' is set. */
102
163
  get value() {
103
164
  if (this.hasAttribute("multiple")) {
104
165
  return Array.from(this.selectedValues).join(",");
105
166
  } else {
106
- return this.selectedValues.size ? Array.from(this.selectedValues)[0] : "";
167
+ return this.selectedValues.size
168
+ ? Array.from(this.selectedValues)[0]
169
+ : "";
107
170
  }
108
171
  }
109
172
  set value(newVal) {
110
173
  if (this.hasAttribute("multiple")) {
111
174
  if (typeof newVal === "string") {
112
- this.selectedValues = new Set(newVal.split(",").map((s) => s.trim()));
175
+ this.selectedValues = new Set(
176
+ newVal.split(",").map((s) => s.trim()),
177
+ );
113
178
  } else if (Array.isArray(newVal)) {
114
179
  this.selectedValues = new Set(newVal);
115
180
  }
@@ -133,27 +198,35 @@ class YumeButton extends HTMLElement {
133
198
 
134
199
  _addEventListeners() {
135
200
  this.button.addEventListener("focus", () => {
136
- this.dispatchEvent(new CustomEvent("focus", { bubbles: true, composed: true }));
201
+ this.dispatchEvent(
202
+ new CustomEvent("focus", { bubbles: true, composed: true }),
203
+ );
137
204
  });
138
205
 
139
206
  this.button.addEventListener("blur", () => {
140
- this.dispatchEvent(new CustomEvent("blur", { bubbles: true, composed: true }));
207
+ this.dispatchEvent(
208
+ new CustomEvent("blur", { bubbles: true, composed: true }),
209
+ );
141
210
  });
142
211
 
143
212
  this.button.addEventListener("keydown", (event) => {
144
- this.dispatchEvent(new CustomEvent("keydown", {
145
- detail: { key: event.key, code: event.code },
146
- bubbles: true,
147
- composed: true,
148
- }));
213
+ this.dispatchEvent(
214
+ new CustomEvent("keydown", {
215
+ detail: { key: event.key, code: event.code },
216
+ bubbles: true,
217
+ composed: true,
218
+ }),
219
+ );
149
220
  });
150
221
 
151
222
  this.button.addEventListener("keyup", (event) => {
152
- this.dispatchEvent(new CustomEvent("keyup", {
153
- detail: { key: event.key, code: event.code },
154
- bubbles: true,
155
- composed: true,
156
- }));
223
+ this.dispatchEvent(
224
+ new CustomEvent("keyup", {
225
+ detail: { key: event.key, code: event.code },
226
+ bubbles: true,
227
+ composed: true,
228
+ }),
229
+ );
157
230
  });
158
231
 
159
232
  this.button.addEventListener("click", (event) => {
@@ -228,15 +301,42 @@ class YumeButton extends HTMLElement {
228
301
  }
229
302
 
230
303
  _applyFilledInteractionStyles(vars) {
231
- this.button.style.setProperty("--hover-background-color", `var(${vars[1]}, #292a2b)`);
232
- this.button.style.setProperty("--hover-text-color", `var(${vars[6]}, #0c0c0d)`);
233
- this.button.style.setProperty("--hover-border-color", `var(${vars[1]}, #292a2b)`);
234
- this.button.style.setProperty("--focus-background-color", `var(${vars[2]}, #46474a)`);
235
- this.button.style.setProperty("--focus-text-color", `var(${vars[6]}, #0c0c0d)`);
236
- this.button.style.setProperty("--focus-border-color", `var(${vars[2]}, #46474a)`);
237
- this.button.style.setProperty("--active-background-color", `var(${vars[3]}, #0c0c0d)`);
238
- this.button.style.setProperty("--active-text-color", `var(${vars[0]}, #f7f7fa)`);
239
- this.button.style.setProperty("--active-border-color", `var(${vars[3]}, #0c0c0d)`);
304
+ this.button.style.setProperty(
305
+ "--hover-background-color",
306
+ `var(${vars[1]}, #292a2b)`,
307
+ );
308
+ this.button.style.setProperty(
309
+ "--hover-text-color",
310
+ `var(${vars[6]}, #0c0c0d)`,
311
+ );
312
+ this.button.style.setProperty(
313
+ "--hover-border-color",
314
+ `var(${vars[1]}, #292a2b)`,
315
+ );
316
+ this.button.style.setProperty(
317
+ "--focus-background-color",
318
+ `var(${vars[2]}, #46474a)`,
319
+ );
320
+ this.button.style.setProperty(
321
+ "--focus-text-color",
322
+ `var(${vars[6]}, #0c0c0d)`,
323
+ );
324
+ this.button.style.setProperty(
325
+ "--focus-border-color",
326
+ `var(${vars[2]}, #46474a)`,
327
+ );
328
+ this.button.style.setProperty(
329
+ "--active-background-color",
330
+ `var(${vars[3]}, #0c0c0d)`,
331
+ );
332
+ this.button.style.setProperty(
333
+ "--active-text-color",
334
+ `var(${vars[0]}, #f7f7fa)`,
335
+ );
336
+ this.button.style.setProperty(
337
+ "--active-border-color",
338
+ `var(${vars[3]}, #0c0c0d)`,
339
+ );
240
340
  }
241
341
 
242
342
  _applyInteractionStyles(vars, styleType) {
@@ -248,22 +348,31 @@ class YumeButton extends HTMLElement {
248
348
  }
249
349
 
250
350
  _applySizeStyles(size) {
251
- const sizeVars = {
252
- small: "--component-button-padding-small",
253
- medium: "--component-button-padding-medium",
254
- large: "--component-button-padding-large",
255
- };
256
- const pad = sizeVars[size] || sizeVars.medium;
257
- this.button.style.setProperty("--button-padding", `var(${pad}, var(--component-button-padding-medium))`);
258
- this.button.style.setProperty("--button-gap", `var(${pad}, var(--component-button-padding-medium))`);
351
+ const s = ["small", "medium", "large"].includes(size) ? size : "medium";
352
+ const shared = `var(--component-button-padding-${s}, var(--component-button-padding-medium))`;
353
+ this.button.style.setProperty(
354
+ "--button-padding-block",
355
+ `var(--component-button-padding-block-${s}, ${shared})`,
356
+ );
357
+ this.button.style.setProperty(
358
+ "--button-padding-inline",
359
+ `var(--component-button-padding-inline-${s}, ${shared})`,
360
+ );
361
+ this.button.style.setProperty("--button-gap", shared);
259
362
 
260
363
  const minSizeMapping = {
261
364
  small: "var(--sizing-small, 32px)",
262
365
  medium: "var(--sizing-medium, 40px)",
263
366
  large: "var(--sizing-large, 56px)",
264
367
  };
265
- this.button.style.setProperty("--button-min-height", minSizeMapping[size] || "40px");
266
- this.button.style.setProperty("--button-min-width", minSizeMapping[size] || "40px");
368
+ this.button.style.setProperty(
369
+ "--button-min-height",
370
+ minSizeMapping[size] || "40px",
371
+ );
372
+ this.button.style.setProperty(
373
+ "--button-min-width",
374
+ minSizeMapping[size] || "40px",
375
+ );
267
376
  }
268
377
 
269
378
  _applyStyles() {
@@ -284,7 +393,7 @@ class YumeButton extends HTMLElement {
284
393
  width: 100%;
285
394
  min-height: var(--button-min-height, var(--sizing-medium, 40px));
286
395
  min-width: var(--button-min-width, var(--sizing-medium, 40px));
287
- padding: var(--button-padding, var(--component-button-padding-medium));
396
+ padding: var(--button-padding-block, var(--component-button-padding-medium)) var(--button-padding-inline, var(--component-button-padding-medium));
288
397
  gap: var(--button-gap, var(--component-button-padding-medium));
289
398
  justify-content: center;
290
399
  align-items: center;
@@ -305,6 +414,13 @@ class YumeButton extends HTMLElement {
305
414
  text-decoration: none;
306
415
  }
307
416
 
417
+ /* Square padding: the inline axis collapses to the block value, so
418
+ the button stays square instead of inheriting a wide label
419
+ padding. Driven by the padding-mode property (auto tracks icon-only). */
420
+ .button.square-padding {
421
+ padding-inline: var(--button-padding-block, var(--component-button-padding-medium));
422
+ }
423
+
308
424
  .button:disabled,
309
425
  .button[aria-disabled="true"] {
310
426
  opacity: 0.5;
@@ -349,12 +465,30 @@ class YumeButton extends HTMLElement {
349
465
  _applyUnfilledInteractionStyles(vars, styleType) {
350
466
  const borderColor = `var(${vars[0]}, #f7f7fa)`;
351
467
 
352
- this.button.style.setProperty("--hover-background-color", `var(${vars[4]}, #292a2b)`);
353
- this.button.style.setProperty("--hover-text-color", `var(${vars[0]}, #f7f7fa)`);
354
- this.button.style.setProperty("--focus-background-color", `var(${vars[5]}, #46474a)`);
355
- this.button.style.setProperty("--focus-text-color", `var(${vars[0]}, #f7f7fa)`);
356
- this.button.style.setProperty("--active-background-color", `var(${vars[0]}, #f7f7fa)`);
357
- this.button.style.setProperty("--active-text-color", `var(${vars[6]}, #0c0c0d)`);
468
+ this.button.style.setProperty(
469
+ "--hover-background-color",
470
+ `var(${vars[4]}, #292a2b)`,
471
+ );
472
+ this.button.style.setProperty(
473
+ "--hover-text-color",
474
+ `var(${vars[0]}, #f7f7fa)`,
475
+ );
476
+ this.button.style.setProperty(
477
+ "--focus-background-color",
478
+ `var(${vars[5]}, #46474a)`,
479
+ );
480
+ this.button.style.setProperty(
481
+ "--focus-text-color",
482
+ `var(${vars[0]}, #f7f7fa)`,
483
+ );
484
+ this.button.style.setProperty(
485
+ "--active-background-color",
486
+ `var(${vars[0]}, #f7f7fa)`,
487
+ );
488
+ this.button.style.setProperty(
489
+ "--active-text-color",
490
+ `var(${vars[6]}, #0c0c0d)`,
491
+ );
358
492
 
359
493
  if (styleType === "outlined") {
360
494
  // Outlined buttons keep their border color across all states
@@ -363,21 +497,86 @@ class YumeButton extends HTMLElement {
363
497
  this.button.style.setProperty("--active-border-color", borderColor);
364
498
  } else {
365
499
  // Flat buttons match border to background
366
- this.button.style.setProperty("--hover-border-color", `var(${vars[4]}, #292a2b)`);
367
- this.button.style.setProperty("--focus-border-color", `var(${vars[5]}, #46474a)`);
368
- this.button.style.setProperty("--active-border-color", `var(${vars[0]}, #f7f7fa)`);
500
+ this.button.style.setProperty(
501
+ "--hover-border-color",
502
+ `var(${vars[4]}, #292a2b)`,
503
+ );
504
+ this.button.style.setProperty(
505
+ "--focus-border-color",
506
+ `var(${vars[5]}, #46474a)`,
507
+ );
508
+ this.button.style.setProperty(
509
+ "--active-border-color",
510
+ `var(${vars[0]}, #f7f7fa)`,
511
+ );
369
512
  }
370
513
  }
371
514
 
372
515
  _getColorVarsMap() {
373
516
  return {
374
- primary: ["--primary-content--", "--primary-content-hover", "--primary-content-active", "--primary-background-component", "--primary-background-hover", "--primary-background-active", "--primary-content-inverse"],
375
- secondary: ["--secondary-content--", "--secondary-content-hover", "--secondary-content-active", "--secondary-background-component", "--secondary-background-hover", "--secondary-background-active", "--secondary-content-inverse"],
376
- base: ["--base-content--", "--base-content-lighter", "--base-content-lightest", "--base-background-component", "--base-background-hover", "--base-background-active", "--base-content-inverse"],
377
- success: ["--success-content--", "--success-content-hover", "--success-content-active", "--success-background-component", "--success-background-hover", "--success-background-active", "--success-content-inverse"],
378
- error: ["--error-content--", "--error-content-hover", "--error-content-active", "--error-background-component", "--error-background-hover", "--error-background-active", "--error-content-inverse"],
379
- warning: ["--warning-content--", "--warning-content-hover", "--warning-content-active", "--warning-background-component", "--warning-background-hover", "--warning-background-active", "--warning-content-inverse"],
380
- help: ["--help-content--", "--help-content-hover", "--help-content-active", "--help-background-component", "--help-background-hover", "--help-background-active", "--help-content-inverse"],
517
+ primary: [
518
+ "--primary-content--",
519
+ "--primary-content-hover",
520
+ "--primary-content-active",
521
+ "--primary-background-component",
522
+ "--primary-background-hover",
523
+ "--primary-background-active",
524
+ "--primary-content-inverse",
525
+ ],
526
+ secondary: [
527
+ "--secondary-content--",
528
+ "--secondary-content-hover",
529
+ "--secondary-content-active",
530
+ "--secondary-background-component",
531
+ "--secondary-background-hover",
532
+ "--secondary-background-active",
533
+ "--secondary-content-inverse",
534
+ ],
535
+ base: [
536
+ "--base-content--",
537
+ "--base-content-lighter",
538
+ "--base-content-lightest",
539
+ "--base-background-component",
540
+ "--base-background-hover",
541
+ "--base-background-active",
542
+ "--base-content-inverse",
543
+ ],
544
+ success: [
545
+ "--success-content--",
546
+ "--success-content-hover",
547
+ "--success-content-active",
548
+ "--success-background-component",
549
+ "--success-background-hover",
550
+ "--success-background-active",
551
+ "--success-content-inverse",
552
+ ],
553
+ error: [
554
+ "--error-content--",
555
+ "--error-content-hover",
556
+ "--error-content-active",
557
+ "--error-background-component",
558
+ "--error-background-hover",
559
+ "--error-background-active",
560
+ "--error-content-inverse",
561
+ ],
562
+ warning: [
563
+ "--warning-content--",
564
+ "--warning-content-hover",
565
+ "--warning-content-active",
566
+ "--warning-background-component",
567
+ "--warning-background-hover",
568
+ "--warning-background-active",
569
+ "--warning-content-inverse",
570
+ ],
571
+ help: [
572
+ "--help-content--",
573
+ "--help-content-hover",
574
+ "--help-content-active",
575
+ "--help-background-component",
576
+ "--help-background-hover",
577
+ "--help-background-active",
578
+ "--help-content-inverse",
579
+ ],
381
580
  };
382
581
  }
383
582
 
@@ -394,11 +593,13 @@ class YumeButton extends HTMLElement {
394
593
  detail[key] = attr.value;
395
594
  });
396
595
 
397
- this.dispatchEvent(new CustomEvent(eventType, {
398
- detail,
399
- bubbles: true,
400
- composed: true,
401
- }));
596
+ this.dispatchEvent(
597
+ new CustomEvent(eventType, {
598
+ detail,
599
+ bubbles: true,
600
+ composed: true,
601
+ }),
602
+ );
402
603
  }
403
604
 
404
605
  _init() {
@@ -408,6 +609,23 @@ class YumeButton extends HTMLElement {
408
609
  this._addEventListeners();
409
610
  }
410
611
 
612
+ /** Whether a slot (named, or default when slotName is "") has non-empty content. */
613
+ _hasSlotContent(slotName) {
614
+ const slot = slotName
615
+ ? this.shadowRoot.querySelector(`slot[name="${slotName}"]`)
616
+ : this.shadowRoot.querySelector("slot:not([name])");
617
+ if (!slot) return false;
618
+ return slot
619
+ .assignedNodes({ flatten: true })
620
+ .some(
621
+ (n) =>
622
+ !(
623
+ n.nodeType === Node.TEXT_NODE &&
624
+ n.textContent.trim() === ""
625
+ ),
626
+ );
627
+ }
628
+
411
629
  _manageSlotVisibility(slotName, selector) {
412
630
  const slot = slotName
413
631
  ? this.shadowRoot.querySelector(`slot[name="${slotName}"]`)
@@ -415,21 +633,38 @@ class YumeButton extends HTMLElement {
415
633
  const container = this.shadowRoot.querySelector(selector);
416
634
 
417
635
  const updateVisibility = () => {
418
- const hasContent = slot
419
- .assignedNodes({ flatten: true })
420
- .some((n) => !(n.nodeType === Node.TEXT_NODE && n.textContent.trim() === ""));
421
- container.style.display = hasContent ? "inline-flex" : "none";
636
+ container.style.display = this._hasSlotContent(slotName)
637
+ ? "inline-flex"
638
+ : "none";
639
+ this._refreshPaddingMode();
422
640
  };
423
641
 
424
642
  updateVisibility();
425
643
  slot.addEventListener("slotchange", updateVisibility);
426
644
  }
427
645
 
646
+ /** Toggle square padding (inline padding collapses to the block value). In
647
+ * "auto" this tracks icon-only buttons; "square"/"wide" force it on/off. */
648
+ _refreshPaddingMode() {
649
+ if (!this.button) return;
650
+ const mode = this.paddingMode;
651
+ let square = mode === "square";
652
+ if (mode === "auto") {
653
+ const hasIcon =
654
+ this._hasSlotContent("left-icon") ||
655
+ this._hasSlotContent("right-icon");
656
+ square = hasIcon && !this._hasSlotContent("");
657
+ }
658
+ this.button.classList.toggle("square-padding", square);
659
+ }
660
+
428
661
  _proxyNativeOnClick() {
429
662
  try {
430
663
  Object.defineProperty(this, "onclick", {
431
664
  get: () => this.button.onclick,
432
- set: (value) => { this.button.onclick = value; },
665
+ set: (value) => {
666
+ this.button.onclick = value;
667
+ },
433
668
  configurable: true,
434
669
  enumerable: true,
435
670
  });
@@ -498,7 +733,19 @@ class YumeButton extends HTMLElement {
498
733
  _updateButtonAttributes() {
499
734
  const isAnchor = this.button?.tagName === "A";
500
735
  // These are only meaningful on <button>
501
- const buttonOnlyAttrs = new Set(["type", "disabled", "name", "value", "autofocus", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget"]);
736
+ const buttonOnlyAttrs = new Set([
737
+ "type",
738
+ "disabled",
739
+ "name",
740
+ "value",
741
+ "autofocus",
742
+ "form",
743
+ "formaction",
744
+ "formenctype",
745
+ "formmethod",
746
+ "formnovalidate",
747
+ "formtarget",
748
+ ]);
502
749
  // These are only meaningful on <a>; href is managed separately in _render
503
750
  const anchorOnlyAttrs = new Set(["href", "target", "rel"]);
504
751