@keenmate/web-components-devkit 1.0.0-rc01 → 1.0.0-rc03

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 (41) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +70 -12
  3. package/components.json +206 -189
  4. package/package.json +4 -1
  5. package/src/cli/commands/doctor.js +1 -0
  6. package/src/cli/commands/new.js +29 -8
  7. package/src/cli/kmwc.js +1 -0
  8. package/src/config.d.ts +5 -0
  9. package/src/config.js +26 -3
  10. package/src/examples/chapter-links.js +35 -0
  11. package/src/examples/chapter-search.js +84 -0
  12. package/src/examples/constants.js +51 -0
  13. package/src/examples/css/content.css +4 -34
  14. package/src/examples/css/controls.css +30 -25
  15. package/src/examples/css/copy-link.css +1 -0
  16. package/src/examples/css/demo.css +2 -0
  17. package/src/examples/css/form-controls.css +285 -0
  18. package/src/examples/css/index-page.css +1 -1
  19. package/src/examples/css/spotlight.css +417 -0
  20. package/src/examples/css/style.css +3 -0
  21. package/src/examples/css/tables.css +141 -0
  22. package/src/examples/css/tokens.css +69 -27
  23. package/src/examples/index.d.ts +38 -0
  24. package/src/examples/index.js +6 -3
  25. package/src/examples/kit.js +11 -4
  26. package/src/examples/page-config.js +1 -0
  27. package/src/examples/sections.js +38 -1
  28. package/src/examples/spotlight.js +473 -0
  29. package/src/vite/chapter-index.js +107 -0
  30. package/src/vite/index.js +19 -1
  31. package/src/vite/page-composer.js +17 -10
  32. package/templates/claude-block.md +9 -2
  33. package/templates/new/component-variables.manifest.json +3 -3
  34. package/templates/new/docs/examples.md +1 -1
  35. package/templates/new/docs/theming.md +1 -1
  36. package/templates/new/e2e/dark-mode.spec.ts +3 -3
  37. package/templates/new/examples-theming.html +3 -3
  38. package/templates/new/src/css/base.css +5 -3
  39. package/templates/new/src/css/variables.css +10 -8
  40. package/templates/new-minimal/src/css/base.css +5 -3
  41. package/templates/new-minimal/src/css/variables.css +7 -5
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Chapter search for the spotlight. MIRRORS web-commandpalette — see
3
+ * scripts/spotlight-mirror.json for the files and the commit it is aligned to;
4
+ * `npm run check:spotlight-mirror` lists what changed there since. A block
5
+ * marked `mirrors …` keeps the wcp structure line for line (devkit naming): fix
6
+ * a bug in one, port it to the other.
7
+ *
8
+ * Matching is wcp's: lower-cased substring of the whole query against any
9
+ * field — here the section code and the heading text. On top (spotlight only):
10
+ * a query that is a section code finds it without the zero padding (`th7` →
11
+ * TH07), and the order is exact code, then codes starting with the query, then
12
+ * the rest — each in index order. Pure, so it is unit-tested.
13
+ */
14
+ import { mapToCodeKey } from './sections.js';
15
+
16
+ const RANK_EXACT_CODE = 0;
17
+ const RANK_CODE_PREFIX = 1;
18
+ const RANK_TEXT = 2;
19
+
20
+ // mirrors web-commandpalette src/filter.ts `norm`
21
+ const mapToNormalizedText = (value) => (value == null ? '' : String(value)).toLowerCase();
22
+
23
+ // mirrors web-commandpalette src/icons.ts escapeHtml()
24
+ export function mapToEscapedHtml(value) {
25
+ return String(value ?? '')
26
+ .replace(/&/g, '&')
27
+ .replace(/</g, '&lt;')
28
+ .replace(/>/g, '&gt;')
29
+ .replace(/"/g, '&quot;')
30
+ .replace(/'/g, '&#39;');
31
+ }
32
+
33
+ /** Substring match of `needle` against any of `fields`. Empty needle matches all. */
34
+ // mirrors web-commandpalette src/filter.ts matchesAny()
35
+ export function checkMatchesAny(fields, needle) {
36
+ const query = mapToNormalizedText(needle).trim();
37
+ if (!query) return true;
38
+ return fields.some((field) => mapToNormalizedText(field).includes(query));
39
+ }
40
+
41
+ /** The fields a chapter is searched by (wcp: commandFields()). */
42
+ export function getChapterFields(chapter) {
43
+ return [chapter.code, chapter.title];
44
+ }
45
+
46
+ /**
47
+ * @param {Array<{ code: string | null, title: string }>} chapters
48
+ * @param {string} query
49
+ * @returns the matching chapters, best first (all of them for an empty query)
50
+ */
51
+ export function searchChapters(chapters, query) {
52
+ const needle = mapToNormalizedText(query).trim();
53
+ if (!needle) return chapters.slice();
54
+ const queryCodeKey = mapToCodeKey(needle);
55
+
56
+ const rankedChapters = [];
57
+ chapters.forEach((chapter, chapterIndex) => {
58
+ const code = mapToNormalizedText(chapter.code);
59
+ const isExactCode = code !== '' && mapToCodeKey(code) === queryCodeKey;
60
+ if (!isExactCode && !checkMatchesAny(getChapterFields(chapter), needle)) return;
61
+ const rank = isExactCode ? RANK_EXACT_CODE : code.startsWith(needle) ? RANK_CODE_PREFIX : RANK_TEXT;
62
+ rankedChapters.push({ chapter, rank, chapterIndex });
63
+ });
64
+
65
+ return rankedChapters
66
+ .sort((first, second) => first.rank - second.rank || first.chapterIndex - second.chapterIndex)
67
+ .map((rankedChapter) => rankedChapter.chapter);
68
+ }
69
+
70
+ /** Escape `text`, wrapping case-insensitive occurrences of `needle` in `<mark>`. */
71
+ // mirrors web-commandpalette src/render.ts highlight()
72
+ export function renderHighlightedText(text, needle) {
73
+ const query = needle.trim();
74
+ if (!query) return mapToEscapedHtml(text);
75
+ const lowerText = text.toLowerCase();
76
+ const lowerQuery = query.toLowerCase();
77
+ let html = '';
78
+ let index = 0;
79
+ for (let matchIndex = lowerText.indexOf(lowerQuery); matchIndex !== -1; matchIndex = lowerText.indexOf(lowerQuery, index)) {
80
+ html += mapToEscapedHtml(text.slice(index, matchIndex)) + '<mark>' + mapToEscapedHtml(text.slice(matchIndex, matchIndex + lowerQuery.length)) + '</mark>';
81
+ index = matchIndex + lowerQuery.length;
82
+ }
83
+ return html + mapToEscapedHtml(text.slice(index));
84
+ }
@@ -20,6 +20,57 @@ export const CHAPTER_NAV_CLASSES = {
20
20
 
21
21
  export const CHAPTER_NAV_PANEL_ID = 'kmwc-chapter-nav-panel';
22
22
 
23
+ /**
24
+ * Spotlight: the cross-page chapter search (spotlight.js). Element names
25
+ * mirror web-commandpalette's `pa-command-palette__*` shell (pure-admin's
26
+ * canonical command-palette snippet) under the kit prefix; `trigger` and
27
+ * `itemCode` are the spotlight's own.
28
+ */
29
+ export const SPOTLIGHT_CLASSES = {
30
+ root: 'kmwc-spotlight',
31
+ rootActive: 'kmwc-spotlight--active',
32
+ rootFullscreen: 'kmwc-spotlight--fullscreen',
33
+ backdrop: 'kmwc-spotlight__backdrop',
34
+ container: 'kmwc-spotlight__container',
35
+ fullscreenBar: 'kmwc-spotlight__fullscreen-bar',
36
+ fullscreenTitle: 'kmwc-spotlight__fullscreen-title',
37
+ close: 'kmwc-spotlight__close',
38
+ search: 'kmwc-spotlight__search',
39
+ inputWrapper: 'kmwc-spotlight__input-wrapper',
40
+ input: 'kmwc-spotlight__input',
41
+ results: 'kmwc-spotlight__results',
42
+ homeSection: 'kmwc-spotlight__home-section',
43
+ homeHeading: 'kmwc-spotlight__home-heading',
44
+ item: 'kmwc-spotlight__item',
45
+ itemActive: 'kmwc-spotlight__item--active',
46
+ itemCode: 'kmwc-spotlight__item-code',
47
+ itemContent: 'kmwc-spotlight__item-content',
48
+ itemTitle: 'kmwc-spotlight__item-title',
49
+ empty: 'kmwc-spotlight__empty',
50
+ footer: 'kmwc-spotlight__footer',
51
+ hint: 'kmwc-spotlight__hint',
52
+ key: 'kmwc-spotlight__key',
53
+ live: 'kmwc-spotlight__live',
54
+ trigger: 'kmwc-spotlight__trigger',
55
+ triggerKey: 'kmwc-spotlight__trigger-key',
56
+ };
57
+
58
+ export const SPOTLIGHT_RESULTS_ID = 'kmwc-spotlight-results';
59
+ export const SPOTLIGHT_ITEM_ID_PREFIX = 'kmwc-spotlight-item-';
60
+ /** The Ctrl/⌘ + letter that toggles the spotlight (wcp: `openKey`, default `k`). */
61
+ export const SPOTLIGHT_OPEN_KEY = 'k';
62
+ /** PgUp / PgDn step (wcp palette.ts PAGE_STEP). */
63
+ export const SPOTLIGHT_PAGE_STEP = 8;
64
+ /** Touch phone → fullscreen sheet (wcp: core's device classification, approximated in CSS terms). */
65
+ export const SPOTLIGHT_FULLSCREEN_MEDIA = '(pointer: coarse) and (max-width: 768px)';
66
+
67
+ /**
68
+ * A section code at the start of a heading label: `TH07 · Title` (rulebook
69
+ * example-pages.md). Forgiving on purpose — `EV4b` counts; enforcing the
70
+ * format is the rulebook check's job.
71
+ */
72
+ export const CHAPTER_CODE_PATTERN = /^([A-Z]{2,3}\d+[a-z]?)\s*·\s*/;
73
+
23
74
  export const COPY_LINK_CLASSES = {
24
75
  root: 'kmwc-copy-link',
25
76
  rootCopied: 'kmwc-copy-link--copied',
@@ -1,7 +1,7 @@
1
1
  /* ==============================================================================
2
2
  EXAMPLES KIT — CONTENT
3
3
  ==============================================================================
4
- Output panels, code, notes, grids, reference tables, badges. */
4
+ Output panels, code, notes, grids, badges. Tables are tables.css. */
5
5
  @layer kmwc {
6
6
  /* ==========================================================================
7
7
  OUTPUT
@@ -99,8 +99,10 @@
99
99
  position: absolute;
100
100
  top: 0.5rem;
101
101
  right: 0.5rem;
102
+ min-height: 0; /* not the control height (button rule) */
102
103
  padding: 0.25rem 0.75rem;
103
104
  font-size: 0.75rem;
105
+ line-height: normal;
104
106
  background: color-mix(in srgb, var(--kmwc-code-text-color) 10%, transparent);
105
107
  color: var(--kmwc-code-text-color);
106
108
  border: 1px solid color-mix(in srgb, var(--kmwc-code-text-color) 20%, transparent);
@@ -184,40 +186,8 @@
184
186
  }
185
187
 
186
188
  /* ==========================================================================
187
- REFERENCE TABLE & BADGES
189
+ BADGES & HELPERS
188
190
  ========================================================================== */
189
- .reference-table {
190
- width: 100%;
191
- border-collapse: collapse;
192
- font-size: 0.875rem;
193
- margin-bottom: 2rem;
194
- }
195
-
196
- .reference-table th,
197
- .reference-table td {
198
- padding: 0.75rem 1rem;
199
- text-align: left;
200
- border: 1px solid var(--kmwc-border-color);
201
- }
202
-
203
- .reference-table th {
204
- background: var(--kmwc-elevated-bg);
205
- font-weight: 600;
206
- }
207
-
208
- .reference-table tbody tr:hover {
209
- background: var(--kmwc-hover-bg);
210
- }
211
-
212
- .reference-table tr.is-highlight {
213
- background: var(--kmwc-accent-light-bg);
214
- }
215
-
216
- .reference-table code {
217
- padding: 0.125rem 0.375rem;
218
- font-size: 0.75rem;
219
- }
220
-
221
191
  /* Section-header badge pill (e.g. a "NEW" tag beside an h2). */
222
192
  .badge {
223
193
  display: inline-block;
@@ -1,7 +1,8 @@
1
1
  /* ==============================================================================
2
2
  EXAMPLES KIT — CONTROLS
3
3
  ==============================================================================
4
- Demo forms, the `.controls` switch rows, demo areas and buttons. */
4
+ Demo forms, the `.controls` switch rows, demo areas and buttons. How the
5
+ native controls themselves look is form-controls.css. */
5
6
  @layer kmwc {
6
7
  /* ==========================================================================
7
8
  FORMS
@@ -61,30 +62,23 @@
61
62
  align-items: center;
62
63
  gap: 0.5rem;
63
64
  cursor: pointer;
65
+ /* A long label wraps inside the row instead of pushing the page wider. */
66
+ flex-wrap: wrap;
67
+ max-width: 100%;
64
68
  }
65
69
 
66
- .toggle-label {
67
- margin-bottom: 0.75rem;
70
+ .controls label code,
71
+ .toggle-label code {
72
+ overflow-wrap: anywhere;
68
73
  }
69
74
 
70
- .controls input[type="checkbox"] {
71
- width: 16px;
72
- height: 16px;
73
- accent-color: var(--kmwc-accent-color);
75
+ /* A select is as wide as its longest option — cap it at the row. */
76
+ .controls select {
77
+ max-width: 100%;
74
78
  }
75
79
 
76
- .controls select,
77
- .controls input[type="text"],
78
- .controls input[type="number"],
79
- .controls input[type="search"],
80
- .control-group input,
81
- .control-group select {
82
- padding: 0.4rem 0.6rem;
83
- border: var(--kmwc-input-border);
84
- border-radius: var(--kmwc-radius-sm);
85
- font-size: 0.875rem;
86
- background: var(--kmwc-input-bg);
87
- color: var(--kmwc-input-color);
80
+ .toggle-label {
81
+ margin-bottom: 0.75rem;
88
82
  }
89
83
 
90
84
  /* Control group — a label stacked over an input/select; rows of them wrap
@@ -155,13 +149,24 @@
155
149
  cursor: pointer;
156
150
  background: var(--kmwc-accent-color);
157
151
  color: var(--kmwc-text-color-on-accent);
158
- border: none;
159
- padding: 0.5rem 1rem;
152
+ /* The control height (form-controls.css): a transparent border, so an
153
+ outline button is as tall as a filled one, and a fixed line box. */
154
+ border: 1px solid transparent;
155
+ min-height: var(--kmwc-control-height);
156
+ padding: calc((var(--kmwc-control-height) - 1.25rem - 2px) / 2) 1rem;
160
157
  border-radius: var(--kmwc-radius-sm);
158
+ font: inherit;
161
159
  font-size: 0.875rem;
160
+ line-height: 1.25rem;
161
+ vertical-align: middle;
162
162
  transition: background 0.2s;
163
163
  }
164
164
 
165
+ button:focus-visible {
166
+ outline: 2px solid var(--kmwc-accent-color);
167
+ outline-offset: 2px;
168
+ }
169
+
165
170
  button:hover,
166
171
  button.btn-primary:hover {
167
172
  background: var(--kmwc-accent-color-hover);
@@ -176,7 +181,7 @@
176
181
  button.secondary,
177
182
  button.btn-secondary {
178
183
  background: var(--kmwc-neutral-color);
179
- color: var(--kmwc-text-color-on-accent);
184
+ color: var(--kmwc-text-color-on-neutral);
180
185
  }
181
186
 
182
187
  button.secondary:hover,
@@ -195,11 +200,11 @@
195
200
  }
196
201
 
197
202
  button.btn-danger {
198
- background: var(--kmwc-danger-color);
199
- color: var(--kmwc-text-color-on-accent);
203
+ background: var(--kmwc-danger-fill-color);
204
+ color: var(--kmwc-text-color-on-danger);
200
205
  }
201
206
 
202
207
  button.btn-danger:hover {
203
- background: color-mix(in srgb, var(--kmwc-danger-color) 85%, black);
208
+ background: var(--kmwc-danger-fill-color-hover);
204
209
  }
205
210
  }
@@ -9,6 +9,7 @@
9
9
  align-items: center;
10
10
  gap: 0.35rem;
11
11
  font-size: 0.8rem;
12
+ min-height: 0; /* not the control height (button rule) */
12
13
  line-height: 1;
13
14
  padding: 0.25rem 0.4rem;
14
15
  margin-inline-start: 0.15rem;
@@ -32,8 +32,10 @@
32
32
  position: absolute;
33
33
  top: 0.5rem;
34
34
  right: 0.5rem;
35
+ min-height: 0; /* not the control height (button rule) */
35
36
  padding: 0.25rem 0.75rem;
36
37
  font-size: 0.75rem;
38
+ line-height: normal;
37
39
  background: color-mix(in srgb, var(--kmwc-code-text-color) 10%, transparent);
38
40
  color: var(--kmwc-code-text-color);
39
41
  border: 1px solid color-mix(in srgb, var(--kmwc-code-text-color) 20%, transparent);
@@ -0,0 +1,285 @@
1
+ /* ==============================================================================
2
+ EXAMPLES KIT — FORM CONTROLS
3
+ ==============================================================================
4
+ One look for the native controls a demo page uses — text fields, selects
5
+ (single and multiple), textareas, checkboxes, radios, switches, ranges,
6
+ colour and file inputs — wherever the page puts them, not only in a
7
+ `.controls` row. Modelled on pure-admin's forms (fixed control height,
8
+ accent focus border + halo, the shared --base-icon-check tick) and read
9
+ through the --base-* contract, so a theme restyles them with the component.
10
+
11
+ Every selector is wrapped in :where() — zero specificity — so any page or
12
+ component rule wins. `.kmwc-native` on an element (or an ancestor) keeps
13
+ the browser's own controls, e.g. for a light-DOM component that draws its
14
+ own inputs. A switch is `<input type="checkbox" role="switch">`. */
15
+ @layer kmwc {
16
+ /* ==========================================================================
17
+ TEXT FIELDS & SELECT
18
+ ==========================================================================
19
+ A fixed height and no vertical padding: the browser centres the text. */
20
+ :where(
21
+ input:not([type]),
22
+ input[type="text"], input[type="search"], input[type="email"], input[type="url"],
23
+ input[type="tel"], input[type="password"], input[type="number"], input[type="date"],
24
+ input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"],
25
+ select, textarea
26
+ ):not(:where(.kmwc-native, .kmwc-native *)) {
27
+ box-sizing: border-box;
28
+ height: var(--kmwc-control-height);
29
+ padding: 0 0.625rem;
30
+ border: var(--kmwc-input-border);
31
+ border-radius: var(--kmwc-radius-sm);
32
+ background-color: var(--kmwc-input-bg);
33
+ color: var(--kmwc-input-color);
34
+ font: inherit;
35
+ font-size: 0.875rem;
36
+ transition: border-color 0.15s ease-out, box-shadow 0.15s ease-out;
37
+ }
38
+
39
+ :where(input, select, textarea):not(:where(.kmwc-native, .kmwc-native *))::placeholder {
40
+ color: var(--kmwc-input-placeholder-color);
41
+ opacity: 1;
42
+ }
43
+
44
+ :where(
45
+ input:not([type]),
46
+ input[type="text"], input[type="search"], input[type="email"], input[type="url"],
47
+ input[type="tel"], input[type="password"], input[type="number"], input[type="date"],
48
+ input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"],
49
+ select, textarea
50
+ ):not(:where(.kmwc-native, .kmwc-native *)):hover {
51
+ border: var(--kmwc-input-border-hover);
52
+ }
53
+
54
+ :where(
55
+ input:not([type]),
56
+ input[type="text"], input[type="search"], input[type="email"], input[type="url"],
57
+ input[type="tel"], input[type="password"], input[type="number"], input[type="date"],
58
+ input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"],
59
+ select, textarea
60
+ ):not(:where(.kmwc-native, .kmwc-native *)):focus {
61
+ outline: none;
62
+ border: var(--kmwc-input-border-focus);
63
+ box-shadow: var(--kmwc-input-box-shadow-focus);
64
+ }
65
+
66
+ /* A field the user has left invalid (not one that starts empty). */
67
+ :where(input, select, textarea):not(:where(.kmwc-native, .kmwc-native *)):user-invalid {
68
+ border-color: var(--kmwc-danger-fill-color);
69
+ }
70
+
71
+ :where(input, select, textarea):not(:where(.kmwc-native, .kmwc-native *)):disabled {
72
+ background-color: var(--kmwc-input-disabled-bg);
73
+ opacity: var(--kmwc-disabled-opacity);
74
+ cursor: not-allowed;
75
+ }
76
+
77
+ :where(input[type="number"]):not(:where(.kmwc-native, .kmwc-native *)) {
78
+ padding-inline-end: 0.25rem; /* room for the spin buttons */
79
+ }
80
+
81
+ /* Single select: the kit's caret (drawn from the input icon colour, so it
82
+ follows the theme) instead of the platform's. */
83
+ :where(select:not([multiple], [size])):not(:where(.kmwc-native, .kmwc-native *)) {
84
+ appearance: none;
85
+ padding-inline-end: 2rem;
86
+ background-image:
87
+ linear-gradient(45deg, transparent 50%, var(--kmwc-input-icon-color) 50%),
88
+ linear-gradient(135deg, var(--kmwc-input-icon-color) 50%, transparent 50%);
89
+ background-position:
90
+ right 0.95rem center,
91
+ right 0.65rem center;
92
+ background-size: 0.3rem 0.3rem;
93
+ background-repeat: no-repeat;
94
+ cursor: pointer;
95
+ }
96
+
97
+ /* Multiple select / list box: grows with its rows; options are rows. */
98
+ :where(select[multiple], select[size]):not(:where(.kmwc-native, .kmwc-native *)) {
99
+ height: auto;
100
+ padding: 0.25rem;
101
+ }
102
+
103
+ :where(select[multiple], select[size]):not(:where(.kmwc-native, .kmwc-native *)) option {
104
+ padding: 0.25rem 0.5rem;
105
+ border-radius: var(--kmwc-radius-sm);
106
+ }
107
+
108
+ :where(select[multiple], select[size]):not(:where(.kmwc-native, .kmwc-native *)) option:checked {
109
+ background: var(--kmwc-accent-light-bg-hover);
110
+ color: var(--kmwc-text-color-1);
111
+ }
112
+
113
+ :where(textarea):not(:where(.kmwc-native, .kmwc-native *)) {
114
+ height: auto;
115
+ min-height: 5rem;
116
+ padding: 0.5rem 0.625rem;
117
+ line-height: 1.5;
118
+ resize: vertical;
119
+ }
120
+
121
+ /* Inside a form group a field takes the row (pure-admin .pa-input). */
122
+ :where(.form-group) :where(
123
+ input:not([type]),
124
+ input[type="text"], input[type="search"], input[type="email"], input[type="url"],
125
+ input[type="tel"], input[type="password"], input[type="number"], input[type="date"],
126
+ input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"],
127
+ select, textarea
128
+ ) {
129
+ width: 100%;
130
+ }
131
+
132
+ /* ==========================================================================
133
+ CHECKBOX & RADIO
134
+ ==========================================================================
135
+ The native input, redrawn: the contract's checkbox render method — a
136
+ border-box square sized by --base-checkbox-scale, the tick / dash masked
137
+ from --base-icon-check / --base-icon-indeterminate (the glyph every
138
+ Keenmate component draws). Radios get a dot from the same tokens. */
139
+ :where(input[type="checkbox"], input[type="radio"]):not(:where(.kmwc-native, .kmwc-native *)) {
140
+ appearance: none;
141
+ box-sizing: border-box;
142
+ position: relative;
143
+ flex-shrink: 0;
144
+ display: inline-block;
145
+ width: var(--kmwc-checkbox-size);
146
+ height: var(--kmwc-checkbox-size);
147
+ margin: 0;
148
+ vertical-align: -0.15em;
149
+ border: calc(1px * var(--base-checkbox-scale, 1)) solid var(--kmwc-checkbox-border-color);
150
+ border-radius: calc(4px * var(--base-checkbox-scale, 1));
151
+ background-color: var(--kmwc-input-bg);
152
+ cursor: pointer;
153
+ transition: background-color 0.15s ease, border-color 0.15s ease;
154
+ }
155
+
156
+ :where(input[type="radio"]):not(:where(.kmwc-native, .kmwc-native *)) {
157
+ border-radius: 50%;
158
+ }
159
+
160
+ :where(input[type="checkbox"], input[type="radio"]):not(:where(.kmwc-native, .kmwc-native *)):hover:not(:checked, :indeterminate, :disabled) {
161
+ border-color: var(--kmwc-accent-color);
162
+ }
163
+
164
+ :where(input[type="checkbox"], input[type="radio"]):not(:where(.kmwc-native, .kmwc-native *)):focus-visible {
165
+ outline: none;
166
+ border-color: var(--kmwc-accent-color);
167
+ box-shadow: 0 0 0 3px var(--kmwc-accent-light-bg-hover);
168
+ }
169
+
170
+ :where(input[type="checkbox"]):not(:where(.kmwc-native, .kmwc-native *)):is(:checked, :indeterminate) {
171
+ background-color: var(--kmwc-accent-color);
172
+ border-color: var(--kmwc-accent-color);
173
+ }
174
+
175
+ :where(input[type="checkbox"]):not(:where(.kmwc-native, .kmwc-native *, [role="switch"])):is(:checked, :indeterminate)::before {
176
+ content: '';
177
+ position: absolute;
178
+ inset: 0;
179
+ background-color: var(--kmwc-text-color-on-accent);
180
+ mask: var(--kmwc-icon-check) center / var(--kmwc-icon-check-size) no-repeat;
181
+ }
182
+
183
+ :where(input[type="checkbox"]):not(:where(.kmwc-native, .kmwc-native *, [role="switch"])):indeterminate::before {
184
+ mask-image: var(--kmwc-icon-indeterminate);
185
+ }
186
+
187
+ :where(input[type="radio"]):not(:where(.kmwc-native, .kmwc-native *)):checked {
188
+ border-color: var(--kmwc-accent-color);
189
+ background: radial-gradient(circle, var(--kmwc-text-color-on-accent) 0 30%, var(--kmwc-accent-color) 34%);
190
+ }
191
+
192
+ :where(input[type="checkbox"], input[type="radio"]):not(:where(.kmwc-native, .kmwc-native *)):disabled {
193
+ opacity: var(--kmwc-disabled-opacity);
194
+ cursor: not-allowed;
195
+ }
196
+
197
+ /* The option's label fades with it. */
198
+ :where(label):has(> :is(input, select, textarea):disabled) {
199
+ color: var(--kmwc-text-color-3);
200
+ cursor: not-allowed;
201
+ }
202
+
203
+ /* ==========================================================================
204
+ SWITCH — <input type="checkbox" role="switch">
205
+ ========================================================================== */
206
+ :where(input[type="checkbox"][role="switch"]):not(:where(.kmwc-native, .kmwc-native *)) {
207
+ width: calc(var(--kmwc-checkbox-size) * 1.9);
208
+ border-radius: 999px;
209
+ background-color: var(--kmwc-checkbox-border-color);
210
+ border-color: var(--kmwc-checkbox-border-color);
211
+ }
212
+
213
+ :where(input[type="checkbox"][role="switch"]):not(:where(.kmwc-native, .kmwc-native *))::before {
214
+ content: '';
215
+ position: absolute;
216
+ top: 1px;
217
+ left: 1px;
218
+ width: calc(var(--kmwc-checkbox-size) - 4px);
219
+ height: calc(var(--kmwc-checkbox-size) - 4px);
220
+ border-radius: 50%;
221
+ background-color: var(--kmwc-main-bg);
222
+ transition: translate 0.15s ease;
223
+ }
224
+
225
+ :where(input[type="checkbox"][role="switch"]):not(:where(.kmwc-native, .kmwc-native *)):checked::before {
226
+ translate: calc(var(--kmwc-checkbox-size) * 0.9) 0;
227
+ }
228
+
229
+ /* ==========================================================================
230
+ RANGE, COLOUR, FILE
231
+ ========================================================================== */
232
+ :where(input[type="range"], progress, meter):not(:where(.kmwc-native, .kmwc-native *)) {
233
+ accent-color: var(--kmwc-accent-color);
234
+ vertical-align: middle;
235
+ }
236
+
237
+ :where(input[type="color"]):not(:where(.kmwc-native, .kmwc-native *)) {
238
+ box-sizing: border-box;
239
+ width: var(--kmwc-control-height);
240
+ height: var(--kmwc-control-height);
241
+ padding: 0.2rem;
242
+ border: var(--kmwc-input-border);
243
+ border-radius: var(--kmwc-radius-sm);
244
+ background-color: var(--kmwc-input-bg);
245
+ cursor: pointer;
246
+ }
247
+
248
+ :where(input[type="file"]):not(:where(.kmwc-native, .kmwc-native *)) {
249
+ font: inherit;
250
+ font-size: 0.875rem;
251
+ color: var(--kmwc-text-color-2);
252
+ }
253
+
254
+ :where(input[type="file"]):not(:where(.kmwc-native, .kmwc-native *))::file-selector-button {
255
+ height: var(--kmwc-control-height);
256
+ margin-inline-end: 0.75rem;
257
+ padding: 0 1rem;
258
+ border: 1px solid var(--kmwc-border-color-strong);
259
+ border-radius: var(--kmwc-radius-sm);
260
+ background: var(--kmwc-main-bg);
261
+ color: var(--kmwc-text-color-1);
262
+ font: inherit;
263
+ cursor: pointer;
264
+ }
265
+
266
+ :where(input[type="file"]):not(:where(.kmwc-native, .kmwc-native *))::file-selector-button:hover {
267
+ background: var(--kmwc-hover-bg);
268
+ }
269
+
270
+ /* ==========================================================================
271
+ FIELDSET
272
+ ========================================================================== */
273
+ :where(fieldset):not(:where(.kmwc-native, .kmwc-native *)) {
274
+ border: 1px solid var(--kmwc-border-color);
275
+ border-radius: var(--kmwc-radius-md);
276
+ padding: 0.75rem 1rem 1rem;
277
+ }
278
+
279
+ :where(legend):not(:where(.kmwc-native, .kmwc-native *)) {
280
+ padding: 0 0.375rem;
281
+ font-weight: 600;
282
+ font-size: 0.875rem;
283
+ color: var(--kmwc-text-color-2);
284
+ }
285
+ }
@@ -93,7 +93,7 @@
93
93
  .kmwc-index__badge {
94
94
  display: inline-block;
95
95
  background: var(--kmwc-badge-new-bg);
96
- color: var(--kmwc-text-color-on-accent);
96
+ color: var(--kmwc-text-color-on-badge-new);
97
97
  padding: 0.15rem 0.5rem;
98
98
  border-radius: var(--kmwc-radius-sm);
99
99
  font-size: 0.65rem;