@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.
Files changed (36) hide show
  1. package/README.md +400 -0
  2. package/fesm2022/zenit-hosting-zenit-ui.mjs +11289 -0
  3. package/fesm2022/zenit-hosting-zenit-ui.mjs.map +1 -0
  4. package/llms-full.txt +5120 -0
  5. package/llms.txt +179 -0
  6. package/package.json +44 -0
  7. package/schematics/collection.json +15 -0
  8. package/schematics/migrate-material/component.js +241 -0
  9. package/schematics/migrate-material/index.js +463 -0
  10. package/schematics/migrate-material/report.js +125 -0
  11. package/schematics/migrate-material/schema.js +2 -0
  12. package/schematics/migrate-material/schema.json +32 -0
  13. package/schematics/migrate-material/tables.js +138 -0
  14. package/schematics/migrate-material/template.js +871 -0
  15. package/schematics/ng-add/html.js +151 -0
  16. package/schematics/ng-add/index.js +609 -0
  17. package/schematics/ng-add/init-script.js +108 -0
  18. package/schematics/ng-add/schema.js +2 -0
  19. package/schematics/ng-add/schema.json +34 -0
  20. package/schematics/package.json +3 -0
  21. package/styles/_daten.css +138 -0
  22. package/styles/_formulare.css +97 -0
  23. package/styles/_grundlage.css +424 -0
  24. package/styles/_konfigurator.css +216 -0
  25. package/styles/_navigation.css +179 -0
  26. package/styles/_overlays.css +86 -0
  27. package/styles/_rueckmeldung.css +100 -0
  28. package/styles/_werkzeuge.css +119 -0
  29. package/styles/themes/accents.css +91 -0
  30. package/styles/themes/base.css +26 -0
  31. package/styles/themes/contrast.css +67 -0
  32. package/styles/themes/light.css +74 -0
  33. package/styles/themes.css +21 -0
  34. package/styles/tokens.css +76 -0
  35. package/styles/zenit-ui.css +12 -0
  36. 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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -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
+ }