@tensor_1/tokens 2.35.0 → 2.37.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.
@@ -96,6 +96,12 @@
96
96
  --uix-scrollbar-track: transparent;
97
97
  --uix-scrim: rgba(0,0,0,.5);
98
98
  --uix-scrim-strong: rgba(0,0,0,.7);
99
+ --uix-print-ink: #0A0A0A;
100
+ --uix-print-ink-quiet: #525252;
101
+ --uix-print-paper: #FFFFFF;
102
+ --uix-print-paper-quiet: #F0F0F0;
103
+ --uix-print-line: #8A8A8A;
104
+ --uix-print-danger: #C40012;
99
105
  --uix-shadow-sm: 0 1px 2px rgba(0,0,0,.06);
100
106
  --uix-shadow-md: 0 4px 12px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.05);
101
107
  --uix-shadow-popover: 0 8px 24px rgba(0,0,0,.10),0 1px 2px rgba(0,0,0,.05);
@@ -97,6 +97,12 @@ module.exports = {
97
97
  "uix-scrollbar-track": "var(--uix-scrollbar-track)",
98
98
  "uix-scrim": "var(--uix-scrim)",
99
99
  "uix-scrim-strong": "var(--uix-scrim-strong)",
100
+ "uix-print-ink": "var(--uix-print-ink)",
101
+ "uix-print-ink-quiet": "var(--uix-print-ink-quiet)",
102
+ "uix-print-paper": "var(--uix-print-paper)",
103
+ "uix-print-paper-quiet": "var(--uix-print-paper-quiet)",
104
+ "uix-print-line": "var(--uix-print-line)",
105
+ "uix-print-danger": "var(--uix-print-danger)",
100
106
  },
101
107
  borderRadius: {
102
108
  "uix-xs": "var(--uix-radius-xs)",
@@ -94,6 +94,12 @@
94
94
  --color-uix-scrollbar-track: var(--uix-scrollbar-track);
95
95
  --color-uix-scrim: var(--uix-scrim);
96
96
  --color-uix-scrim-strong: var(--uix-scrim-strong);
97
+ --color-uix-print-ink: var(--uix-print-ink);
98
+ --color-uix-print-ink-quiet: var(--uix-print-ink-quiet);
99
+ --color-uix-print-paper: var(--uix-print-paper);
100
+ --color-uix-print-paper-quiet: var(--uix-print-paper-quiet);
101
+ --color-uix-print-line: var(--uix-print-line);
102
+ --color-uix-print-danger: var(--uix-print-danger);
97
103
 
98
104
  --radius-uix-xs: var(--uix-radius-xs);
99
105
  --radius-uix-sm: var(--uix-radius-sm);
@@ -1,5 +1,5 @@
1
1
  /* GENERATED — do not edit. */
2
- export type UixTokenName = "bg-app" | "bg-subtle" | "bg-hover" | "bg-active" | "surface" | "surface-2" | "surface-3" | "border" | "border-strong" | "text" | "text-hushed" | "text-muted" | "text-reverse" | "text-hushed-reverse" | "accent" | "accent-dark" | "accent-light" | "accent-hover" | "accent-fg" | "link" | "accent-text" | "ring" | "brand-muted" | "success" | "success-fg" | "success-bg" | "success-text" | "warning" | "warning-fg" | "warning-bg" | "warning-text" | "warning-border" | "info" | "info-fg" | "info-bg" | "info-text" | "danger" | "danger-fg" | "danger-bg" | "danger-border" | "danger-text" | "amber" | "amber-text" | "amber-bg" | "attention" | "attention-bg" | "attention-text" | "attention-solid" | "overdue" | "overdue-bg" | "overdue-text" | "overdue-solid" | "sev-4" | "sev-4-bg" | "sev-4-text" | "sev-5" | "sev-5-bg" | "sev-5-text" | "success-solid" | "warning-solid" | "info-solid" | "danger-solid" | "neutral-solid" | "neutral-text" | "neutral-bg" | "neutral-border" | "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "chart-6" | "chart-7" | "chart-8" | "chart-neutral" | "chart-grid" | "chart-axis" | "chart-reference" | "chart-zone-warning" | "chart-zone-danger" | "chart-forecast-band" | "chart-event" | "chart-partial" | "chart-comparison" | "row" | "row-alt" | "row-pinned-bg" | "row-selected-bg" | "scrollbar-thumb" | "scrollbar-thumb-hover" | "scrollbar-track" | "scrim" | "scrim-strong" | "shadow-sm" | "shadow-md" | "shadow-popover" | "shadow-overlay" | "highlight-top" | "ease-out" | "ease-out-strong" | "ease-in" | "ease-in-out" | "ease-spring" | "dur-fast" | "dur" | "dur-slow" | "dur-slower" | "lift" | "z-raised" | "z-sticky" | "z-dropdown" | "z-overlay" | "z-tooltip" | "z-toast" | "radius-xs" | "radius-sm" | "radius-md" | "radius-lg" | "radius-xl" | "radius-pill" | "row-h" | "row-h-compact" | "row-h-comfortable" | "control-h" | "sidebar-w" | "sidebar-w-rail" | "topbar-h" | "scrollbar-size" | "icon-sm" | "icon-md" | "icon-lg" | "peek-w" | "col-w-xs" | "col-w-sm" | "col-w-md" | "col-w-lg" | "col-w-xl" | "col-title-min" | "space-0" | "space-1" | "space-2" | "space-3" | "space-4" | "space-5" | "space-6" | "space-7" | "space-8" | "space-9" | "space-10" | "space-11" | "space-12" | "font-sans" | "font-heading" | "font-mono" | "text-display" | "leading-display" | "text-data-hero" | "leading-data-hero" | "text-h2" | "leading-h2" | "text-h3" | "leading-h3" | "text-body" | "leading-body" | "text-meta" | "leading-meta" | "text-eyebrow" | "leading-eyebrow" | "tracking-tight" | "tracking-eyebrow";
2
+ export type UixTokenName = "bg-app" | "bg-subtle" | "bg-hover" | "bg-active" | "surface" | "surface-2" | "surface-3" | "border" | "border-strong" | "text" | "text-hushed" | "text-muted" | "text-reverse" | "text-hushed-reverse" | "accent" | "accent-dark" | "accent-light" | "accent-hover" | "accent-fg" | "link" | "accent-text" | "ring" | "brand-muted" | "success" | "success-fg" | "success-bg" | "success-text" | "warning" | "warning-fg" | "warning-bg" | "warning-text" | "warning-border" | "info" | "info-fg" | "info-bg" | "info-text" | "danger" | "danger-fg" | "danger-bg" | "danger-border" | "danger-text" | "amber" | "amber-text" | "amber-bg" | "attention" | "attention-bg" | "attention-text" | "attention-solid" | "overdue" | "overdue-bg" | "overdue-text" | "overdue-solid" | "sev-4" | "sev-4-bg" | "sev-4-text" | "sev-5" | "sev-5-bg" | "sev-5-text" | "success-solid" | "warning-solid" | "info-solid" | "danger-solid" | "neutral-solid" | "neutral-text" | "neutral-bg" | "neutral-border" | "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "chart-6" | "chart-7" | "chart-8" | "chart-neutral" | "chart-grid" | "chart-axis" | "chart-reference" | "chart-zone-warning" | "chart-zone-danger" | "chart-forecast-band" | "chart-event" | "chart-partial" | "chart-comparison" | "row" | "row-alt" | "row-pinned-bg" | "row-selected-bg" | "scrollbar-thumb" | "scrollbar-thumb-hover" | "scrollbar-track" | "scrim" | "scrim-strong" | "print-ink" | "print-ink-quiet" | "print-paper" | "print-paper-quiet" | "print-line" | "print-danger" | "shadow-sm" | "shadow-md" | "shadow-popover" | "shadow-overlay" | "highlight-top" | "ease-out" | "ease-out-strong" | "ease-in" | "ease-in-out" | "ease-spring" | "dur-fast" | "dur" | "dur-slow" | "dur-slower" | "lift" | "z-raised" | "z-sticky" | "z-dropdown" | "z-overlay" | "z-tooltip" | "z-toast" | "radius-xs" | "radius-sm" | "radius-md" | "radius-lg" | "radius-xl" | "radius-pill" | "row-h" | "row-h-compact" | "row-h-comfortable" | "control-h" | "sidebar-w" | "sidebar-w-rail" | "topbar-h" | "scrollbar-size" | "icon-sm" | "icon-md" | "icon-lg" | "peek-w" | "col-w-xs" | "col-w-sm" | "col-w-md" | "col-w-lg" | "col-w-xl" | "col-title-min" | "space-0" | "space-1" | "space-2" | "space-3" | "space-4" | "space-5" | "space-6" | "space-7" | "space-8" | "space-9" | "space-10" | "space-11" | "space-12" | "font-sans" | "font-heading" | "font-mono" | "text-display" | "leading-display" | "text-data-hero" | "leading-data-hero" | "text-h2" | "leading-h2" | "text-h3" | "leading-h3" | "text-body" | "leading-body" | "text-meta" | "leading-meta" | "text-eyebrow" | "leading-eyebrow" | "tracking-tight" | "tracking-eyebrow";
3
3
  export declare const cssVar: Record<UixTokenName, string>;
4
4
  export declare const light: Record<UixTokenName, string>;
5
5
  export declare const dark: Partial<Record<UixTokenName, string>>;
@@ -97,6 +97,12 @@ export const cssVar = {
97
97
  "scrollbar-track": "var(--uix-scrollbar-track)",
98
98
  "scrim": "var(--uix-scrim)",
99
99
  "scrim-strong": "var(--uix-scrim-strong)",
100
+ "print-ink": "var(--uix-print-ink)",
101
+ "print-ink-quiet": "var(--uix-print-ink-quiet)",
102
+ "print-paper": "var(--uix-print-paper)",
103
+ "print-paper-quiet": "var(--uix-print-paper-quiet)",
104
+ "print-line": "var(--uix-print-line)",
105
+ "print-danger": "var(--uix-print-danger)",
100
106
  "shadow-sm": "var(--uix-shadow-sm)",
101
107
  "shadow-md": "var(--uix-shadow-md)",
102
108
  "shadow-popover": "var(--uix-shadow-popover)",
@@ -270,6 +276,12 @@ export const light = {
270
276
  "scrollbar-track": "transparent",
271
277
  "scrim": "rgba(0,0,0,.5)",
272
278
  "scrim-strong": "rgba(0,0,0,.7)",
279
+ "print-ink": "#0A0A0A",
280
+ "print-ink-quiet": "#525252",
281
+ "print-paper": "#FFFFFF",
282
+ "print-paper-quiet": "#F0F0F0",
283
+ "print-line": "#8A8A8A",
284
+ "print-danger": "#C40012",
273
285
  "shadow-sm": "0 1px 2px rgba(0,0,0,.06)",
274
286
  "shadow-md": "0 4px 12px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.05)",
275
287
  "shadow-popover": "0 8px 24px rgba(0,0,0,.10),0 1px 2px rgba(0,0,0,.05)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensor_1/tokens",
3
- "version": "2.35.0",
3
+ "version": "2.37.0",
4
4
  "description": "UIx v2 design tokens — the --uix-* contract for house products (Tensor, POSx, SHOPx). One DTCG source -> CSS variables, Tailwind theme, and typed TS constants.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -13,6 +13,12 @@
13
13
  .uix-avatar--xl .uix-avatar__status { width: 14px; height: 14px; right: 1px; bottom: 1px; }
14
14
  .uix-avatar--2xl .uix-avatar__status { width: 18px; height: 18px; border-width: 3px; right: 4px; bottom: 4px; }
15
15
  .uix-avatar__status { position: absolute; right: -1px; bottom: -1px; width: 10px; height: 10px; border-radius: 50%; border: 2px solid var(--uix-surface); background: var(--uix-success); }
16
+ /* The dot sits on the circle's edge, so the circle must not clip it: it used to, and a quarter
17
+ of the dot was all that showed (HAR-1374). Only an avatar with a dot stops clipping; its
18
+ photo and its edit overlay keep the round shape themselves. */
19
+ .uix-avatar:has(> .uix-avatar__status) { overflow: visible; }
20
+ .uix-avatar:has(> .uix-avatar__status) > img, .uix-avatar__edit { border-radius: 50%; }
21
+ .uix-avatar--lg .uix-avatar__status { right: 1px; bottom: 1px; }
16
22
  .uix-avatar-group { display: inline-flex; }
17
23
  .uix-avatar-group .uix-avatar { box-shadow: 0 0 0 2px var(--uix-surface); margin-left: calc(-1 * var(--uix-space-2)); }
18
24
  .uix-avatar-group .uix-avatar:first-child { margin-left: 0; }
@@ -27,3 +33,17 @@
27
33
  .uix-avatar--editable:hover .uix-avatar__edit, .uix-avatar--editable:focus-visible .uix-avatar__edit { opacity: 1; }
28
34
  .uix-avatar--empty { background: var(--uix-bg-subtle); border: 1.5px dashed var(--uix-border-strong); color: var(--uix-text-muted); }
29
35
  }
36
+
37
+ /* Presence (HAR-1374): online, busy, away, offline. Each state has its own SHAPE as well as its
38
+ own colour (a filled dot, a dot with a bar, two rings), and the React components add the state
39
+ in text. The same four looks serve the dot on an avatar and the standalone .uix-presence. The
40
+ ring colours are text colours on purpose: the status hues are too light for a 2 px ring
41
+ (packages/tokens/tests/presence-contrast.test.mjs holds all four to 3:1 on the surface). */
42
+ @layer uix.components {
43
+ .uix-presence { position: relative; display: inline-block; flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--uix-success); }
44
+ .uix-avatar__status::after, .uix-presence::after { content: ""; position: absolute; left: 20%; right: 20%; top: calc(50% - 1px); height: 2px; border-radius: var(--uix-radius-pill); background: var(--uix-surface); display: none; }
45
+ .uix-avatar__status[data-presence="busy"], .uix-presence--busy { background: var(--uix-danger); }
46
+ .uix-avatar__status[data-presence="busy"]::after, .uix-presence--busy::after { display: block; }
47
+ .uix-avatar__status[data-presence="away"], .uix-presence--away { background: var(--uix-surface); box-shadow: inset 0 0 0 2px var(--uix-warning-text); }
48
+ .uix-avatar__status[data-presence="offline"], .uix-presence--offline { background: var(--uix-surface); box-shadow: inset 0 0 0 2px var(--uix-text-muted); }
49
+ }
@@ -0,0 +1,72 @@
1
+ /* uix date fields — DatePicker, DateTimePicker and the field mode of DateRangePicker (HAR-1383).
2
+ The day cells are the range picker's (date-range-picker.css); this file adds the field, the
3
+ popover's one-month calendar and the single selected day. Depends only on --uix-* tokens. */
4
+ @layer uix.components {
5
+ /* --- DatePicker: a text input with a calendar button inside its trailing edge ------------- */
6
+ .uix-date-picker { display: grid; gap: var(--uix-space-1); width: 100%; min-width: 0; max-width: 11rem; }
7
+ .uix-date-picker__control { position: relative; display: flex; min-width: 0; }
8
+ .uix-date-picker__input { padding-inline-end: calc(var(--uix-control-h) + var(--uix-space-1)); font-variant-numeric: tabular-nums; }
9
+ .uix-date-picker--sm .uix-date-picker__input { padding-inline-end: var(--uix-space-7); }
10
+ .uix-date-picker__toggle {
11
+ /* Half a space step (2 px) inside the field's border, on every side it touches. */
12
+ --date-picker-inset: calc(var(--uix-space-1) / 2);
13
+ position: absolute; inset-block: var(--date-picker-inset); inset-inline-end: var(--date-picker-inset); aspect-ratio: 1;
14
+ display: inline-flex; align-items: center; justify-content: center; padding: 0;
15
+ border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted); cursor: pointer;
16
+ transition: background-color var(--uix-dur-fast) var(--uix-ease-out), color var(--uix-dur-fast) var(--uix-ease-out);
17
+ }
18
+ .uix-date-picker__toggle:hover:not(:disabled),
19
+ .uix-date-picker__toggle[aria-expanded="true"] { background: var(--uix-bg-hover); color: var(--uix-text); }
20
+ .uix-date-picker__toggle:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 0; }
21
+ .uix-date-picker__toggle:disabled { cursor: not-allowed; opacity: .55; }
22
+ .uix-date-picker__error { min-width: 0; overflow-wrap: anywhere; }
23
+
24
+ /* --- The calendar in the popover -------------------------------------------------------- */
25
+ .uix-date-picker__popover { padding: var(--uix-space-3); }
26
+ .uix-date-picker__calendar { display: grid; gap: var(--uix-space-2); width: 17rem; max-width: calc(100vw - 2 * var(--uix-space-6)); color: var(--uix-text); }
27
+ .uix-date-picker__nav { display: flex; align-items: center; justify-content: space-between; gap: var(--uix-space-2); }
28
+ .uix-date-picker__month { font-weight: 600; }
29
+ .uix-date-picker__nav-btn {
30
+ display: inline-flex; align-items: center; justify-content: center; flex: none; width: 28px; height: 28px; padding: 0;
31
+ border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted); cursor: pointer;
32
+ }
33
+ .uix-date-picker__nav-btn:hover:not([aria-disabled="true"]) { background: var(--uix-bg-hover); color: var(--uix-text); }
34
+ .uix-date-picker__nav-btn:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
35
+ .uix-date-picker__nav-btn[aria-disabled="true"] { opacity: .4; cursor: not-allowed; }
36
+ .uix-date-picker__nav-btn:dir(rtl) svg { transform: scaleX(-1); }
37
+
38
+ /* One selected day: the accent circle the range picker gives its two ends. A day that
39
+ cannot be chosen stays focusable (so it can be read out), hence aria-disabled, not :disabled. */
40
+ .uix-date-picker__grid button { cursor: pointer; }
41
+ .uix-date-picker__grid button[data-selected] { color: var(--uix-accent-fg); font-weight: 600; }
42
+ .uix-date-picker__grid button[data-selected]::after,
43
+ .uix-date-picker__grid button[data-selected]:hover:not(:disabled)::after { background: var(--uix-accent); }
44
+ .uix-date-picker__grid button[aria-disabled="true"] { color: var(--uix-text-hushed); text-decoration: line-through; cursor: not-allowed; }
45
+ .uix-date-picker__grid button[aria-disabled="true"]:not([data-selected]):hover:not(:disabled)::after { background: transparent; }
46
+
47
+ /* --- DateTimePicker: the date field, a time input and the zone they are in ---------------- */
48
+ .uix-date-time-picker { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--uix-space-2); min-width: 0; }
49
+ .uix-date-time-picker > .uix-date-picker { flex: 1 1 8.5rem; }
50
+ .uix-date-time-picker__time { flex: 0 1 auto; width: auto; min-width: 6.5rem; font-variant-numeric: tabular-nums; }
51
+ .uix-date-time-picker__zone { display: inline-flex; align-items: center; min-height: var(--uix-control-h); color: var(--uix-text-muted); font-size: var(--uix-text-meta); white-space: nowrap; }
52
+ .uix-date-time-picker--sm .uix-date-time-picker__zone { min-height: 28px; }
53
+
54
+ /* --- DateRangePicker mode="field": a trigger that shows the range ------------------------- */
55
+ .uix-date-range-field { display: grid; width: 100%; min-width: 0; max-width: 18rem; }
56
+ .uix-date-range-field__trigger { display: flex; align-items: center; gap: var(--uix-space-2); min-width: 0; text-align: start; cursor: pointer; }
57
+ .uix-date-range-field__trigger:disabled { cursor: not-allowed; }
58
+ .uix-date-range-field__icon { display: inline-flex; flex: none; color: var(--uix-text-muted); }
59
+ .uix-date-range-field__value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
60
+ .uix-date-range-field__value--empty { color: var(--uix-text-muted); }
61
+ .uix-date-range-field__popover { padding: 0; }
62
+ .uix-date-range-picker--in-popover { max-width: min(46rem, calc(100vw - 2 * var(--uix-space-4))); border: 0; background: transparent; }
63
+ .uix-date-range-picker--in-popover .uix-date-range-picker__months:has(> :only-child) { grid-template-columns: minmax(15rem, 1fr); }
64
+
65
+ @media (forced-colors: active) {
66
+ .uix-date-picker__grid button[data-selected] { forced-color-adjust: none; color: HighlightText; }
67
+ .uix-date-picker__grid button[data-selected]::after,
68
+ .uix-date-picker__grid button[data-selected]:hover:not(:disabled)::after { forced-color-adjust: none; background: Highlight; }
69
+ .uix-date-picker__grid button[aria-disabled="true"] { color: GrayText; }
70
+ .uix-date-picker__toggle[aria-expanded="true"] { outline: 1px solid Highlight; }
71
+ }
72
+ }
@@ -0,0 +1,28 @@
1
+ /* uix inline edit — a value that is edited in place (React `InlineEdit`, HAR-1381).
2
+ Depends only on --uix-* tokens. */
3
+ @layer uix.components {
4
+ .uix-inline-edit { display: inline-flex; max-width: 100%; min-width: 0; vertical-align: top; }
5
+ .uix-inline-edit--editing { display: flex; }
6
+ /* A value that cannot be edited is plain text on the same line box as its editable neighbours. */
7
+ .uix-inline-edit--readonly { align-items: center; min-height: 28px; }
8
+ /* The value IS the button: it looks like text until it is hovered or focused. The negative
9
+ inline margin keeps the text on the column it had as plain text. */
10
+ .uix-inline-edit__view {
11
+ display: inline-flex; align-items: center; gap: var(--uix-space-2); max-width: 100%; min-width: 0; min-height: 28px;
12
+ margin: 0 calc(-1 * var(--uix-space-2)); padding: var(--uix-space-1) var(--uix-space-2);
13
+ border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: inherit; font: inherit; text-align: start; cursor: pointer;
14
+ transition: background-color var(--uix-dur-fast) var(--uix-ease-out);
15
+ }
16
+ .uix-inline-edit__view:hover { background: var(--uix-bg-hover); }
17
+ .uix-inline-edit__view:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
18
+ .uix-inline-edit__value { min-width: 0; overflow-wrap: anywhere; }
19
+ .uix-inline-edit__placeholder { color: var(--uix-text-muted); }
20
+ /* 75% at rest keeps the pencil above 3:1 (the same floor as the view menu's quiet controls). */
21
+ .uix-inline-edit__icon { display: inline-flex; flex: none; color: var(--uix-text-muted); opacity: .75; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
22
+ .uix-inline-edit__view:is(:hover, :focus-visible) .uix-inline-edit__icon { opacity: 1; }
23
+ .uix-inline-edit__editor { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--uix-space-2); flex: 1; min-width: 0; }
24
+ .uix-inline-edit__control { flex: 1 1 12rem; min-width: 0; }
25
+ .uix-inline-edit__control > * { width: 100%; }
26
+ .uix-inline-edit__actions { display: inline-flex; flex: none; gap: var(--uix-space-2); }
27
+ .uix-inline-edit__error { flex-basis: 100%; }
28
+ }
@@ -18,3 +18,36 @@
18
18
  .uix-radio > input:focus-visible + .uix-radio__dot { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
19
19
  .uix-radio > input:disabled ~ * { opacity: .5; }
20
20
  }
21
+
22
+ /* RadioCard (HAR-1373): a native radio inside a label card, for a choice whose options need a
23
+ description or a preview. Selected = a stronger border AND a check mark, never colour alone.
24
+ The group is a grid that drops to one column when a card would be narrower than 12rem. */
25
+ @layer uix.components {
26
+ .uix-radio-group--cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr)); gap: var(--uix-space-3); min-width: 0; margin: 0; padding: 0; border: 0; }
27
+ .uix-radio-group--cards > .uix-radio-group__legend { grid-column: 1 / -1; padding: 0; margin-bottom: var(--uix-space-1); }
28
+ .uix-radio-card {
29
+ position: relative; display: flex; flex-direction: column; gap: var(--uix-space-3); min-width: 0; padding: var(--uix-space-4);
30
+ border: 1px solid var(--uix-border-strong); border-radius: var(--uix-radius-lg); background: var(--uix-surface); color: var(--uix-text); cursor: pointer;
31
+ transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
32
+ }
33
+ .uix-radio-card > input { position: absolute; opacity: 0; width: 1px; height: 1px; }
34
+ .uix-radio-card:hover { border-color: var(--uix-text-muted); }
35
+ .uix-radio-card:has(> input:checked) { border-color: var(--uix-accent); box-shadow: inset 0 0 0 1px var(--uix-accent); }
36
+ .uix-radio-card:has(> input:focus-visible) { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
37
+ .uix-radio-card:has(> input:disabled) { opacity: .5; cursor: not-allowed; }
38
+ .uix-radio-card__media { display: block; min-width: 0; border-radius: var(--uix-radius-md); overflow: hidden; }
39
+ .uix-radio-card__media > :is(img, svg) { display: block; max-width: 100%; }
40
+ .uix-radio-card__body { display: grid; gap: var(--uix-space-1); min-width: 0; padding-inline-end: var(--uix-space-6); }
41
+ .uix-radio-card__title { font-size: var(--uix-text-body); font-weight: 600; overflow-wrap: anywhere; }
42
+ .uix-radio-card__desc { font-size: var(--uix-text-meta); color: var(--uix-text-hushed); overflow-wrap: anywhere; }
43
+ .uix-radio-card__check {
44
+ position: absolute; inset-block-start: var(--uix-space-3); inset-inline-end: var(--uix-space-3); display: grid; place-items: center;
45
+ width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--uix-border-strong); background: var(--uix-surface); color: transparent;
46
+ }
47
+ .uix-radio-card__check svg { width: 14px; height: 14px; }
48
+ .uix-radio-card > input:checked ~ .uix-radio-card__check { border-color: var(--uix-accent); background: var(--uix-accent); color: var(--uix-accent-fg); }
49
+ @media (forced-colors: active) {
50
+ .uix-radio-card:has(> input:checked) { outline: 2px solid Highlight; outline-offset: -2px; }
51
+ .uix-radio-card > input:checked ~ .uix-radio-card__check { background: Highlight; color: HighlightText; }
52
+ }
53
+ }
@@ -24,6 +24,7 @@
24
24
  --calendar-status-text-dim: var(--uix-text-muted);
25
25
  --calendar-band-medium-edge: var(--uix-neutral-solid);
26
26
  --calendar-band-high-fill: var(--uix-danger);
27
+ --calendar-band-high-edge: var(--uix-danger); /* read on paper only, where the fill is the paper's */
27
28
  --calendar-band-high-text: var(--uix-danger-fg);
28
29
  --calendar-window-surface: var(--uix-neutral-bg);
29
30
  --calendar-window-ink: var(--uix-neutral-solid);
@@ -280,6 +281,8 @@
280
281
  .uix-scheduling-calendar__tg-item > .uix-scheduling-calendar__entry-text { white-space:normal; overflow-wrap:anywhere; max-height:100%; }
281
282
  .uix-scheduling-calendar__tg-item[data-lines="1"] > .uix-scheduling-calendar__entry-text { white-space:nowrap; overflow-wrap:normal; }
282
283
  .uix-scheduling-calendar__tg-item > .uix-scheduling-calendar__markers { height:1rem; }
284
+ /* an item too short for a line shows its markers only; its words are in the markup for paper (HAR-1541) */
285
+ .uix-scheduling-calendar__tg-item > [data-room="none"] { display:none; }
283
286
  .uix-scheduling-calendar__tg-from { flex:1 1 auto; min-width:0; overflow:hidden; white-space:nowrap; font-style:italic; }
284
287
  /* an item that crosses midnight: two parts with the shared edge left open */
285
288
  .uix-scheduling-calendar__tg-item[data-part="start"] { border-bottom-style:dashed; border-bottom-left-radius:0; border-bottom-right-radius:0; }
@@ -298,4 +301,115 @@
298
301
  .uix-scheduling-calendar__tg-probe { position:absolute; left:0; top:0; height:0; padding:0 0.125rem; border:0 solid transparent; border-width:0 1px; visibility:hidden; overflow:hidden; white-space:nowrap; font-size:var(--uix-text-meta); pointer-events:none; }
299
302
 
300
303
  .uix-scheduling-calendar__state { padding:var(--uix-space-8); border:1px dashed var(--calendar-chrome-line-strong); border-radius:var(--uix-radius-lg); text-align:center; }
304
+ /* ── Print (HAR-1541) ────────────────────────────────────────────────────────────────────────
305
+ The same markup on paper. Nothing scrolls or is cut, and the colours are the paper's (light,
306
+ whatever theme the screen has). The calendar asks the browser to print its surfaces as they
307
+ are; a browser may refuse, so no meaning rests on a fill or a pattern: each is an edge too.
308
+ The first rule is the colour map again. */
309
+ @media print {
310
+ .uix-scheduling-calendar {
311
+ --calendar-chrome-text: var(--uix-print-ink);
312
+ --calendar-chrome-text-quiet: var(--uix-print-ink-quiet);
313
+ --calendar-chrome-surface: var(--uix-print-paper);
314
+ --calendar-chrome-surface-quiet: var(--uix-print-paper-quiet);
315
+ --calendar-chrome-line: var(--uix-print-line);
316
+ --calendar-chrome-line-strong: var(--uix-print-ink-quiet);
317
+ --calendar-status-surface: var(--uix-print-paper);
318
+ --calendar-status-surface-quiet: var(--uix-print-paper-quiet);
319
+ --calendar-status-edge: var(--uix-print-line);
320
+ --calendar-status-edge-strong: var(--uix-print-ink-quiet);
321
+ --calendar-status-text: var(--uix-print-ink);
322
+ --calendar-status-text-dim: var(--uix-print-ink-quiet);
323
+ --calendar-band-medium-edge: var(--uix-print-ink-quiet);
324
+ --calendar-band-high-fill: var(--uix-print-paper);
325
+ --calendar-band-high-edge: var(--uix-print-danger);
326
+ --calendar-band-high-text: var(--uix-print-ink);
327
+ --calendar-window-surface: var(--uix-print-paper);
328
+ --calendar-window-ink: var(--uix-print-ink);
329
+ --calendar-window-edge: var(--uix-print-ink-quiet);
330
+ --calendar-window-text: var(--uix-print-ink);
331
+ --calendar-marker-ink: var(--uix-print-ink);
332
+ --calendar-chrome-now: var(--uix-print-ink);
333
+ --calendar-chrome-ghost: var(--uix-print-line);
334
+ --calendar-chrome-ghost-surface: var(--uix-print-paper);
335
+ }
336
+ /* The calendar brings its own paper and asks for its surfaces to be printed as they are, so
337
+ an item covers what is behind it whether or not the reader prints background graphics. */
338
+ :where(.uix-scheduling-calendar) { background:var(--calendar-chrome-surface); -webkit-print-color-adjust:exact; print-color-adjust:exact; }
339
+ /* a focus ring belongs to the screen */
340
+ .uix-scheduling-calendar :focus-visible { outline:none; }
341
+ /* nothing scrolls on paper: a grid is as wide as the page and as tall as what it holds */
342
+ .uix-scheduling-calendar__grid,.uix-scheduling-calendar__timegrid,.uix-scheduling-calendar__agenda--virtual { overflow:visible; max-height:none; }
343
+ .uix-scheduling-calendar__grid,.uix-scheduling-calendar__week { grid-template-columns:repeat(7,minmax(0,1fr)); }
344
+ .uix-scheduling-calendar__tg-head,.uix-scheduling-calendar__tg-strip,.uix-scheduling-calendar__tg-body { grid-template-columns:var(--calendar-gutter) repeat(var(--uix-scheduling-calendar-days,7),minmax(0,1fr)); min-width:0; }
345
+ /* controls do nothing on paper; the period title, the zone label and the legend stay */
346
+ .uix-scheduling-calendar__header :is(button,.uix-segmented) { display:none; }
347
+ /* the line for "now" and a move not yet sent belong to the moment, not to the page */
348
+ .uix-scheduling-calendar__tg-now,.uix-scheduling-calendar__tg-ghost { display:none; }
349
+ /* a week row, an agenda row, the legend, and a time grid that fits a page stay on one page;
350
+ a heading stays with what it heads */
351
+ .uix-scheduling-calendar__week,.uix-scheduling-calendar__agenda-rows > li,.uix-scheduling-calendar__agenda-vrow,.uix-scheduling-calendar__tg-head,.uix-scheduling-calendar__tg-strip,.uix-scheduling-calendar__timegrid,.uix-scheduling-calendar__legend { break-inside:avoid; }
352
+ .uix-scheduling-calendar__header,.uix-scheduling-calendar__agenda-vrow[data-kind="heading"] { break-after:avoid; }
353
+ .uix-scheduling-calendar__agenda-head { position:static; break-after:avoid; }
354
+ .uix-scheduling-calendar__empty { position:static; }
355
+
356
+ /* Month: a week is as tall as the page allows for six of them, and grows with what it holds */
357
+ .uix-scheduling-calendar__day { min-height:clamp(4.5rem,calc((100vh - 10rem) / 6),8rem); }
358
+ .uix-scheduling-calendar__grid[data-fixed] .uix-scheduling-calendar__week { height:auto; }
359
+ .uix-scheduling-calendar__grid[data-fixed] .uix-scheduling-calendar__day { overflow:visible; }
360
+ /* a chip holds its whole title, and a day badge its whole text */
361
+ .uix-scheduling-calendar__entries > .uix-scheduling-calendar__entry { height:auto; min-height:var(--calendar-chip); white-space:normal; overflow:visible; }
362
+ .uix-scheduling-calendar__entries > .uix-scheduling-calendar__entry > .uix-scheduling-calendar__entry-text { overflow:visible; overflow-wrap:anywhere; }
363
+ .uix-scheduling-calendar__badge { overflow:visible; white-space:normal; }
364
+
365
+ /* Time grid: an hour is as tall as the page allows for the whole day, between a readable
366
+ hour and the hour of the screen. Every position is counted in hours, so the items follow. */
367
+ .uix-scheduling-calendar__timegrid { --calendar-hour:clamp(1.25rem,calc((100vh - 21rem) / var(--uix-scheduling-calendar-hours,24)),3rem); margin-bottom:var(--uix-space-8); }
368
+ .uix-scheduling-calendar__tg-dayhead,.uix-scheduling-calendar__tg-dayhead > .uix-scheduling-calendar__markers { overflow:visible; flex-wrap:wrap; }
369
+ /* the lanes over the days grow with the words of a window or of an item with no time of day */
370
+ .uix-scheduling-calendar__tg-strip { grid-auto-rows:minmax(var(--calendar-lane),auto); }
371
+ .uix-scheduling-calendar__tg-strip > :is(.uix-scheduling-calendar__window,.uix-scheduling-calendar__tg-span),.uix-scheduling-calendar__agenda-notes .uix-scheduling-calendar__window,.uix-scheduling-calendar__agenda-vrow > .uix-scheduling-calendar__window { height:auto; min-height:calc(var(--calendar-lane) - var(--calendar-gap)); white-space:normal; overflow:visible; }
372
+ :is(.uix-scheduling-calendar__tg-strip,.uix-scheduling-calendar__agenda-notes,.uix-scheduling-calendar__agenda-vrow) :is(.uix-scheduling-calendar__window-text,.uix-scheduling-calendar__window-name,.uix-scheduling-calendar__window-scope,.uix-scheduling-calendar__entry-text) { flex-wrap:wrap; overflow:visible; overflow-wrap:anywhere; }
373
+ /* the hour lines are a background: on paper each hour label draws its own line across the days */
374
+ .uix-scheduling-calendar__tg-column { background:none; }
375
+ .uix-scheduling-calendar__tg-gutter { position:static; }
376
+ .uix-scheduling-calendar__tg-gutter > .uix-scheduling-calendar__tg-hour { left:0; right:0; padding-right:calc(100% - var(--calendar-gutter) + var(--uix-space-1)); }
377
+ .uix-scheduling-calendar__tg-gutter > .uix-scheduling-calendar__tg-hour::after { content:""; position:absolute; top:0; left:var(--calendar-gutter); right:0; border-top:1px dotted var(--calendar-chrome-line); }
378
+ /* the hours of a window for everything: a quiet tint under the hour lines, and an edge of its own inside the
379
+ day. Its clip makes it a layer of its own, painted in the order of the grid: `order` puts it before the
380
+ hours, so their lines are drawn over it and the items over both. */
381
+ .uix-scheduling-calendar__tg-shade[data-pattern] { order:-1; margin-inline:var(--uix-space-1); border-inline:1px dashed var(--calendar-window-edge); background-color:var(--calendar-chrome-surface-quiet); opacity:1; }
382
+ /* an item is at least as tall as its time and grows to hold its words; one that starts later lies over the end of it */
383
+ .uix-scheduling-calendar__tg-item { height:auto; min-height:max(calc(var(--uix-scheduling-calendar-length,1) * var(--calendar-hour) - 1px),0.875rem); overflow:visible; }
384
+ .uix-scheduling-calendar__tg-item[data-lines] > :is(.uix-scheduling-calendar__entry-text,.uix-scheduling-calendar__tg-from) { display:block; max-height:none; overflow:visible; white-space:normal; overflow-wrap:anywhere; }
385
+
386
+ /* Agenda: the long form mounts every row while the page prints, each as tall as its text */
387
+ .uix-scheduling-calendar__agenda-window { padding:0 !important; }
388
+ .uix-scheduling-calendar__agenda--virtual .uix-scheduling-calendar__agenda-vrow,.uix-scheduling-calendar__agenda--virtual[data-narrow] .uix-scheduling-calendar__agenda-vrow { height:auto; min-height:44px; overflow:visible; }
389
+ .uix-scheduling-calendar__agenda--virtual .uix-scheduling-calendar__agenda-vrow :is(.uix-scheduling-calendar__agenda-row,.uix-scheduling-calendar__agenda-main,.uix-scheduling-calendar__agenda-time,.uix-scheduling-calendar__agenda-heading,.uix-scheduling-calendar__markers,.uix-scheduling-calendar__markers > span,.uix-scheduling-calendar__marker) { flex-wrap:wrap; overflow:visible; white-space:normal; }
390
+
391
+ /* Paper has no fill to rely on: the high band is the heaviest edge, in the one signal colour, in every
392
+ state. Its fill is the paper's on paper (the map above), so it still covers what is behind it, and its
393
+ edge has a name of its own there. */
394
+ :is(.uix-scheduling-calendar__entry,.uix-scheduling-calendar__swatch)[data-band="high"] { border-width:3px; border-color:var(--calendar-band-high-edge); }
395
+ :is(.uix-scheduling-calendar__entry,.uix-scheduling-calendar__swatch)[data-band="high"]:is([data-status="tentative"],[data-status="dead"],[data-status="done"]) { border-color:var(--calendar-band-high-edge); }
396
+ .uix-scheduling-calendar__entry[data-band="high"][data-dim] { border-color:var(--calendar-band-high-edge); }
397
+ /* a highlight is a heavier edge, which does not show beside the edge of the high band, and in
398
+ the agendas a shadow, which may not print: on paper its title is underlined as well */
399
+ .uix-scheduling-calendar__entry[data-highlight] .uix-scheduling-calendar__title,:is(.uix-scheduling-calendar__agenda-row,.uix-scheduling-calendar__agenda > ol > li > button)[data-highlight] :is(.uix-scheduling-calendar__title,strong) { text-decoration:underline; }
400
+ /* a window's pattern is a background and may not print: its edge says the same */
401
+ :is(.uix-scheduling-calendar__window,.uix-scheduling-calendar__swatch)[data-pattern="diagonal"] { border-style:dashed; }
402
+ :is(.uix-scheduling-calendar__window,.uix-scheduling-calendar__swatch)[data-pattern="cross"] { border-style:double; border-width:3px; }
403
+ :is(.uix-scheduling-calendar__window,.uix-scheduling-calendar__swatch)[data-pattern="dotted"] { border-style:dotted; border-width:2px; }
404
+ /* a window for everything keeps its heavier edge in each pattern */
405
+ .uix-scheduling-calendar__window[data-global][data-pattern="diagonal"] { border-width:2px; }
406
+ .uix-scheduling-calendar__window[data-global][data-pattern="cross"] { border-width:4px; }
407
+ .uix-scheduling-calendar__window[data-global][data-pattern="dotted"] { border-width:3px; }
408
+ /* a window that goes on past the row is open at that end, whatever its pattern */
409
+ .uix-scheduling-calendar__window[data-continues-before] { border-left-style:none; }
410
+ .uix-scheduling-calendar__window[data-continues-after] { border-right-style:none; }
411
+ /* a bar too short for all its words gives up the scope first and keeps the name */
412
+ .uix-scheduling-calendar__window-name { flex-shrink:0; }
413
+ .uix-scheduling-calendar__window-scope { flex-shrink:1; }
414
+ }
301
415
  }