@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.
- package/CHANGELOG.md +114 -0
- package/README.md +70 -12
- package/components.json +206 -189
- package/package.json +4 -1
- package/src/cli/commands/doctor.js +1 -0
- package/src/cli/commands/new.js +29 -8
- package/src/cli/kmwc.js +1 -0
- package/src/config.d.ts +5 -0
- package/src/config.js +26 -3
- package/src/examples/chapter-links.js +35 -0
- package/src/examples/chapter-search.js +84 -0
- package/src/examples/constants.js +51 -0
- package/src/examples/css/content.css +4 -34
- package/src/examples/css/controls.css +30 -25
- package/src/examples/css/copy-link.css +1 -0
- package/src/examples/css/demo.css +2 -0
- package/src/examples/css/form-controls.css +285 -0
- package/src/examples/css/index-page.css +1 -1
- package/src/examples/css/spotlight.css +417 -0
- package/src/examples/css/style.css +3 -0
- package/src/examples/css/tables.css +141 -0
- package/src/examples/css/tokens.css +69 -27
- package/src/examples/index.d.ts +38 -0
- package/src/examples/index.js +6 -3
- package/src/examples/kit.js +11 -4
- package/src/examples/page-config.js +1 -0
- package/src/examples/sections.js +38 -1
- package/src/examples/spotlight.js +473 -0
- package/src/vite/chapter-index.js +107 -0
- package/src/vite/index.js +19 -1
- package/src/vite/page-composer.js +17 -10
- package/templates/claude-block.md +9 -2
- package/templates/new/component-variables.manifest.json +3 -3
- package/templates/new/docs/examples.md +1 -1
- package/templates/new/docs/theming.md +1 -1
- package/templates/new/e2e/dark-mode.spec.ts +3 -3
- package/templates/new/examples-theming.html +3 -3
- package/templates/new/src/css/base.css +5 -3
- package/templates/new/src/css/variables.css +10 -8
- package/templates/new-minimal/src/css/base.css +5 -3
- 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, '<')
|
|
28
|
+
.replace(/>/g, '>')
|
|
29
|
+
.replace(/"/g, '"')
|
|
30
|
+
.replace(/'/g, ''');
|
|
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,
|
|
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
|
-
|
|
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
|
-
.
|
|
67
|
-
|
|
70
|
+
.controls label code,
|
|
71
|
+
.toggle-label code {
|
|
72
|
+
overflow-wrap: anywhere;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
.
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
.
|
|
77
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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-
|
|
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-
|
|
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:
|
|
208
|
+
background: var(--kmwc-danger-fill-color-hover);
|
|
204
209
|
}
|
|
205
210
|
}
|
|
@@ -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-
|
|
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;
|