@ktbsh/ui 0.2.0 → 0.3.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.
Files changed (36) hide show
  1. package/dist/custom-elements.json +155 -0
  2. package/dist/react/preset-toggle.d.ts +18 -0
  3. package/dist/react/preset-toggle.js +30 -0
  4. package/dist/react/scroll-top.d.ts +20 -0
  5. package/dist/react/scroll-top.js +30 -0
  6. package/dist/react/theme-toggle.d.ts +18 -0
  7. package/dist/react/theme-toggle.js +30 -0
  8. package/dist/react/toggle-group.d.ts +17 -0
  9. package/dist/react/toggle-group.js +30 -0
  10. package/dist/vanilla/checkbox.js +53 -9
  11. package/dist/vanilla/checkbox.src.js +1 -1
  12. package/dist/vanilla/input.js +6 -0
  13. package/dist/vanilla/input.src.js +1 -1
  14. package/dist/vanilla/preset-toggle.d.ts +2 -0
  15. package/dist/vanilla/preset-toggle.js +72 -0
  16. package/dist/vanilla/preset-toggle.src.js +271 -0
  17. package/dist/vanilla/radio.js +44 -10
  18. package/dist/vanilla/radio.src.js +1 -1
  19. package/dist/vanilla/scroll-top.d.ts +2 -0
  20. package/dist/vanilla/scroll-top.js +69 -0
  21. package/dist/vanilla/scroll-top.src.js +251 -0
  22. package/dist/vanilla/select.js +42 -3
  23. package/dist/vanilla/select.src.js +1 -1
  24. package/dist/vanilla/skeleton.js +8 -3
  25. package/dist/vanilla/skeleton.src.js +1 -1
  26. package/dist/vanilla/switch.js +57 -19
  27. package/dist/vanilla/switch.src.js +1 -1
  28. package/dist/vanilla/textarea.js +6 -0
  29. package/dist/vanilla/textarea.src.js +1 -1
  30. package/dist/vanilla/theme-toggle.d.ts +2 -0
  31. package/dist/vanilla/theme-toggle.js +72 -0
  32. package/dist/vanilla/theme-toggle.src.js +242 -0
  33. package/dist/vanilla/toggle-group.d.ts +2 -0
  34. package/dist/vanilla/toggle-group.js +19 -0
  35. package/dist/vanilla/toggle-group.src.js +215 -0
  36. package/package.json +1 -1
@@ -0,0 +1,251 @@
1
+
2
+ import { render, html } from 'uhtml';
3
+
4
+ const sheet = new CSSStyleSheet();
5
+ sheet.replaceSync("\n\n :host {\n position: fixed;\n bottom: var(--kb-space-lg);\n right: var(--kb-space-md);\n z-index: 40;\n font-family: var(--kb-font-family-sans);\n /* Hidden unless visible — keeps focusable only when shown */\n display: none;\n }\n :host([visible]) {\n display: block;\n }\n button {\n box-sizing: border-box;\n min-height: 2.25rem;\n min-width: 2.25rem;\n padding: var(--kb-space-sm) var(--kb-space-md);\n border: 1px solid var(--kb-color-border-default);\n border-radius: var(--kb-radius-sm);\n background: var(--kb-color-bg-canvas);\n color: var(--kb-color-accent-default);\n font: inherit;\n font-size: var(--kb-font-size-sm);\n font-weight: var(--kb-font-weight-medium);\n letter-spacing: 0.25em;\n text-transform: uppercase;\n cursor: pointer;\n box-shadow: var(--kb-shadow-sm);\n transition:\n color 0.2s ease,\n background-color 0.2s ease,\n border-color 0.2s ease;\n }\n button:hover {\n color: var(--kb-color-fg-default);\n background: var(--kb-color-bg-surface);\n border-color: var(--kb-color-border-focus);\n }\n button:focus-visible {\n outline: none;\n box-shadow: var(--kb-focus-ring);\n }\n .caption {\n display: inline-block;\n }\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n @media (prefers-reduced-motion: reduce) {\n button {\n transition: none;\n }\n }\n ");
6
+
7
+ export class KitbashScrollTop extends HTMLElement {
8
+
9
+ static get observedAttributes() {
10
+ return ["visible","label","caption","behavior"];
11
+ }
12
+
13
+ static get propTypes() {
14
+ return {"visible":"Boolean","label":"String","caption":"String","behavior":"String"};
15
+ }
16
+
17
+
18
+ get visible() {
19
+ return this._props['visible'];
20
+ }
21
+ set visible(val) {
22
+ // External / framework property writes: re-render, do not emit kitbash-change
23
+ // (caller already knows the value they set).
24
+ this._assignProp('visible', val);
25
+ this.update();
26
+ }
27
+
28
+ get label() {
29
+ return this._props['label'];
30
+ }
31
+ set label(val) {
32
+ // External / framework property writes: re-render, do not emit kitbash-change
33
+ // (caller already knows the value they set).
34
+ this._assignProp('label', val);
35
+ this.update();
36
+ }
37
+
38
+ get caption() {
39
+ return this._props['caption'];
40
+ }
41
+ set caption(val) {
42
+ // External / framework property writes: re-render, do not emit kitbash-change
43
+ // (caller already knows the value they set).
44
+ this._assignProp('caption', val);
45
+ this.update();
46
+ }
47
+
48
+ get behavior() {
49
+ return this._props['behavior'];
50
+ }
51
+ set behavior(val) {
52
+ // External / framework property writes: re-render, do not emit kitbash-change
53
+ // (caller already knows the value they set).
54
+ this._assignProp('behavior', val);
55
+ this.update();
56
+ }
57
+
58
+
59
+ constructor() {
60
+ super();
61
+ this.attachShadow({ mode: 'open', delegatesFocus: true });
62
+
63
+ this.shadowRoot.adoptedStyleSheets = [sheet];
64
+
65
+ this._defaults = {"visible":false,"label":"Scroll to top","caption":"$ top","behavior":"smooth"};
66
+
67
+ this._state = {};
68
+ this._props = { ...this._defaults };
69
+ this._eventHandlers = { 'click button': function(_e, { props }) {
70
+ if (typeof window > "u")
71
+ return;
72
+ const behavior = props.behavior === "auto" || props.behavior === "instant" ? "auto" : "smooth", reduce = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
73
+ window.scrollTo({
74
+ top: 0,
75
+ behavior: reduce ? "auto" : behavior
76
+ });
77
+ } };
78
+ this._eventCleanup = [];
79
+ this._renderFn = function({ props, html }) {
80
+ const label = typeof props.label === "string" && props.label.length > 0 ? props.label : "Scroll to top", caption = typeof props.caption === "string" && props.caption.length > 0 ? props.caption : "$ top";
81
+ return html`
82
+ <button part="scroll-top-root" type="button" aria-label=${label}>
83
+ <span class="sr-only">${label}</span>
84
+ <span class="caption" aria-hidden="true">${caption}</span>
85
+ </button>
86
+ `;
87
+ };
88
+ this._reflecting = false;
89
+ }
90
+
91
+ connectedCallback() {
92
+ // Initial attribute values are parsed by attributeChangedCallback before connectedCallback fires
93
+ this.update();
94
+
95
+ }
96
+
97
+ disconnectedCallback() {
98
+ this.cleanupEvents();
99
+ }
100
+
101
+ attributeChangedCallback(name, oldValue, newValue) {
102
+ // Skip when we caused the attribute write ourselves (avoids double update)
103
+ if (this._reflecting) return;
104
+ if (oldValue === newValue) return;
105
+
106
+ const type = this.constructor.propTypes[name];
107
+
108
+ let parsedValue = newValue;
109
+ if (newValue === null || newValue === undefined) {
110
+ parsedValue = this._defaults[name];
111
+ } else if (type === 'Number' && newValue !== '') {
112
+ // Match property coercion: empty string is not Number('') → 0
113
+ parsedValue = Number(newValue);
114
+ } else if (type === 'Boolean') {
115
+ parsedValue = newValue !== null && newValue !== 'false';
116
+ }
117
+
118
+ this._props[name] = parsedValue;
119
+
120
+ this.update();
121
+ }
122
+
123
+ /** Coerce + reflect a single prop; no re-render (caller decides). */
124
+ _assignProp(key, val) {
125
+ const type = this.constructor.propTypes[key];
126
+ let next = val;
127
+ if (type === 'Boolean') {
128
+ next = val === true || val === '';
129
+ } else if (type === 'Number' && val !== null && val !== undefined && val !== '') {
130
+ next = Number(val);
131
+ }
132
+ this._props[key] = next;
133
+
134
+ // Only reflect primitives — objects/arrays stay as properties (no [object Object] attrs)
135
+ const isPrimitive =
136
+ next === null ||
137
+ next === undefined ||
138
+ typeof next === 'string' ||
139
+ typeof next === 'number' ||
140
+ typeof next === 'boolean';
141
+
142
+ this._reflecting = true;
143
+ try {
144
+ if (type === 'Boolean') {
145
+ if (next) this.setAttribute(key, '');
146
+ else this.removeAttribute(key);
147
+ } else if (!isPrimitive) {
148
+ // property-only; drop stale attribute if any
149
+ this.removeAttribute(key);
150
+ } else if (next === null || next === undefined) {
151
+ this.removeAttribute(key);
152
+ } else {
153
+ this.setAttribute(key, String(next));
154
+ }
155
+ } finally {
156
+ this._reflecting = false;
157
+ }
158
+
159
+ }
160
+
161
+ _emitChange() {
162
+ // Shallow-copy both bags so listeners cannot mutate internal state/props via event.detail
163
+ this.dispatchEvent(new CustomEvent('kitbash-change', {
164
+ detail: {
165
+ state: { ...this._state },
166
+ props: { ...this._props },
167
+ },
168
+ bubbles: true,
169
+ composed: true
170
+ }));
171
+ }
172
+
173
+ /**
174
+ * Batch props and/or state: one uhtml update, one kitbash-change.
175
+ * Prefer this in event handlers (e.g. controlled inputs).
176
+ */
177
+ commit(patch) {
178
+ const p = patch || {};
179
+ if (p.props) {
180
+ const allowed = this.constructor.propTypes || {};
181
+ for (const key of Object.keys(p.props)) {
182
+ // Only declared props — ignore typos / stray keys
183
+ if (!Object.prototype.hasOwnProperty.call(allowed, key)) continue;
184
+ this._assignProp(key, p.props[key]);
185
+ }
186
+ }
187
+ if (p.state) {
188
+ this._state = { ...this._state, ...p.state };
189
+ }
190
+ this.update();
191
+ this._emitChange();
192
+ }
193
+
194
+ setState(newState) {
195
+ this.commit({ state: newState });
196
+ }
197
+
198
+ setProps(nextProps) {
199
+ this.commit({ props: nextProps });
200
+ }
201
+
202
+ _handlerCtx() {
203
+ // Snapshots so handlers/render cannot mutate internal bags by accident
204
+ return {
205
+ props: { ...this._props },
206
+ state: { ...this._state },
207
+ setState: this.setState.bind(this),
208
+ setProps: this.setProps.bind(this),
209
+ commit: this.commit.bind(this),
210
+ };
211
+ }
212
+
213
+ cleanupEvents() {
214
+ this._eventCleanup.forEach(cleanup => cleanup());
215
+ this._eventCleanup = [];
216
+ }
217
+
218
+ update() {
219
+ // Run the uhtml DOM diffing render cycle
220
+ render(this.shadowRoot, this._renderFn({
221
+ ...this._handlerCtx(),
222
+ html
223
+ }));
224
+ this.cleanupEvents();
225
+ this.bindEvents();
226
+
227
+ }
228
+
229
+
230
+
231
+ bindEvents() {
232
+ Object.keys(this._eventHandlers).forEach(eventSelector => {
233
+ const parts = eventSelector.split(' ');
234
+ const eventName = parts[0];
235
+ const selector = parts.slice(1).join(' ');
236
+
237
+ const targets = selector ? this.shadowRoot.querySelectorAll(selector) : [this];
238
+ targets.forEach(target => {
239
+ const handler = (e) => {
240
+ this._eventHandlers[eventSelector](e, this._handlerCtx());
241
+ };
242
+ target.addEventListener(eventName, handler);
243
+ this._eventCleanup.push(() => target.removeEventListener(eventName, handler));
244
+ });
245
+ });
246
+ }
247
+ }
248
+
249
+ if (!customElements.get('kitbash-scroll-top')) {
250
+ customElements.define('kitbash-scroll-top', KitbashScrollTop);
251
+ }
@@ -6,24 +6,58 @@ var d=(_,U,B,Y,z)=>{let X=B.length,J=U.length,I=X,O=0,$=0,Q=null;while(O<J||$<I)
6
6
  font-family: var(--kb-font-family-sans);
7
7
  }
8
8
  select {
9
+ appearance: none;
10
+ -webkit-appearance: none;
9
11
  box-sizing: border-box;
10
12
  width: 100%;
11
13
  min-height: 2.25rem;
12
- padding: var(--kb-space-sm) var(--kb-space-md);
14
+ padding: var(--kb-space-sm) 2.5rem var(--kb-space-sm) var(--kb-space-md);
13
15
  border: 1px solid var(--kb-color-border-default);
14
16
  border-radius: var(--kb-radius-sm);
15
- background: var(--kb-color-bg-canvas);
17
+ background-color: var(--kb-color-bg-canvas);
18
+ /* Custom chevron (token-colored); listbox chrome is still browser-native */
19
+ background-image:
20
+ linear-gradient(
21
+ 45deg,
22
+ transparent 50%,
23
+ var(--kb-color-fg-muted) 50%
24
+ ),
25
+ linear-gradient(
26
+ 135deg,
27
+ var(--kb-color-fg-muted) 50%,
28
+ transparent 50%
29
+ ),
30
+ linear-gradient(
31
+ to right,
32
+ transparent,
33
+ transparent
34
+ );
35
+ background-position:
36
+ calc(100% - 1.15rem) calc(50% - 0.1rem),
37
+ calc(100% - 0.8rem) calc(50% - 0.1rem),
38
+ 100% 0;
39
+ background-size:
40
+ 0.35rem 0.35rem,
41
+ 0.35rem 0.35rem,
42
+ 2.25rem 100%;
43
+ background-repeat: no-repeat;
16
44
  color: var(--kb-color-fg-default);
17
45
  font-size: var(--kb-font-size-md);
18
46
  font-family: inherit;
19
47
  line-height: var(--kb-line-height-normal);
20
48
  cursor: pointer;
21
49
  outline: none;
50
+ transition:
51
+ border-color 0.12s ease,
52
+ box-shadow 0.12s ease;
53
+ }
54
+ select:hover:not(:disabled) {
55
+ border-color: var(--kb-color-border-focus);
22
56
  }
23
57
  :host([size="sm"]) select {
24
58
  min-height: 2rem;
25
59
  font-size: var(--kb-font-size-sm);
26
- padding: var(--kb-space-xs) var(--kb-space-sm);
60
+ padding: var(--kb-space-xs) 2.25rem var(--kb-space-xs) var(--kb-space-sm);
27
61
  }
28
62
  :host([size="lg"]) select {
29
63
  min-height: 2.75rem;
@@ -40,6 +74,11 @@ var d=(_,U,B,Y,z)=>{let X=B.length,J=U.length,I=X,O=0,$=0,Q=null;while(O<J||$<I)
40
74
  select[aria-invalid="true"] {
41
75
  border-color: var(--kb-color-danger-default);
42
76
  }
77
+ option,
78
+ optgroup {
79
+ background: var(--kb-color-bg-canvas);
80
+ color: var(--kb-color-fg-default);
81
+ }
43
82
  /* Light-DOM options are cloned into <select>; keep the source slot out of layout */
44
83
  slot {
45
84
  display: none;
@@ -2,7 +2,7 @@
2
2
  import { render, html } from 'uhtml';
3
3
 
4
4
  const sheet = new CSSStyleSheet();
5
- sheet.replaceSync("\n\n :host {\n display: inline-block;\n width: 100%;\n font-family: var(--kb-font-family-sans);\n }\n select {\n box-sizing: border-box;\n width: 100%;\n min-height: 2.25rem;\n padding: var(--kb-space-sm) var(--kb-space-md);\n border: 1px solid var(--kb-color-border-default);\n border-radius: var(--kb-radius-sm);\n background: var(--kb-color-bg-canvas);\n color: var(--kb-color-fg-default);\n font-size: var(--kb-font-size-md);\n font-family: inherit;\n line-height: var(--kb-line-height-normal);\n cursor: pointer;\n outline: none;\n }\n :host([size=\"sm\"]) select {\n min-height: 2rem;\n font-size: var(--kb-font-size-sm);\n padding: var(--kb-space-xs) var(--kb-space-sm);\n }\n :host([size=\"lg\"]) select {\n min-height: 2.75rem;\n font-size: var(--kb-font-size-lg);\n }\n select:focus {\n border-color: var(--kb-color-border-focus);\n box-shadow: var(--kb-focus-ring);\n }\n select:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n }\n select[aria-invalid=\"true\"] {\n border-color: var(--kb-color-danger-default);\n }\n /* Light-DOM options are cloned into <select>; keep the source slot out of layout */\n slot {\n display: none;\n }\n ");
5
+ sheet.replaceSync("\n\n :host {\n display: inline-block;\n width: 100%;\n font-family: var(--kb-font-family-sans);\n }\n select {\n appearance: none;\n -webkit-appearance: none;\n box-sizing: border-box;\n width: 100%;\n min-height: 2.25rem;\n padding: var(--kb-space-sm) 2.5rem var(--kb-space-sm) var(--kb-space-md);\n border: 1px solid var(--kb-color-border-default);\n border-radius: var(--kb-radius-sm);\n background-color: var(--kb-color-bg-canvas);\n /* Custom chevron (token-colored); listbox chrome is still browser-native */\n background-image:\n linear-gradient(\n 45deg,\n transparent 50%,\n var(--kb-color-fg-muted) 50%\n ),\n linear-gradient(\n 135deg,\n var(--kb-color-fg-muted) 50%,\n transparent 50%\n ),\n linear-gradient(\n to right,\n transparent,\n transparent\n );\n background-position:\n calc(100% - 1.15rem) calc(50% - 0.1rem),\n calc(100% - 0.8rem) calc(50% - 0.1rem),\n 100% 0;\n background-size:\n 0.35rem 0.35rem,\n 0.35rem 0.35rem,\n 2.25rem 100%;\n background-repeat: no-repeat;\n color: var(--kb-color-fg-default);\n font-size: var(--kb-font-size-md);\n font-family: inherit;\n line-height: var(--kb-line-height-normal);\n cursor: pointer;\n outline: none;\n transition:\n border-color 0.12s ease,\n box-shadow 0.12s ease;\n }\n select:hover:not(:disabled) {\n border-color: var(--kb-color-border-focus);\n }\n :host([size=\"sm\"]) select {\n min-height: 2rem;\n font-size: var(--kb-font-size-sm);\n padding: var(--kb-space-xs) 2.25rem var(--kb-space-xs) var(--kb-space-sm);\n }\n :host([size=\"lg\"]) select {\n min-height: 2.75rem;\n font-size: var(--kb-font-size-lg);\n }\n select:focus {\n border-color: var(--kb-color-border-focus);\n box-shadow: var(--kb-focus-ring);\n }\n select:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n }\n select[aria-invalid=\"true\"] {\n border-color: var(--kb-color-danger-default);\n }\n option,\n optgroup {\n background: var(--kb-color-bg-canvas);\n color: var(--kb-color-fg-default);\n }\n /* Light-DOM options are cloned into <select>; keep the source slot out of layout */\n slot {\n display: none;\n }\n ");
6
6
 
7
7
  export class KitbashSelect extends HTMLElement {
8
8
  static formAssociated = true;
@@ -6,14 +6,18 @@ var d=(_,U,B,Y,z)=>{let O=B.length,J=U.length,I=O,Q=0,$=0,X=null;while(Q<J||$<I)
6
6
  .root {
7
7
  display: block;
8
8
  box-sizing: border-box;
9
+ /* Use accent-subtle + border for contrast in dark / terminal (surface≈subtle fails) */
9
10
  background: linear-gradient(
10
11
  90deg,
11
12
  var(--kb-color-bg-subtle) 0%,
12
- var(--kb-color-bg-surface) 50%,
13
+ var(--kb-color-accent-subtle) 40%,
14
+ var(--kb-color-border-muted) 50%,
15
+ var(--kb-color-accent-subtle) 60%,
13
16
  var(--kb-color-bg-subtle) 100%
14
17
  );
15
- background-size: 200% 100%;
16
- animation: kitbash-skeleton 1.2s ease-in-out infinite;
18
+ background-size: 220% 100%;
19
+ border: 1px solid var(--kb-color-border-muted);
20
+ animation: kitbash-skeleton 1.35s ease-in-out infinite;
17
21
  }
18
22
  .text {
19
23
  height: 0.875em;
@@ -40,6 +44,7 @@ var d=(_,U,B,Y,z)=>{let O=B.length,J=U.length,I=O,Q=0,$=0,X=null;while(Q<J||$<I)
40
44
  .root {
41
45
  animation: none;
42
46
  background: var(--kb-color-bg-subtle);
47
+ border-color: var(--kb-color-border-default);
43
48
  }
44
49
  }
45
50
  `);class L1 extends HTMLElement{static get observedAttributes(){return["variant","width","height"]}static get propTypes(){return{variant:"String",width:"String",height:"String"}}get variant(){return this._props.variant}set variant(_){this._assignProp("variant",_),this.update()}get width(){return this._props.width}set width(_){this._assignProp("width",_),this.update()}get height(){return this._props.height}set height(_){this._assignProp("height",_),this.update()}constructor(){super();this.attachShadow({mode:"open",delegatesFocus:!1}),this.shadowRoot.adoptedStyleSheets=[G1],this._defaults={variant:"text",width:"100%",height:""},this._state={},this._props={...this._defaults},this._eventHandlers={},this._eventCleanup=[],this._renderFn=function({props:_,html:U}){let B=_.variant==="rect"||_.variant==="circle"?_.variant:"text",Y=typeof _.width==="string"&&_.width.length>0?_.width:B==="circle"?"":"100%",z=typeof _.height==="string"&&_.height.length>0?_.height:"",O=[];if(Y)O.push(`width: ${Y}`);if(z)O.push(`height: ${z}`);let J=O.length>0?O.join("; "):null;return U`
@@ -2,7 +2,7 @@
2
2
  import { render, html } from 'uhtml';
3
3
 
4
4
  const sheet = new CSSStyleSheet();
5
- sheet.replaceSync("\n\n :host {\n display: block;\n }\n .root {\n display: block;\n box-sizing: border-box;\n background: linear-gradient(\n 90deg,\n var(--kb-color-bg-subtle) 0%,\n var(--kb-color-bg-surface) 50%,\n var(--kb-color-bg-subtle) 100%\n );\n background-size: 200% 100%;\n animation: kitbash-skeleton 1.2s ease-in-out infinite;\n }\n .text {\n height: 0.875em;\n border-radius: var(--kb-radius-sm);\n }\n .rect {\n height: 6rem;\n border-radius: var(--kb-radius-md);\n }\n .circle {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: var(--kb-radius-full);\n }\n @keyframes kitbash-skeleton {\n 0% {\n background-position: 100% 0;\n }\n 100% {\n background-position: -100% 0;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .root {\n animation: none;\n background: var(--kb-color-bg-subtle);\n }\n }\n ");
5
+ sheet.replaceSync("\n\n :host {\n display: block;\n }\n .root {\n display: block;\n box-sizing: border-box;\n /* Use accent-subtle + border for contrast in dark / terminal (surface≈subtle fails) */\n background: linear-gradient(\n 90deg,\n var(--kb-color-bg-subtle) 0%,\n var(--kb-color-accent-subtle) 40%,\n var(--kb-color-border-muted) 50%,\n var(--kb-color-accent-subtle) 60%,\n var(--kb-color-bg-subtle) 100%\n );\n background-size: 220% 100%;\n border: 1px solid var(--kb-color-border-muted);\n animation: kitbash-skeleton 1.35s ease-in-out infinite;\n }\n .text {\n height: 0.875em;\n border-radius: var(--kb-radius-sm);\n }\n .rect {\n height: 6rem;\n border-radius: var(--kb-radius-md);\n }\n .circle {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: var(--kb-radius-full);\n }\n @keyframes kitbash-skeleton {\n 0% {\n background-position: 100% 0;\n }\n 100% {\n background-position: -100% 0;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .root {\n animation: none;\n background: var(--kb-color-bg-subtle);\n border-color: var(--kb-color-border-default);\n }\n }\n ");
6
6
 
7
7
  export class KitbashSkeleton extends HTMLElement {
8
8
 
@@ -6,15 +6,12 @@ var u=(_,U,B,Y,I)=>{let W=B.length,J=U.length,z=W,O=0,$=0,X=null;while(O<J||$<z)
6
6
  font-family: var(--kb-font-family-sans);
7
7
  font-size: var(--kb-font-size-md);
8
8
  color: var(--kb-color-fg-default);
9
- --kitbash-switch-w: 2.75rem;
10
- --kitbash-switch-h: 1.5rem;
11
- --kitbash-switch-pad: 2px;
9
+ --kitbash-switch-w: 2.85rem;
10
+ --kitbash-switch-h: 1.45rem;
11
+ --kitbash-switch-pad: 3px;
12
12
  --kitbash-switch-thumb: calc(
13
13
  var(--kitbash-switch-h) - 2 * var(--kitbash-switch-pad)
14
14
  );
15
- --kitbash-switch-track: var(--kb-color-bg-subtle);
16
- --kitbash-switch-track-on: var(--kb-color-accent-default);
17
- --kitbash-switch-knob: #ffffff;
18
15
  }
19
16
  .label-wrap {
20
17
  display: inline-flex;
@@ -48,11 +45,39 @@ var u=(_,U,B,Y,I)=>{let W=B.length,J=U.length,z=W,O=0,$=0,X=null;while(O<J||$<z)
48
45
  inset: 0;
49
46
  z-index: 0;
50
47
  box-sizing: border-box;
51
- border-radius: 999px;
52
- background: var(--kitbash-switch-track);
53
- border: 1px solid var(--kb-color-border-default);
54
- transition: background 0.15s ease, border-color 0.15s ease;
48
+ border-radius: var(--kb-radius-sm);
49
+ background: var(--kb-color-bg-canvas);
50
+ border: 2px solid var(--kb-color-border-default);
51
+ transition:
52
+ background 0.15s ease,
53
+ border-color 0.15s ease;
54
+ pointer-events: none;
55
+ overflow: hidden;
56
+ }
57
+ /* Terminal-ish status glyphs in the gutter */
58
+ .track::before,
59
+ .track::after {
60
+ position: absolute;
61
+ top: 50%;
62
+ transform: translateY(-50%);
63
+ font-size: 0.55rem;
64
+ font-weight: var(--kb-font-weight-semibold);
65
+ letter-spacing: 0.04em;
66
+ line-height: 1;
55
67
  pointer-events: none;
68
+ opacity: 0.55;
69
+ }
70
+ .track::before {
71
+ content: '1';
72
+ left: 0.35rem;
73
+ color: var(--kb-color-fg-on-accent);
74
+ opacity: 0;
75
+ }
76
+ .track::after {
77
+ content: '0';
78
+ right: 0.35rem;
79
+ color: var(--kb-color-fg-muted);
80
+ opacity: 0.7;
56
81
  }
57
82
  .thumb {
58
83
  position: absolute;
@@ -61,31 +86,44 @@ var u=(_,U,B,Y,I)=>{let W=B.length,J=U.length,z=W,O=0,$=0,X=null;while(O<J||$<z)
61
86
  left: var(--kitbash-switch-pad);
62
87
  width: var(--kitbash-switch-thumb);
63
88
  height: var(--kitbash-switch-thumb);
64
- border-radius: 999px;
65
- background: var(--kitbash-switch-knob);
66
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
67
- transition: transform 0.15s ease;
89
+ border-radius: calc(var(--kb-radius-sm) - 1px);
90
+ background: var(--kb-color-fg-muted);
91
+ border: 1px solid var(--kb-color-border-default);
92
+ box-shadow: var(--kb-shadow-sm);
93
+ transition:
94
+ transform 0.15s ease,
95
+ background 0.15s ease,
96
+ border-color 0.15s ease;
68
97
  pointer-events: none;
69
98
  }
70
99
  input:checked + .track {
71
- background: var(--kitbash-switch-track-on);
72
- border-color: var(--kitbash-switch-track-on);
100
+ background: var(--kb-color-accent-default);
101
+ border-color: var(--kb-color-accent-default);
102
+ }
103
+ input:checked + .track::before {
104
+ opacity: 0.9;
105
+ }
106
+ input:checked + .track::after {
107
+ opacity: 0;
73
108
  }
74
109
  input:checked + .track .thumb {
75
110
  transform: translateX(
76
111
  calc(
77
112
  var(--kitbash-switch-w) - var(--kitbash-switch-thumb) - 2 *
78
- var(--kitbash-switch-pad)
113
+ var(--kitbash-switch-pad) - 4px
79
114
  )
80
115
  );
116
+ background: var(--kb-color-fg-on-accent);
117
+ border-color: transparent;
118
+ box-shadow: none;
81
119
  }
82
120
  input:focus-visible + .track {
83
121
  box-shadow: var(--kb-focus-ring);
84
122
  }
85
123
  input:disabled + .track {
86
- opacity: 0.55;
124
+ opacity: 0.5;
87
125
  }
88
- input[aria-invalid="true"] + .track {
126
+ input[aria-invalid='true'] + .track {
89
127
  border-color: var(--kb-color-danger-default);
90
128
  }
91
129
  .label {
@@ -2,7 +2,7 @@
2
2
  import { render, html } from 'uhtml';
3
3
 
4
4
  const sheet = new CSSStyleSheet();
5
- sheet.replaceSync("\n\n :host {\n display: inline-flex;\n vertical-align: middle;\n font-family: var(--kb-font-family-sans);\n font-size: var(--kb-font-size-md);\n color: var(--kb-color-fg-default);\n --kitbash-switch-w: 2.75rem;\n --kitbash-switch-h: 1.5rem;\n --kitbash-switch-pad: 2px;\n --kitbash-switch-thumb: calc(\n var(--kitbash-switch-h) - 2 * var(--kitbash-switch-pad)\n );\n --kitbash-switch-track: var(--kb-color-bg-subtle);\n --kitbash-switch-track-on: var(--kb-color-accent-default);\n --kitbash-switch-knob: #ffffff;\n }\n .label-wrap {\n display: inline-flex;\n align-items: center;\n gap: var(--kb-space-sm);\n cursor: pointer;\n user-select: none;\n }\n .switch {\n position: relative;\n display: inline-block;\n width: var(--kitbash-switch-w);\n height: var(--kitbash-switch-h);\n flex-shrink: 0;\n }\n input {\n position: absolute;\n inset: 0;\n z-index: 2;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n }\n input:disabled {\n cursor: not-allowed;\n }\n .track {\n position: absolute;\n inset: 0;\n z-index: 0;\n box-sizing: border-box;\n border-radius: 999px;\n background: var(--kitbash-switch-track);\n border: 1px solid var(--kb-color-border-default);\n transition: background 0.15s ease, border-color 0.15s ease;\n pointer-events: none;\n }\n .thumb {\n position: absolute;\n z-index: 1;\n top: var(--kitbash-switch-pad);\n left: var(--kitbash-switch-pad);\n width: var(--kitbash-switch-thumb);\n height: var(--kitbash-switch-thumb);\n border-radius: 999px;\n background: var(--kitbash-switch-knob);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n transition: transform 0.15s ease;\n pointer-events: none;\n }\n input:checked + .track {\n background: var(--kitbash-switch-track-on);\n border-color: var(--kitbash-switch-track-on);\n }\n input:checked + .track .thumb {\n transform: translateX(\n calc(\n var(--kitbash-switch-w) - var(--kitbash-switch-thumb) - 2 *\n var(--kitbash-switch-pad)\n )\n );\n }\n input:focus-visible + .track {\n box-shadow: var(--kb-focus-ring);\n }\n input:disabled + .track {\n opacity: 0.55;\n }\n input[aria-invalid=\"true\"] + .track {\n border-color: var(--kb-color-danger-default);\n }\n .label {\n line-height: var(--kb-line-height-normal);\n }\n @media (prefers-reduced-motion: reduce) {\n .track,\n .thumb {\n transition: none;\n }\n }\n ");
5
+ sheet.replaceSync("\n\n :host {\n display: inline-flex;\n vertical-align: middle;\n font-family: var(--kb-font-family-sans);\n font-size: var(--kb-font-size-md);\n color: var(--kb-color-fg-default);\n --kitbash-switch-w: 2.85rem;\n --kitbash-switch-h: 1.45rem;\n --kitbash-switch-pad: 3px;\n --kitbash-switch-thumb: calc(\n var(--kitbash-switch-h) - 2 * var(--kitbash-switch-pad)\n );\n }\n .label-wrap {\n display: inline-flex;\n align-items: center;\n gap: var(--kb-space-sm);\n cursor: pointer;\n user-select: none;\n }\n .switch {\n position: relative;\n display: inline-block;\n width: var(--kitbash-switch-w);\n height: var(--kitbash-switch-h);\n flex-shrink: 0;\n }\n input {\n position: absolute;\n inset: 0;\n z-index: 2;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n }\n input:disabled {\n cursor: not-allowed;\n }\n .track {\n position: absolute;\n inset: 0;\n z-index: 0;\n box-sizing: border-box;\n border-radius: var(--kb-radius-sm);\n background: var(--kb-color-bg-canvas);\n border: 2px solid var(--kb-color-border-default);\n transition:\n background 0.15s ease,\n border-color 0.15s ease;\n pointer-events: none;\n overflow: hidden;\n }\n /* Terminal-ish status glyphs in the gutter */\n .track::before,\n .track::after {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n font-size: 0.55rem;\n font-weight: var(--kb-font-weight-semibold);\n letter-spacing: 0.04em;\n line-height: 1;\n pointer-events: none;\n opacity: 0.55;\n }\n .track::before {\n content: '1';\n left: 0.35rem;\n color: var(--kb-color-fg-on-accent);\n opacity: 0;\n }\n .track::after {\n content: '0';\n right: 0.35rem;\n color: var(--kb-color-fg-muted);\n opacity: 0.7;\n }\n .thumb {\n position: absolute;\n z-index: 1;\n top: var(--kitbash-switch-pad);\n left: var(--kitbash-switch-pad);\n width: var(--kitbash-switch-thumb);\n height: var(--kitbash-switch-thumb);\n border-radius: calc(var(--kb-radius-sm) - 1px);\n background: var(--kb-color-fg-muted);\n border: 1px solid var(--kb-color-border-default);\n box-shadow: var(--kb-shadow-sm);\n transition:\n transform 0.15s ease,\n background 0.15s ease,\n border-color 0.15s ease;\n pointer-events: none;\n }\n input:checked + .track {\n background: var(--kb-color-accent-default);\n border-color: var(--kb-color-accent-default);\n }\n input:checked + .track::before {\n opacity: 0.9;\n }\n input:checked + .track::after {\n opacity: 0;\n }\n input:checked + .track .thumb {\n transform: translateX(\n calc(\n var(--kitbash-switch-w) - var(--kitbash-switch-thumb) - 2 *\n var(--kitbash-switch-pad) - 4px\n )\n );\n background: var(--kb-color-fg-on-accent);\n border-color: transparent;\n box-shadow: none;\n }\n input:focus-visible + .track {\n box-shadow: var(--kb-focus-ring);\n }\n input:disabled + .track {\n opacity: 0.5;\n }\n input[aria-invalid='true'] + .track {\n border-color: var(--kb-color-danger-default);\n }\n .label {\n line-height: var(--kb-line-height-normal);\n }\n @media (prefers-reduced-motion: reduce) {\n .track,\n .thumb {\n transition: none;\n }\n }\n ");
6
6
 
7
7
  export class KitbashSwitch extends HTMLElement {
8
8
  static formAssociated = true;
@@ -19,6 +19,12 @@ var p=(_,U,B,Y,z)=>{let X=B.length,O=U.length,J=X,Q=0,I=0,$=null;while(Q<O||I<J)
19
19
  line-height: var(--kb-line-height-normal);
20
20
  resize: vertical;
21
21
  outline: none;
22
+ transition:
23
+ border-color 0.12s ease,
24
+ box-shadow 0.12s ease;
25
+ }
26
+ textarea:hover:not(:disabled):not(:read-only) {
27
+ border-color: var(--kb-color-border-focus);
22
28
  }
23
29
  textarea::placeholder {
24
30
  color: var(--kb-color-fg-subtle);
@@ -2,7 +2,7 @@
2
2
  import { render, html } from 'uhtml';
3
3
 
4
4
  const sheet = new CSSStyleSheet();
5
- sheet.replaceSync("\n\n :host {\n display: block;\n width: 100%;\n font-family: var(--kb-font-family-sans);\n }\n textarea {\n box-sizing: border-box;\n width: 100%;\n min-height: 5rem;\n padding: var(--kb-space-sm) var(--kb-space-md);\n border: 1px solid var(--kb-color-border-default);\n border-radius: var(--kb-radius-sm);\n background: var(--kb-color-bg-canvas);\n color: var(--kb-color-fg-default);\n font-size: var(--kb-font-size-md);\n font-family: inherit;\n line-height: var(--kb-line-height-normal);\n resize: vertical;\n outline: none;\n }\n textarea::placeholder {\n color: var(--kb-color-fg-subtle);\n }\n textarea:focus {\n border-color: var(--kb-color-border-focus);\n box-shadow: var(--kb-focus-ring);\n }\n textarea:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n resize: none;\n }\n textarea[aria-invalid=\"true\"] {\n border-color: var(--kb-color-danger-default);\n }\n ");
5
+ sheet.replaceSync("\n\n :host {\n display: block;\n width: 100%;\n font-family: var(--kb-font-family-sans);\n }\n textarea {\n box-sizing: border-box;\n width: 100%;\n min-height: 5rem;\n padding: var(--kb-space-sm) var(--kb-space-md);\n border: 1px solid var(--kb-color-border-default);\n border-radius: var(--kb-radius-sm);\n background: var(--kb-color-bg-canvas);\n color: var(--kb-color-fg-default);\n font-size: var(--kb-font-size-md);\n font-family: inherit;\n line-height: var(--kb-line-height-normal);\n resize: vertical;\n outline: none;\n transition:\n border-color 0.12s ease,\n box-shadow 0.12s ease;\n }\n textarea:hover:not(:disabled):not(:read-only) {\n border-color: var(--kb-color-border-focus);\n }\n textarea::placeholder {\n color: var(--kb-color-fg-subtle);\n }\n textarea:focus {\n border-color: var(--kb-color-border-focus);\n box-shadow: var(--kb-focus-ring);\n }\n textarea:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n resize: none;\n }\n textarea[aria-invalid=\"true\"] {\n border-color: var(--kb-color-danger-default);\n }\n ");
6
6
 
7
7
  export class KitbashTextarea extends HTMLElement {
8
8
  static formAssociated = true;
@@ -0,0 +1,2 @@
1
+ /** Side-effect import — registers the custom element. */
2
+ export {};
@@ -0,0 +1,72 @@
1
+ var d=(_,U,B,Y,z)=>{let W=B.length,J=U.length,I=W,O=0,$=0,X=null;while(O<J||$<I)if(J===O){let Q=I<W?$?Y(B[$-1],-0).nextSibling:Y(B[I],0):z;while($<I)_.insertBefore(Y(B[$++],1),Q)}else if(I===$)while(O<J){if(!X||!X.has(U[O]))_.removeChild(Y(U[O],-1));O++}else if(U[O]===B[$])O++,$++;else if(U[J-1]===B[I-1])J--,I--;else if(U[O]===B[I-1]&&B[$]===U[J-1]){let Q=Y(U[--J],-0).nextSibling;_.insertBefore(Y(B[$++],1),Y(U[O++],-0).nextSibling),_.insertBefore(Y(B[--I],1),Q),U[J]=B[I]}else{if(!X){X=new Map;let Q=$;while(Q<I)X.set(B[Q],Q++)}if(X.has(U[O])){let Q=X.get(U[O]);if($<Q&&Q<I){let Z=O,K=1;while(++Z<J&&Z<I&&X.get(U[Z])===Q+K)K++;if(K>Q-$){let M=Y(U[O],0);while($<Q)_.insertBefore(Y(B[$++],1),M)}else _.replaceChild(Y(B[$++],1),Y(U[O++],-1))}else O++}else _.removeChild(Y(U[O++],-1))}return B};var{isArray:E}=Array,{getPrototypeOf:D1,getOwnPropertyDescriptor:M1}=Object;var i="http://www.w3.org/2000/svg",q=[],N=()=>document.createRange(),L=(_,U,B)=>{return _.set(U,B),B},m=(_,U)=>{let B;do B=M1(_,U);while(!B&&(_=D1(_)));return B},o=(_,U)=>U.reduceRight(L1,_),L1=(_,U)=>_.childNodes[U];var s=1;var r=8;var l=11;var{setPrototypeOf:T1}=Object,n=(_)=>{function U(B){return T1(B,new.target.prototype)}return U.prototype=_.prototype,U};var T,V=(_,U,B)=>{if(!T)T=N();if(B)T.setStartAfter(_);else T.setStartBefore(_);return T.setEndAfter(U),T.deleteContents(),_};var w=({firstChild:_,lastChild:U},B)=>V(_,U,B),t=!1,j=(_,U)=>t&&_.nodeType===l?1/U<0?U?w(_,!0):_.lastChild:U?_.valueOf():_.firstChild:_,a=(_)=>document.createComment(_);class x extends n(DocumentFragment){#U=a("<>");#B=a("</>");#_=q;constructor(_){super(_);this.replaceChildren(...[this.#U,..._.childNodes,this.#B]),t=!0}get firstChild(){return this.#U}get lastChild(){return this.#B}get parentNode(){return this.#U.parentNode}remove(){w(this,!1)}replaceWith(_){w(this,!0).replaceWith(_)}valueOf(){let{parentNode:_}=this;if(_===this){if(this.#_===q)this.#_=[...this.childNodes]}else{if(_){let{firstChild:U,lastChild:B}=this;this.#_=[U];while(U!==B)this.#_.push(U=U.nextSibling)}this.replaceChildren(...this.#_)}return this}}var e=(_,U,B)=>_.setAttribute(U,B),P=(_,U)=>_.removeAttribute(U),H1=(_,U)=>{for(let B in U){let Y=U[B],z=B==="role"?B:`aria-${B}`;if(Y==null)P(_,z);else e(_,z,Y)}return U},C,P1=(_,U,B)=>{if(B=B.slice(1),!C)C=new WeakMap;let Y=C.get(_)||L(C,_,{}),z=Y[B];if(z&&z[0])_.removeEventListener(B,...z);if(z=E(U)?U:[U,!1],Y[B]=z,z[0])_.addEventListener(B,...z);return U};var G=(_,U)=>{let{t:B,n:Y}=_,z=!1;switch(typeof U){case"object":if(U!==null){(Y||B).replaceWith(_.n=U.valueOf());break}case"undefined":z=!0;default:if(B.data=z?"":U,Y)_.n=null,Y.replaceWith(B);break}return U},F1=(_,U)=>f(_,U,U==null?"class":"className"),A1=(_,U)=>{let{dataset:B}=_;for(let Y in U)if(U[Y]==null)delete B[Y];else B[Y]=U[Y];return U},k=(_,U,B)=>_[B]=U,E1=(_,U,B)=>k(_,U,B.slice(1)),f=(_,U,B)=>U==null?(P(_,B),U):k(_,U,B),_1=(_,U)=>(typeof U==="function"?U(_):U.current=_,U),y=(_,U,B)=>(U==null?P(_,B):e(_,B,U),U),N1=(_,U)=>U==null?f(_,U,"style"):k(_.style,U,"cssText"),V1=(_,U,B)=>(_.toggleAttribute(B.slice(1),U),U),D=(_,U,B)=>{let{length:Y}=U;if(_.data=`[${Y}]`,Y)return d(_.parentNode,B,U,j,_);switch(B.length){case 1:B[0].remove();case 0:break;default:V(j(B[0],0),j(B.at(-1),-0),!1);break}return q},U1=new Map([["aria",H1],["class",F1],["data",A1],["ref",_1],["style",N1]]),B1=(_,U,B)=>{switch(U[0]){case".":return E1;case"?":return V1;case"@":return P1;default:return B||"ownerSVGElement"in _?U==="ref"?_1:y:U1.get(U)||(U in _?U.startsWith("on")?k:m(_,U)?.set?f:y:y)}},Y1=(_,U)=>(_.textContent=U==null?"":U,U);var H=(_,U,B)=>({a:_,b:U,c:B}),z1=(_,U)=>({b:_,c:U}),I1=(_,U,B,Y)=>({v:q,u:_,t:U,n:B,c:Y}),R=()=>H(null,null,q);var b=(_)=>(U,B)=>{let{a:Y,b:z,c:W}=_(U,B),J=document.importNode(Y,!0),I=q;if(z!==q){I=[];for(let O,$,X=0;X<z.length;X++){let{a:Q,b:Z,c:K}=z[X],M=Q===$?O:O=o(J,$=Q);I[X]=I1(Z,M,K,Z===D?[]:Z===G?R():null)}}return z1(W?J.firstChild:new x(J),I)};var J1=/^(?:plaintext|script|style|textarea|title|xmp)$/i,O1=/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;var j1=/<([a-zA-Z0-9]+[a-zA-Z0-9:._-]*)([^>]*?)(\/?)>/g,C1=/([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g,k1=/[\x01\x02]/g,Q1=(_,U,B)=>{let Y=0;return _.join("\x01").trim().replace(j1,(z,W,J,I)=>`<${W}${J.replace(C1,"\x02=$2$1").trimEnd()}${I?B||O1.test(W)?" /":`></${W}`:""}>`).replace(k1,(z)=>z==="\x01"?`<!--${U+Y++}-->`:U+Y++)};var S=document.createElement("template"),g,h,W1=(_,U)=>{if(U){if(!g)g=document.createElementNS(i,"svg"),h=N(),h.selectNodeContents(g);return h.createContextualFragment(_)}S.innerHTML=_;let{content:B}=S;return S=S.cloneNode(!1),B};var c=(_)=>{let U=[],B;while(B=_.parentNode)U.push(U.indexOf.call(B.childNodes,_)),_=B;return U},X1=()=>document.createTextNode(""),S1=(_,U,B)=>{let Y=W1(Q1(_,F,B),B),{length:z}=_,W=q;if(z>1){let O=[],$=document.createTreeWalker(Y,129),X=0,Q=`${F}${X++}`;W=[];while(X<z){let Z=$.nextNode();if(Z.nodeType===r){if(Z.data===Q){let K=E(U[X-1])?D:G;if(K===G)O.push(Z);W.push(H(c(Z),K,null)),Q=`${F}${X++}`}}else{let K;while(Z.hasAttribute(Q)){if(!K)K=c(Z);let M=Z.getAttribute(Q);W.push(H(K,B1(Z,M,B),M)),P(Z,Q),Q=`${F}${X++}`}if(!B&&J1.test(Z.localName)&&Z.textContent.trim()===`<!--${Q}-->`)W.push(H(K||c(Z),Y1,null)),Q=`${F}${X++}`}}for(X=0;X<O.length;X++)O[X].replaceWith(X1())}let{childNodes:J}=Y,{length:I}=J;if(I<1)I=1,Y.appendChild(X1());else if(I===1&&z!==1&&J[0].nodeType!==s)I=0;return L($1,_,H(Y,W,I===1))},$1=new WeakMap,F="isµ",v=(_)=>(U,B)=>$1.get(U)||S1(U,B,_);var w1=b(v(!1)),x1=b(v(!0)),p=(_,{s:U,t:B,v:Y})=>{if(_.a!==B){let{b:z,c:W}=(U?x1:w1)(B,Y);_.a=B,_.b=z,_.c=W}for(let{c:z}=_,W=0;W<z.length;W++){let J=Y[W],I=z[W];switch(I.u){case D:I.v=D(I.t,y1(I.c,J),I.v);break;case G:let O=J instanceof A?p(I.c||(I.c=R()),J):(I.c=null,J);if(O!==I.v)I.v=G(I,O);break;default:if(J!==I.v)I.v=I.u(I.t,J,I.n,I.v);break}}return _.b},y1=(_,U)=>{let B=0,{length:Y}=U;if(Y<_.length)_.splice(Y);for(;B<Y;B++){let z=U[B];if(z instanceof A)U[B]=p(_[B]||(_[B]=R()),z);else _[B]=null}return U};class A{constructor(_,U,B){this.s=_,this.t=U,this.v=B}toDOM(_=R()){return p(_,this)}}var Z1=new WeakMap,u=(_,U)=>{let B=Z1.get(_)||L(Z1,_,R()),{b:Y}=B;if(Y!==(typeof U==="function"?U():U).toDOM(B))_.replaceChildren(B.b.valueOf());return _};/*! (c) Andrea Giammarchi - MIT */var K1=(_)=>(U,...B)=>new A(_,U,B),q1=K1(!1),C_=K1(!0);var G1=new CSSStyleSheet;G1.replaceSync(`
2
+
3
+ :host {
4
+ display: inline-block;
5
+ font-family: var(--kb-font-family-sans);
6
+ }
7
+ button {
8
+ box-sizing: border-box;
9
+ margin: 0;
10
+ min-height: 2.25rem;
11
+ padding: 0.45rem 0.8rem;
12
+ border: 1px solid var(--kb-color-border-default);
13
+ border-radius: var(--kb-radius-sm);
14
+ background: var(--kb-color-bg-subtle);
15
+ color: var(--kb-color-accent-default);
16
+ font: inherit;
17
+ font-size: var(--kb-font-size-sm);
18
+ font-weight: var(--kb-font-weight-medium);
19
+ line-height: 1;
20
+ letter-spacing: 0.08em;
21
+ text-transform: lowercase;
22
+ cursor: pointer;
23
+ transition:
24
+ color 0.2s ease,
25
+ background-color 0.2s ease,
26
+ border-color 0.2s ease;
27
+ }
28
+ button:hover {
29
+ color: var(--kb-color-fg-default);
30
+ background: var(--kb-color-bg-surface);
31
+ border-color: var(--kb-color-border-focus);
32
+ }
33
+ button:focus-visible {
34
+ outline: 2px solid var(--kb-color-border-focus);
35
+ outline-offset: 2px;
36
+ color: var(--kb-color-fg-default);
37
+ background: var(--kb-color-bg-surface);
38
+ }
39
+ /* Segmented group (set by kitbash-toggle-group) */
40
+ :host([data-group-pos="start"]) button {
41
+ border-top-right-radius: 0;
42
+ border-bottom-right-radius: 0;
43
+ border-right-width: 0;
44
+ }
45
+ :host([data-group-pos="end"]) button {
46
+ border-top-left-radius: 0;
47
+ border-bottom-left-radius: 0;
48
+ }
49
+ :host([data-group-pos="mid"]) button {
50
+ border-radius: 0;
51
+ border-right-width: 0;
52
+ }
53
+ :host([data-group-pos]:hover),
54
+ :host([data-group-pos]:focus-within) {
55
+ position: relative;
56
+ z-index: 1;
57
+ }
58
+ @media (prefers-reduced-motion: reduce) {
59
+ button {
60
+ transition: none;
61
+ }
62
+ }
63
+ `);class R1 extends HTMLElement{static get observedAttributes(){return["value","storageKey"]}static get propTypes(){return{value:"String",storageKey:"String"}}get value(){return this._props.value}set value(_){this._assignProp("value",_),this.update()}get storageKey(){return this._props.storageKey}set storageKey(_){this._assignProp("storageKey",_),this.update()}constructor(){super();this.attachShadow({mode:"open",delegatesFocus:!0}),this.shadowRoot.adoptedStyleSheets=[G1],this._defaults={value:"",storageKey:"theme"},this._state={},this._props={...this._defaults},this._eventHandlers={"click button":function(_,{props:U,commit:B}){let Y=(U.value==="light"||U.value==="dark"?U.value:typeof document<"u"&&document.documentElement.dataset.theme==="light"?"light":"dark")==="light"?"dark":"light";if(typeof document<"u")document.documentElement.dataset.theme=Y;let z=typeof U.storageKey==="string"?U.storageKey:"theme";if(z&&typeof localStorage<"u")try{localStorage.setItem(z,Y)}catch{}B({props:{value:Y}})}},this._eventCleanup=[],this._renderFn=function({props:_,html:U}){let B=_.value==="light"||_.value==="dark"?_.value:"";if(!B&&typeof document<"u")B=document.documentElement.dataset.theme==="light"?"light":"dark";if(!B)B="dark";let Y=B==="light";return U`
64
+ <button
65
+ part="toggle-root"
66
+ type="button"
67
+ aria-pressed=${Y?"true":"false"}
68
+ aria-label=${Y?"Switch to dark theme":"Switch to light theme"}
69
+ >
70
+ ${Y?"theme=light":"theme=night"}
71
+ </button>
72
+ `},this._reflecting=!1}connectedCallback(){this.update()}disconnectedCallback(){this.cleanupEvents()}attributeChangedCallback(_,U,B){if(this._reflecting)return;if(U===B)return;let Y=this.constructor.propTypes[_],z=B;if(B===null||B===void 0)z=this._defaults[_];else if(Y==="Number"&&B!=="")z=Number(B);else if(Y==="Boolean")z=B!==null&&B!=="false";this._props[_]=z,this.update()}_assignProp(_,U){let B=this.constructor.propTypes[_],Y=U;if(B==="Boolean")Y=U===!0||U==="";else if(B==="Number"&&U!==null&&U!==void 0&&U!=="")Y=Number(U);this._props[_]=Y;let z=Y===null||Y===void 0||typeof Y==="string"||typeof Y==="number"||typeof Y==="boolean";this._reflecting=!0;try{if(B==="Boolean")if(Y)this.setAttribute(_,"");else this.removeAttribute(_);else if(!z)this.removeAttribute(_);else if(Y===null||Y===void 0)this.removeAttribute(_);else this.setAttribute(_,String(Y))}finally{this._reflecting=!1}}_emitChange(){this.dispatchEvent(new CustomEvent("kitbash-change",{detail:{state:{...this._state},props:{...this._props}},bubbles:!0,composed:!0}))}commit(_){let U=_||{};if(U.props){let B=this.constructor.propTypes||{};for(let Y of Object.keys(U.props)){if(!Object.prototype.hasOwnProperty.call(B,Y))continue;this._assignProp(Y,U.props[Y])}}if(U.state)this._state={...this._state,...U.state};this.update(),this._emitChange()}setState(_){this.commit({state:_})}setProps(_){this.commit({props:_})}_handlerCtx(){return{props:{...this._props},state:{...this._state},setState:this.setState.bind(this),setProps:this.setProps.bind(this),commit:this.commit.bind(this)}}cleanupEvents(){this._eventCleanup.forEach((_)=>_()),this._eventCleanup=[]}update(){u(this.shadowRoot,this._renderFn({...this._handlerCtx(),html:q1})),this.cleanupEvents(),this.bindEvents()}bindEvents(){Object.keys(this._eventHandlers).forEach((_)=>{let U=_.split(" "),B=U[0],Y=U.slice(1).join(" ");(Y?this.shadowRoot.querySelectorAll(Y):[this]).forEach((W)=>{let J=(I)=>{this._eventHandlers[_](I,this._handlerCtx())};W.addEventListener(B,J),this._eventCleanup.push(()=>W.removeEventListener(B,J))})})}}if(!customElements.get("kitbash-theme-toggle"))customElements.define("kitbash-theme-toggle",R1);export{R1 as KitbashThemeToggle};