@zenit-hosting/zenit-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +400 -0
- package/fesm2022/zenit-hosting-zenit-ui.mjs +11289 -0
- package/fesm2022/zenit-hosting-zenit-ui.mjs.map +1 -0
- package/llms-full.txt +5120 -0
- package/llms.txt +179 -0
- package/package.json +44 -0
- package/schematics/collection.json +15 -0
- package/schematics/migrate-material/component.js +241 -0
- package/schematics/migrate-material/index.js +463 -0
- package/schematics/migrate-material/report.js +125 -0
- package/schematics/migrate-material/schema.js +2 -0
- package/schematics/migrate-material/schema.json +32 -0
- package/schematics/migrate-material/tables.js +138 -0
- package/schematics/migrate-material/template.js +871 -0
- package/schematics/ng-add/html.js +151 -0
- package/schematics/ng-add/index.js +609 -0
- package/schematics/ng-add/init-script.js +108 -0
- package/schematics/ng-add/schema.js +2 -0
- package/schematics/ng-add/schema.json +34 -0
- package/schematics/package.json +3 -0
- package/styles/_daten.css +138 -0
- package/styles/_formulare.css +97 -0
- package/styles/_grundlage.css +424 -0
- package/styles/_konfigurator.css +216 -0
- package/styles/_navigation.css +179 -0
- package/styles/_overlays.css +86 -0
- package/styles/_rueckmeldung.css +100 -0
- package/styles/_werkzeuge.css +119 -0
- package/styles/themes/accents.css +91 -0
- package/styles/themes/base.css +26 -0
- package/styles/themes/contrast.css +67 -0
- package/styles/themes/light.css +74 -0
- package/styles/themes.css +21 -0
- package/styles/tokens.css +76 -0
- package/styles/zenit-ui.css +12 -0
- package/types/zenit-hosting-zenit-ui.d.ts +5957 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Plain TypeScript without a single import, on purpose: `ng add zenit-ui`
|
|
4
|
+
* needs the same script, and schematics are compiled apart from the library
|
|
5
|
+
* (CommonJS, no DOM types, `rootDir: schematics`). A byte-identical copy of
|
|
6
|
+
* this file therefore lives at `schematics/ng-add/init-script.ts`; the test in
|
|
7
|
+
* `schematics/ng-add/index.spec.ts` fails as soon as the two differ. Edit this
|
|
8
|
+
* file, then copy it over.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.ATTRIBUT_AKZENT = exports.ATTRIBUT_SCHEMA = exports.ABFRAGE_KONTRAST = exports.ABFRAGE_DUNKEL = exports.SCHEMA_HELL = exports.SCHEMA_DUNKEL = exports.SCHEMA_KONTRAST = exports.SCHEMA_SYSTEM = exports.THEME_STANDARD = void 0;
|
|
12
|
+
exports.themeEinstellung = themeEinstellung;
|
|
13
|
+
exports.zenitThemeInitScript = zenitThemeInitScript;
|
|
14
|
+
/** @internal Defaults shared by `ZTheme` and the init script. */
|
|
15
|
+
exports.THEME_STANDARD = {
|
|
16
|
+
schemes: ['dark', 'light', 'contrast'],
|
|
17
|
+
accents: ['rot', 'blau', 'gruen', 'violett'],
|
|
18
|
+
defaultScheme: 'dark',
|
|
19
|
+
defaultAccent: 'rot',
|
|
20
|
+
storageKey: 'zenit-theme',
|
|
21
|
+
};
|
|
22
|
+
/** @internal The scheme id that hands the decision to the operating system. */
|
|
23
|
+
exports.SCHEMA_SYSTEM = 'system';
|
|
24
|
+
/** @internal The scheme `system` resolves to while `prefers-contrast: more` matches. */
|
|
25
|
+
exports.SCHEMA_KONTRAST = 'contrast';
|
|
26
|
+
/** @internal */
|
|
27
|
+
exports.SCHEMA_DUNKEL = 'dark';
|
|
28
|
+
/** @internal */
|
|
29
|
+
exports.SCHEMA_HELL = 'light';
|
|
30
|
+
/** @internal */
|
|
31
|
+
exports.ABFRAGE_DUNKEL = '(prefers-color-scheme: dark)';
|
|
32
|
+
/** @internal */
|
|
33
|
+
exports.ABFRAGE_KONTRAST = '(prefers-contrast: more)';
|
|
34
|
+
/** @internal */
|
|
35
|
+
exports.ATTRIBUT_SCHEMA = 'data-theme';
|
|
36
|
+
/** @internal */
|
|
37
|
+
exports.ATTRIBUT_AKZENT = 'data-accent';
|
|
38
|
+
/** @internal Merges a config onto the defaults, ignoring fields left out or `undefined`. */
|
|
39
|
+
function themeEinstellung(config) {
|
|
40
|
+
const gesetzt = Object.fromEntries(Object.entries(config).filter(([, wert]) => wert !== undefined));
|
|
41
|
+
return { ...exports.THEME_STANDARD, ...gesetzt };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The script body. Written for every browser that can run the library and a
|
|
45
|
+
* few older ones (no arrow functions, no `let`), because it runs before any
|
|
46
|
+
* build step had a say. It mirrors what `ZTheme` does on start: read the stored
|
|
47
|
+
* choice, drop unknown ids, resolve `system`, write `data-theme` and, unless
|
|
48
|
+
* the accent is the default, `data-accent` onto `<html>`. Without `matchMedia`
|
|
49
|
+
* `system` resolves to `dark`, as in the service.
|
|
50
|
+
*/
|
|
51
|
+
const RUMPF = '(function(k,S,A,ds,da){' +
|
|
52
|
+
'var s=ds,a=da,d=document.documentElement,' +
|
|
53
|
+
'm=function(q,f){try{return matchMedia(q).matches}catch(e){return f}};' +
|
|
54
|
+
"try{var v=JSON.parse((k&&localStorage.getItem(k))||'null');" +
|
|
55
|
+
"if(v&&typeof v==='object'){" +
|
|
56
|
+
`if(v.scheme==='${exports.SCHEMA_SYSTEM}'||S.indexOf(v.scheme)>-1)s=v.scheme;` +
|
|
57
|
+
'if(A.indexOf(v.accent)>-1)a=v.accent}}catch(e){}' +
|
|
58
|
+
`if(s==='${exports.SCHEMA_SYSTEM}')s=S.indexOf('${exports.SCHEMA_KONTRAST}')>-1&&m('${exports.ABFRAGE_KONTRAST}',false)?'${exports.SCHEMA_KONTRAST}':` +
|
|
59
|
+
`m('${exports.ABFRAGE_DUNKEL}',true)?'${exports.SCHEMA_DUNKEL}':'${exports.SCHEMA_HELL}';` +
|
|
60
|
+
`d.setAttribute('${exports.ATTRIBUT_SCHEMA}',s);` +
|
|
61
|
+
`if(a!==da)d.setAttribute('${exports.ATTRIBUT_AKZENT}',a)` +
|
|
62
|
+
'})';
|
|
63
|
+
/**
|
|
64
|
+
* JSON that is safe inside `<script>`: `<` would close the element
|
|
65
|
+
* (`</script>`, `<!--`), U+2028 and U+2029 end a line in old engines. The
|
|
66
|
+
* pattern is built here and not at module level, so an application that never
|
|
67
|
+
* calls the function does not keep it in its bundle.
|
|
68
|
+
*/
|
|
69
|
+
function alsLiteral(wert) {
|
|
70
|
+
const heikel = new RegExp('[<' + String.fromCharCode(0x2028, 0x2029) + ']', 'g');
|
|
71
|
+
return JSON.stringify(wert).replace(heikel, (zeichen) => '\\u' + zeichen.charCodeAt(0).toString(16).padStart(4, '0'));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns the source of a small script that applies the stored or the default
|
|
75
|
+
* theme before the first paint. `ZTheme` can only do that once Angular has
|
|
76
|
+
* started, which is several frames late: a user who chose `light` would see
|
|
77
|
+
* the dark default flash on every load.
|
|
78
|
+
*
|
|
79
|
+
* Put the result into a `<script>` as early in `<head>` as possible, in front
|
|
80
|
+
* of every stylesheet, and pass the same config as to `provideZenitTheme`. The
|
|
81
|
+
* script always writes to `<html>`; with a custom `target` it is of no use.
|
|
82
|
+
* It is a pure string function: no DOM access, safe on the server and in a
|
|
83
|
+
* build script, and the result contains no `eval`.
|
|
84
|
+
*
|
|
85
|
+
* The stylesheet has to block rendering as well. The Angular CLI inlines
|
|
86
|
+
* "critical" CSS and loads the rest late; `[data-theme="light"]` is never
|
|
87
|
+
* critical, because nothing in `index.html` matches it. Set
|
|
88
|
+
* `optimization.styles.inlineCritical` to `false` in the `production`
|
|
89
|
+
* configuration of the build target; `ng add zenit-ui --themes` writes both
|
|
90
|
+
* the script and that setting.
|
|
91
|
+
*
|
|
92
|
+
* With a Content Security Policy the returned string is what you hash
|
|
93
|
+
* (`sha256`) or what goes into the `<script nonce="…">` element.
|
|
94
|
+
*
|
|
95
|
+
* @param config The same values as for `provideZenitTheme`; `target` is ignored.
|
|
96
|
+
* @returns The script body, without the `<script>` tags.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* // server.ts or a build script
|
|
101
|
+
* const head = `<script nonce="${nonce}">${zenitThemeInitScript({ defaultScheme: 'system' })}</script>`;
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
function zenitThemeInitScript(config = {}) {
|
|
105
|
+
const e = themeEinstellung(config);
|
|
106
|
+
const argumente = [e.storageKey, e.schemes, e.accents, e.defaultScheme, e.defaultAccent].map(alsLiteral);
|
|
107
|
+
return `${RUMPF}(${argumente.join(',')})`;
|
|
108
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "SchematicsZenitUiNgAdd",
|
|
4
|
+
"title": "zenit-ui ng-add schema",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"project": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Name of the application the library is wired into. Without it the workspace must have exactly one application.",
|
|
10
|
+
"$default": {
|
|
11
|
+
"$source": "projectName"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"themes": {
|
|
15
|
+
"type": "boolean",
|
|
16
|
+
"default": false,
|
|
17
|
+
"description": "Register zenit-ui/styles/themes.css and wire the theme without a flash: init script in index.html, provideZenitTheme() in the application config, inlineCritical off for production.",
|
|
18
|
+
"x-prompt": "Set up the colour schemes (theme stylesheet, init script, provideZenitTheme)?"
|
|
19
|
+
},
|
|
20
|
+
"fonts": {
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"default": true,
|
|
23
|
+
"description": "Add the self-hosted fonts (Material Icons, Inter, Space Grotesk, JetBrains Mono).",
|
|
24
|
+
"x-prompt": "Add the self-hosted fonts?"
|
|
25
|
+
},
|
|
26
|
+
"toastOutlet": {
|
|
27
|
+
"type": "boolean",
|
|
28
|
+
"default": true,
|
|
29
|
+
"description": "Mount <z-toast-outlet /> in the root component.",
|
|
30
|
+
"x-prompt": "Mount the toast outlet in the root component?"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"additionalProperties": false
|
|
34
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* Package daten: Metric, ServerList, Table and Pagination.
|
|
2
|
+
Verbatim from spec/components/bundle.css. */
|
|
3
|
+
|
|
4
|
+
/* Metric */
|
|
5
|
+
.z-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
|
|
6
|
+
.z-metric { display: grid; gap: var(--space-1); padding: var(--space-4); border-left: 1px solid var(--border); min-width: 0; }
|
|
7
|
+
.z-metric:first-child { border-left: 0; }
|
|
8
|
+
.z-metric__label { font-size: 12px; line-height: 16px; color: var(--text-muted); }
|
|
9
|
+
.z-metric__value { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 20px; line-height: 28px; font-weight: 600; }
|
|
10
|
+
.z-metric__value small { font-size: 12px; font-weight: 400; color: var(--text-subtle); }
|
|
11
|
+
.z-metric__sub { font-size: 12px; line-height: 16px; color: var(--text-subtle); }
|
|
12
|
+
.z-meter { height: 4px; border-radius: var(--radius-full); background: var(--surface-hover); overflow: hidden; }
|
|
13
|
+
.z-meter__fill { display: block; height: 100%; background: var(--text-muted); }
|
|
14
|
+
.z-meter--warning .z-meter__fill { background: var(--warning); }
|
|
15
|
+
.z-meter--danger .z-meter__fill { background: var(--danger); }
|
|
16
|
+
|
|
17
|
+
/* ServerList */
|
|
18
|
+
.z-rows { display: grid; }
|
|
19
|
+
.z-rows__head, .z-row {
|
|
20
|
+
display: grid; align-items: center; gap: var(--space-4);
|
|
21
|
+
grid-template-columns: var(--z-cols, minmax(0, 2fr) 128px minmax(0, 1fr) 96px 20px);
|
|
22
|
+
padding: var(--space-3) var(--space-4);
|
|
23
|
+
}
|
|
24
|
+
.z-rows__head { font-size: 12px; line-height: 16px; color: var(--text-subtle); border-bottom: 1px solid var(--border); padding-top: var(--space-2); padding-bottom: var(--space-2); }
|
|
25
|
+
.z-row { border-top: 1px solid var(--border); color: inherit; text-decoration: none; cursor: pointer; }
|
|
26
|
+
.z-root a.z-row { color: inherit; }
|
|
27
|
+
.z-root a.z-row:hover { text-decoration: none; }
|
|
28
|
+
.z-rows__head + .z-row { border-top: 0; }
|
|
29
|
+
.z-row:hover { background: var(--surface-raised); }
|
|
30
|
+
.z-row__main { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
|
|
31
|
+
.z-row__thumb { width: 32px; height: 32px; border-radius: var(--radius-sm); background: var(--surface-hover); flex: none; display: grid; place-items: center; font-family: var(--font-display); font-weight: 700; color: var(--text-muted); }
|
|
32
|
+
.z-row__title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
33
|
+
.z-row__meta { font-size: 12px; line-height: 16px; color: var(--text-subtle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
34
|
+
.z-row__num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
|
35
|
+
@media (max-width: 640px) {
|
|
36
|
+
.z-rows__head { display: none; }
|
|
37
|
+
.z-row { grid-template-columns: minmax(0, 1fr) auto; row-gap: var(--space-1); }
|
|
38
|
+
.z-row > :nth-child(n+3) { display: none; }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Table */
|
|
42
|
+
.z-table-wrap { overflow-x: auto; }
|
|
43
|
+
.z-table { width: 100%; border-collapse: collapse; }
|
|
44
|
+
.z-table th { padding: var(--space-2) var(--space-4); border-bottom: 1px solid var(--border); text-align: left; font-size: 12px; line-height: 16px; font-weight: 400; color: var(--text-subtle); white-space: nowrap; }
|
|
45
|
+
.z-table td { padding: var(--space-3) var(--space-4); border-top: 1px solid var(--border); white-space: nowrap; }
|
|
46
|
+
.z-table tbody tr:first-child td { border-top: 0; }
|
|
47
|
+
.z-table tbody tr:hover td { background: var(--surface-raised); }
|
|
48
|
+
.z-table .z-table__num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text-muted); }
|
|
49
|
+
.z-table__name { display: flex; align-items: center; gap: var(--space-3); font-family: var(--font-mono); }
|
|
50
|
+
.z-table__check { width: 18px; }
|
|
51
|
+
|
|
52
|
+
/* Pagination */
|
|
53
|
+
.z-pager { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-3); padding: var(--space-2) var(--space-4); border-top: 1px solid var(--border); font-size: 12px; line-height: 16px; color: var(--text-muted); }
|
|
54
|
+
.z-pager__nav { display: flex; align-items: center; gap: var(--space-1); }
|
|
55
|
+
|
|
56
|
+
/* ---------- Angular hosts and additions of the library ---------- */
|
|
57
|
+
|
|
58
|
+
.z-table-wrap, z-pagination { display: block; }
|
|
59
|
+
.z-rows__head > *, .z-row > * { min-width: 0; }
|
|
60
|
+
/* Replaces the inline style="min-width:0" of the preview on the text block of the row. */
|
|
61
|
+
.z-row__text { min-width: 0; }
|
|
62
|
+
/* Like .z-game__cover img in bundle.css: the image fills the area of the thumb. */
|
|
63
|
+
.z-row__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
64
|
+
/* Addition to the reference: div[zRow] is not clickable. */
|
|
65
|
+
.z-root div.z-row { cursor: auto; }
|
|
66
|
+
|
|
67
|
+
/* Addition to the reference: the stretched link. 40-bibliothek.md asks for rows
|
|
68
|
+
that are links and for actions inside a row as their own tab stops, and a
|
|
69
|
+
<button> inside an <a> is invalid markup. So the row stays a div, the link
|
|
70
|
+
sits on the title, and its ::after covers the row: one link, one tab stop,
|
|
71
|
+
the whole row clickable. The overlay escapes the overflow of .z-row__title
|
|
72
|
+
because its containing block is the row. No transform, only tokens. */
|
|
73
|
+
.z-row:has(.z-row__link) { position: relative; }
|
|
74
|
+
.z-root div.z-row:has(.z-row__link) { cursor: pointer; }
|
|
75
|
+
.z-root a.z-row__link, .z-root a.z-row__link:hover { color: inherit; text-decoration: none; }
|
|
76
|
+
.z-row__link::after { content: ""; position: absolute; inset: 0; }
|
|
77
|
+
.z-row__action { position: relative; z-index: 1; }
|
|
78
|
+
/* The hit area of the link is the whole row, so that is where its focus ring
|
|
79
|
+
belongs: the 2px/2px ring in focus is drawn on the stretched ::after, and the
|
|
80
|
+
ring around the title text alone is turned off so there are not two. */
|
|
81
|
+
.z-root .z-row__link:focus-visible { outline-color: transparent; }
|
|
82
|
+
.z-root .z-row__link:focus-visible::after { outline: 2px solid var(--focus); outline-offset: 2px; }
|
|
83
|
+
|
|
84
|
+
/* Addition to the reference: the action of a row stays reachable on a phone.
|
|
85
|
+
The reference hides every cell from the third on below 640px, and the menu
|
|
86
|
+
button of a row is one of them, so copying, restarting and deleting would be
|
|
87
|
+
unreachable on a phone while the touch-target check still passed, because a
|
|
88
|
+
hidden button is never measured. The action keeps its cell and the row gets a
|
|
89
|
+
third column for it; a row without an action keeps the two columns of the
|
|
90
|
+
reference. inline-flex is the display an action carries at every other width,
|
|
91
|
+
because [zRowAction] sits on a button. */
|
|
92
|
+
@media (max-width: 640px) {
|
|
93
|
+
.z-row > .z-row__action { display: inline-flex; }
|
|
94
|
+
.z-row:has(> .z-row__action) { grid-template-columns: minmax(0, 1fr) auto auto; }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Addition to the reference: the page size select in the pager. The reference
|
|
98
|
+
fixes 25 entries and has no picker; every paginator of the application has
|
|
99
|
+
one, so z-pagination renders one as soon as pageSizeOptions is set. Label and
|
|
100
|
+
select stand on one line and form a flex item of .z-pager, which already
|
|
101
|
+
wraps. The select itself is the z-select shell in size sm, so it takes the
|
|
102
|
+
40px height below 640px from _grundlage.css together with every other small
|
|
103
|
+
control. Below 640px the group takes the full width and therefore a row of
|
|
104
|
+
its own, so the arrows keep theirs at 360px. */
|
|
105
|
+
.z-pager__size { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
|
|
106
|
+
@media (max-width: 640px) {
|
|
107
|
+
.z-pager__size { width: 100%; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Addition to the reference: sortable column headers. FileTable/README.md asks
|
|
111
|
+
for "Sortieren über Klick auf den Spaltenkopf", bundle.css has no rule for
|
|
112
|
+
it. The button is a reset control that inherits the caption style of the
|
|
113
|
+
.z-table th rule, so a sortable header looks exactly like a plain one until
|
|
114
|
+
it is sorted; then the header text steps up from text-subtle to text. The
|
|
115
|
+
focus ring is the global one. */
|
|
116
|
+
.z-table__sort {
|
|
117
|
+
display: inline-flex; align-items: center; gap: var(--space-2);
|
|
118
|
+
padding: 0; border: 0; background: none; color: inherit; font: inherit;
|
|
119
|
+
text-align: inherit; cursor: pointer;
|
|
120
|
+
}
|
|
121
|
+
.z-table__sort:enabled:hover { color: var(--text); }
|
|
122
|
+
.z-table__sort:disabled { cursor: default; }
|
|
123
|
+
.z-table__sort .z-icon { color: var(--text-muted); }
|
|
124
|
+
/* [zNum] on a header cell borrows the right alignment of the data cells only.
|
|
125
|
+
The mono face and text-muted belong to the values below it, the header keeps
|
|
126
|
+
the caption style of the reference. The button follows the text-align of the
|
|
127
|
+
cell, and the arrow moves in front of the text, so the right edge of the
|
|
128
|
+
column stays the edge of the header text. */
|
|
129
|
+
.z-table th.z-table__num { font-family: inherit; color: var(--text-subtle); }
|
|
130
|
+
.z-table th.z-table__num .z-icon { order: -1; }
|
|
131
|
+
.z-table th[aria-sort="ascending"], .z-table th[aria-sort="descending"] { color: var(--text); }
|
|
132
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
133
|
+
.z-table__sort { transition: color 150ms; }
|
|
134
|
+
}
|
|
135
|
+
/* Click targets at least 40px on mobile (CLAUDE.md, Abstand und Layout). */
|
|
136
|
+
@media (max-width: 640px) {
|
|
137
|
+
.z-table__sort { min-height: var(--control-md); }
|
|
138
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* Package formulare: Checkbox, Toggle, Setting, Segment, Slider.
|
|
2
|
+
Verbatim from spec/components/bundle.css. */
|
|
3
|
+
|
|
4
|
+
/* Checkbox, Toggle */
|
|
5
|
+
.z-check { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
|
|
6
|
+
.z-check input {
|
|
7
|
+
appearance: none; -webkit-appearance: none; flex: none; margin: 0;
|
|
8
|
+
width: 18px; height: 18px; border: 1.5px solid var(--border-control); border-radius: var(--radius-sm);
|
|
9
|
+
background: var(--surface); cursor: pointer; position: relative;
|
|
10
|
+
}
|
|
11
|
+
.z-check input:hover { border-color: var(--text-muted); }
|
|
12
|
+
.z-check input:checked { background: var(--accent); border-color: var(--accent); }
|
|
13
|
+
.z-check input:checked::after {
|
|
14
|
+
content: ""; position: absolute; left: 5px; top: 1.5px; width: 4px; height: 9px;
|
|
15
|
+
border-right: 2px solid var(--on-accent); border-bottom: 2px solid var(--on-accent); transform: rotate(45deg);
|
|
16
|
+
}
|
|
17
|
+
.z-check input:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
18
|
+
.z-toggle {
|
|
19
|
+
appearance: none; -webkit-appearance: none; flex: none; margin: 0; cursor: pointer; position: relative;
|
|
20
|
+
width: 36px; height: 20px; border: 1px solid var(--border-control); border-radius: var(--radius-full);
|
|
21
|
+
background: var(--surface);
|
|
22
|
+
}
|
|
23
|
+
.z-toggle::after {
|
|
24
|
+
content: ""; position: absolute; left: 2px; top: 2px; width: 14px; height: 14px;
|
|
25
|
+
border-radius: var(--radius-full); background: var(--text-muted);
|
|
26
|
+
}
|
|
27
|
+
.z-toggle:checked { background: var(--success); border-color: var(--success); }
|
|
28
|
+
.z-toggle:checked::after { left: 18px; background: var(--on-mc); }
|
|
29
|
+
.z-toggle:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
30
|
+
.z-setting { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: var(--space-3) 0; border-top: 1px solid var(--border); }
|
|
31
|
+
.z-setting:first-child { border-top: 0; }
|
|
32
|
+
.z-setting__text { display: grid; gap: 2px; min-width: 0; }
|
|
33
|
+
.z-setting__title { font-weight: 600; }
|
|
34
|
+
|
|
35
|
+
/* Segment (from the section "Tabs, Segment") */
|
|
36
|
+
.z-segment { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--border); border-radius: var(--radius-md); }
|
|
37
|
+
.z-segment button {
|
|
38
|
+
height: 28px; padding: 0 var(--space-3); border: 0; border-radius: var(--radius-sm); background: none; cursor: pointer;
|
|
39
|
+
color: var(--text-muted); font-size: 12px; line-height: 16px; font-weight: 500;
|
|
40
|
+
}
|
|
41
|
+
.z-segment button:hover { color: var(--text); }
|
|
42
|
+
.z-segment button[aria-pressed="true"] { background: var(--surface-hover); color: var(--text); }
|
|
43
|
+
|
|
44
|
+
/* Slider */
|
|
45
|
+
.z-range { display: grid; gap: var(--space-2); }
|
|
46
|
+
.z-range__head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3); }
|
|
47
|
+
.z-range__value { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 20px; line-height: 28px; font-weight: 600; }
|
|
48
|
+
.z-range input[type="range"] { appearance: none; -webkit-appearance: none; width: 100%; height: 20px; margin: 0; background: none; cursor: pointer; }
|
|
49
|
+
.z-range input[type="range"]::-webkit-slider-runnable-track { height: 4px; border-radius: var(--radius-full); background: var(--border-control); }
|
|
50
|
+
.z-range input[type="range"]::-moz-range-track { height: 4px; border-radius: var(--radius-full); background: var(--border-control); }
|
|
51
|
+
.z-range input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; margin-top: -7px; border: 0; border-radius: var(--radius-full); background: var(--text); }
|
|
52
|
+
.z-range input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border: 0; border-radius: var(--radius-full); background: var(--text); }
|
|
53
|
+
.z-range__ticks { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: 12px; line-height: 16px; color: var(--text-subtle); }
|
|
54
|
+
|
|
55
|
+
/* ---------- Angular hosts and additions of the library ---------- */
|
|
56
|
+
|
|
57
|
+
z-checkbox, z-toggle { display: inline-flex; }
|
|
58
|
+
z-slider { display: block; }
|
|
59
|
+
/* A checkbox without visible text (aria-label, for example in a table row)
|
|
60
|
+
has an empty span. Without this rule the gap would remain. */
|
|
61
|
+
.z-check span:empty { display: none; }
|
|
62
|
+
/* Addition to the reference: bundle.css has no mixed state for "select all".
|
|
63
|
+
A bar in on-accent on accent, with the stroke (2px) and the length (9px) of
|
|
64
|
+
the check mark, centred in the same box. It wins over :checked, the way the
|
|
65
|
+
native indeterminate property does. */
|
|
66
|
+
.z-check input:indeterminate { background: var(--accent); border-color: var(--accent); }
|
|
67
|
+
.z-check input:indeterminate::after {
|
|
68
|
+
content: ""; position: absolute; inset: 0; margin: auto; width: 9px; height: 2px;
|
|
69
|
+
border: 0; background: var(--on-accent); transform: none;
|
|
70
|
+
}
|
|
71
|
+
/* Disabled like checkbox, toggle and button in bundle.css. The hover holds
|
|
72
|
+
its colour, and the chosen view keeps its text along with it. */
|
|
73
|
+
.z-segment button:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
74
|
+
.z-segment button:disabled:not([aria-pressed="true"]):hover { color: var(--text-muted); }
|
|
75
|
+
.z-range input[type="range"]:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
76
|
+
/* Addition to the reference: Slider/README.md asks for plus and minus buttons
|
|
77
|
+
beyond twelve steps, bundle.css has no markup for them. One modifier on the
|
|
78
|
+
host; the two buttons are icon-only ghost buttons and keep their look from
|
|
79
|
+
.z-btn, siblings of the range input, and the track takes whatever width is
|
|
80
|
+
left. Without steppers there is neither the class nor the buttons, and the
|
|
81
|
+
slider renders exactly as before. */
|
|
82
|
+
.z-range--steppers { grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--space-3); align-items: center; }
|
|
83
|
+
.z-range--steppers > .z-range__head,
|
|
84
|
+
.z-range--steppers > .z-range__ticks,
|
|
85
|
+
.z-range--steppers > .z-field__hint { grid-column: 1 / -1; }
|
|
86
|
+
|
|
87
|
+
/* Click targets at least 40px on mobile (acceptance A9). The measurements
|
|
88
|
+
from the previews stay visible, only the hit area grows. */
|
|
89
|
+
@media (max-width: 640px) {
|
|
90
|
+
.z-check { min-height: var(--control-md); }
|
|
91
|
+
.z-segment button { height: var(--control-md); }
|
|
92
|
+
.z-range input[type="range"] { height: var(--control-md); }
|
|
93
|
+
.z-toggle::before {
|
|
94
|
+
content: ""; position: absolute; left: 0; right: 0; top: 50%;
|
|
95
|
+
height: var(--control-md); transform: translateY(-50%);
|
|
96
|
+
}
|
|
97
|
+
}
|