@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.
@@ -1,51 +1,306 @@
1
- /* uix scheduling timeline (React `SchedulingTimeline`, HAR-1364) — lanes of bars on a time
2
- axis. Depends only on --uix-* tokens. The lane label column is sticky; the axis scrolls. */
1
+ /* uix scheduling timeline (React `SchedulingTimeline`, HAR-1364, HAR-1521) — lanes of bars on a
2
+ time axis. Depends only on --uix-* tokens. The lane label column is sticky; the axis scrolls.
3
+
4
+ COLOUR MAP. Every colour the timeline paints is named once in the first rule and read from
5
+ there; no other rule names a --uix-* colour. One name has one meaning:
6
+ --timeline-band-* the band, the only dimension that takes a hue (only `high` is filled)
7
+ --timeline-status-* the state channel of a bar: line style, weight and dimming, never a hue
8
+ --timeline-window-* windows: neutral, told apart by pattern and visible label
9
+ --timeline-marker-* markers: a shape or icon with text on a bar, a dashed line on the axis
10
+ --timeline-chrome-* rows, lines, the axis, the now-line, the move outline and controls
11
+ To retune a colour, change its line in the map (HAR-1521; packages/tokens/tests/scheduling-timeline-colours.test.mjs). */
3
12
  @layer uix.components {
4
- .uix-scheduling-timeline { display: grid; gap: var(--uix-space-3); color: var(--uix-text); min-width: 0; }
5
- .uix-scheduling-timeline__scroller { position: relative; overflow-x: auto; border: 1px solid var(--uix-border); border-radius: var(--uix-radius-md); background: var(--uix-surface); }
13
+ .uix-scheduling-timeline {
14
+ --timeline-chrome-text: var(--uix-text);
15
+ --timeline-chrome-text-quiet: var(--uix-text-muted);
16
+ --timeline-chrome-surface: var(--uix-surface);
17
+ --timeline-chrome-surface-quiet: var(--uix-bg-subtle);
18
+ --timeline-chrome-line: var(--uix-border);
19
+ --timeline-chrome-line-strong: var(--uix-border-strong);
20
+ --timeline-chrome-now: var(--uix-text);
21
+ --timeline-chrome-now-text: var(--uix-surface);
22
+ --timeline-chrome-ghost: var(--uix-text);
23
+ --timeline-chrome-ghost-halo: var(--uix-surface);
24
+ --timeline-status-surface: var(--uix-surface);
25
+ --timeline-status-surface-quiet: var(--uix-bg-subtle);
26
+ --timeline-status-edge: var(--uix-neutral-border);
27
+ --timeline-status-edge-strong: var(--uix-neutral-solid);
28
+ --timeline-status-text: var(--uix-text);
29
+ --timeline-status-text-dim: var(--uix-text-muted);
30
+ --timeline-band-medium-edge: var(--uix-neutral-solid);
31
+ --timeline-band-high-fill: var(--uix-danger);
32
+ --timeline-band-high-edge: var(--uix-danger); /* read on paper only, where the fill is the paper's */
33
+ --timeline-band-high-text: var(--uix-danger-fg);
34
+ --timeline-window-surface: var(--uix-neutral-bg);
35
+ --timeline-window-ink: var(--uix-neutral-solid);
36
+ --timeline-window-edge: var(--uix-neutral-border);
37
+ --timeline-window-text: var(--uix-text);
38
+ --timeline-marker-ink: var(--uix-text);
39
+ --timeline-marker-line: var(--uix-neutral-solid);
40
+
41
+ /* geometry: a lane is its sub-rows plus its padding (the rule under it is inside that); a group
42
+ head is one line. The component counts row positions in these three lengths (scheduling-timeline-rows.ts). */
43
+ --timeline-bar-row: 2.25rem;
44
+ --timeline-bar: 2rem;
45
+ --timeline-bar-min: 1.5rem; /* the component measures this (the element .…__bar-min) and packs bars as at least this wide */
46
+ --timeline-lane-pad: calc(2 * var(--uix-space-2));
47
+ --timeline-head: 2.5rem;
48
+ --timeline-axis: 2.25rem;
49
+ --timeline-axis-height: var(--timeline-axis);
50
+ --timeline-gap: 0.125rem;
51
+
52
+ display: grid; gap: var(--uix-space-3); color: var(--timeline-chrome-text); min-width: 0;
53
+ }
54
+ .uix-scheduling-timeline__notice { padding: var(--uix-space-2) var(--uix-space-3); border: 1px solid var(--timeline-chrome-line); border-radius: var(--uix-radius-sm); background: var(--timeline-chrome-surface-quiet); font-size: var(--uix-text-meta); }
55
+ .uix-scheduling-timeline__notice > :first-child { margin-top: 0; }
56
+ .uix-scheduling-timeline__notice > :last-child { margin-bottom: 0; }
57
+ .uix-scheduling-timeline__scroller { position: relative; overflow-x: auto; border: 1px solid var(--timeline-chrome-line); border-radius: var(--uix-radius-md); background: var(--timeline-chrome-surface); }
58
+ /* with `maxHeight` the lanes scroll inside the timeline, under the axis */
59
+ .uix-scheduling-timeline__scroller[data-scroll-y] { overflow-y: auto; }
6
60
  .uix-scheduling-timeline__grid { display: grid; width: max-content; min-width: 100%; }
7
- .uix-scheduling-timeline__row { display: flex; border-bottom: 1px solid var(--uix-border); }
61
+ .uix-scheduling-timeline__grid[data-sub-ticks] { --timeline-axis-height: calc(var(--timeline-axis) + 1rem); }
62
+ .uix-scheduling-timeline__row { display: flex; border-bottom: 1px solid var(--timeline-chrome-line); }
8
63
  .uix-scheduling-timeline__row:last-child { border-bottom: 0; }
9
64
  .uix-scheduling-timeline__lane-label {
10
65
  position: sticky; left: 0; z-index: var(--uix-z-sticky); flex: 0 0 clamp(7.5rem, 30vw, 12rem); display: grid; align-content: center; gap: var(--uix-space-1);
11
- padding: var(--uix-space-2) var(--uix-space-3); border-right: 1px solid var(--uix-border); background: var(--uix-surface); min-width: 0;
66
+ padding: var(--uix-space-2) var(--uix-space-3); border-right: 1px solid var(--timeline-chrome-line); background: var(--timeline-chrome-surface); min-width: 0;
12
67
  }
13
- .uix-scheduling-timeline__row--axis .uix-scheduling-timeline__lane-label { background: var(--uix-bg-subtle); color: var(--uix-text-muted); font-size: var(--uix-text-meta); font-weight: 600; }
14
68
  .uix-scheduling-timeline__lane-name { font-weight: 500; overflow-wrap: break-word; hyphens: auto; }
15
- .uix-scheduling-timeline__lane-meta { font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
16
- .uix-scheduling-timeline__track { position: relative; flex: 1 0 auto; min-height: calc(var(--uix-timeline-rows, 1) * 2.25rem + 2 * var(--uix-space-2)); }
17
- .uix-scheduling-timeline__axis { min-height: 2.25rem; background: var(--uix-bg-subtle); }
18
- .uix-scheduling-timeline__tick { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--uix-border); }
19
- .uix-scheduling-timeline__tick[data-major] { border-left-color: var(--uix-border-strong); }
20
- .uix-scheduling-timeline__tick-label { position: absolute; top: 50%; left: var(--uix-space-1); transform: translateY(-50%); white-space: nowrap; font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
21
- .uix-scheduling-timeline__tick[data-major] .uix-scheduling-timeline__tick-label { color: var(--uix-text); font-weight: 600; }
22
- .uix-scheduling-timeline__gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--uix-border); }
23
- .uix-scheduling-timeline__gridline[data-major] { background: var(--uix-border-strong); }
24
- .uix-scheduling-timeline__overlay { position: absolute; top: 0; bottom: 0; background: var(--uix-info-bg); }
25
- .uix-scheduling-timeline__overlay[data-kind="freeze"],
26
- .uix-scheduling-timeline__overlay[data-kind="blackout"] { background: var(--uix-danger-bg); border-inline: 1px dashed var(--uix-danger-border); }
27
- .uix-scheduling-timeline__overlay-label { position: absolute; top: var(--uix-space-1); left: var(--uix-space-1); right: var(--uix-space-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--uix-text-eyebrow); letter-spacing: var(--uix-tracking-eyebrow); text-transform: uppercase; color: var(--uix-text); }
28
- .uix-scheduling-timeline__now { position: absolute; top: 0; bottom: 0; z-index: var(--uix-z-raised); width: 2px; transform: translateX(-50%); background: var(--uix-danger); }
29
- .uix-scheduling-timeline__now-label { position: absolute; top: 0; left: var(--uix-space-1); padding: 0 var(--uix-space-1); border-radius: var(--uix-radius-xs); background: var(--uix-danger); color: var(--uix-danger-fg); font-size: var(--uix-text-eyebrow); white-space: nowrap; }
30
- .uix-scheduling-timeline__marker { position: absolute; top: 0; bottom: 0; z-index: var(--uix-z-raised); border-left: 2px dashed var(--uix-warning); }
69
+ .uix-scheduling-timeline__lane-meta { font-size: var(--uix-text-meta); color: var(--timeline-chrome-text-quiet); }
70
+ .uix-scheduling-timeline__track { position: relative; flex: 1 0 auto; min-height: calc(var(--uix-timeline-rows, 1) * var(--timeline-bar-row) + 2 * var(--uix-space-2)); }
71
+
72
+ /* the axis: it stays at the top of a timeline that scrolls inside itself */
73
+ .uix-scheduling-timeline__row--axis { position: sticky; top: 0; z-index: var(--uix-z-sticky); }
74
+ .uix-scheduling-timeline__row--axis .uix-scheduling-timeline__lane-label { background: var(--timeline-chrome-surface-quiet); color: var(--timeline-chrome-text-quiet); font-size: var(--uix-text-meta); font-weight: 600; }
75
+ .uix-scheduling-timeline__axis { min-height: var(--timeline-axis-height); background: var(--timeline-chrome-surface-quiet); }
76
+ .uix-scheduling-timeline__tick { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--timeline-chrome-line); }
77
+ .uix-scheduling-timeline__tick[data-major] { border-left-color: var(--timeline-chrome-line-strong); }
78
+ .uix-scheduling-timeline__tick-label { position: absolute; top: 50%; left: var(--uix-space-1); transform: translateY(-50%); white-space: nowrap; font-size: var(--uix-text-meta); font-variant-numeric: tabular-nums; color: var(--timeline-chrome-text-quiet); }
79
+ .uix-scheduling-timeline__tick[data-major] .uix-scheduling-timeline__tick-label { color: var(--timeline-chrome-text); font-weight: 600; }
80
+ /* a repeated hour says which one it is: its UTC offset, under the hour */
81
+ .uix-scheduling-timeline__tick-offset { display: block; font-size: var(--uix-text-eyebrow); font-weight: 400; line-height: 1; }
82
+ /* sub-ticks: the day labels take the upper line of a taller axis, the hours the lower one */
83
+ .uix-scheduling-timeline__grid[data-sub-ticks] .uix-scheduling-timeline__tick:not([data-minor]) > .uix-scheduling-timeline__tick-label { top: var(--uix-space-1); transform: none; }
84
+ .uix-scheduling-timeline__tick[data-minor] { top: auto; height: 1.25rem; }
85
+ .uix-scheduling-timeline__tick[data-minor] > .uix-scheduling-timeline__tick-label { top: auto; bottom: var(--timeline-gap); transform: none; font-size: var(--uix-text-eyebrow); }
86
+
87
+ /* notes under the axis, one for the column of a day */
88
+ .uix-scheduling-timeline__row--notes { background: var(--timeline-chrome-surface-quiet); }
89
+ .uix-scheduling-timeline__row--notes > .uix-scheduling-timeline__lane-label { background: var(--timeline-chrome-surface-quiet); padding-block: 0; }
90
+ .uix-scheduling-timeline__row--notes > .uix-scheduling-timeline__track { min-height: 1.75rem; }
91
+ .uix-scheduling-timeline__column-note { position: absolute; top: 0; bottom: 0; display: flex; align-items: center; min-width: 0; padding: 0 var(--uix-space-1); border-left: 1px solid var(--timeline-chrome-line); overflow: hidden; white-space: nowrap; font-size: var(--uix-text-meta); color: var(--timeline-chrome-text); }
92
+
93
+ /* the body: the rows, and under them one layer that holds each grid line, window, marker and
94
+ the now-line once. `isolation` keeps the sticky lane labels under the sticky axis. */
95
+ .uix-scheduling-timeline__body { position: relative; isolation: isolate; }
96
+ .uix-scheduling-timeline__layer { position: absolute; inset: 0; display: flex; pointer-events: none; }
97
+ /* the layer repeats the lane column, unseen, so its track is exactly as wide as the tracks of the rows */
98
+ .uix-scheduling-timeline__layer > .uix-scheduling-timeline__lane-label { position: static; visibility: hidden; }
99
+ .uix-scheduling-timeline__layer > .uix-scheduling-timeline__track { min-height: 0; }
100
+ /* a click on the empty part of a lane reaches the window drawn under it */
101
+ .uix-scheduling-timeline__body > .uix-scheduling-timeline__row > .uix-scheduling-timeline__track { pointer-events: none; }
102
+ .uix-scheduling-timeline__spacer { flex: none; }
103
+ .uix-scheduling-timeline__gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--timeline-chrome-line); }
104
+ .uix-scheduling-timeline__gridline[data-major] { background: var(--timeline-chrome-line-strong); }
105
+ .uix-scheduling-timeline__gridline[data-minor] { background: repeating-linear-gradient(to bottom, var(--timeline-chrome-line) 0 2px, transparent 2px 6px); }
106
+
107
+ /* Fixed rows. With groups, a window or marker limited to lanes, or a virtual window, every row
108
+ has the height the component counts with: a lane is its sub-rows, a head is one line, and a
109
+ label that does not fit is cut. A plain timeline keeps rows that grow with their label. */
110
+ .uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body > .uix-scheduling-timeline__row { height: calc(var(--uix-timeline-rows, 1) * var(--timeline-bar-row) + var(--timeline-lane-pad)); }
111
+ .uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body .uix-scheduling-timeline__lane-label { padding-block: 0; overflow: hidden; }
112
+ .uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body .uix-scheduling-timeline__track { min-height: 0; }
113
+ .uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body :is(.uix-scheduling-timeline__lane-name, .uix-scheduling-timeline__lane-meta) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
114
+
115
+ /* a group: one row. Its head band is lifted over the layer and opaque, so nothing shows through
116
+ the name. Collapsed, that row is all there is of the group; when a window covers a lane it
117
+ hides, the row has a strip under the head, one sub-row tall and not lifted, where the window
118
+ (and the grid lines) are drawn with nothing over them. */
119
+ .uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body > .uix-scheduling-timeline__row--group { flex-direction: column; height: var(--timeline-head); background: var(--timeline-chrome-surface-quiet); }
120
+ .uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body > .uix-scheduling-timeline__row--group[data-strip] { height: calc(var(--timeline-head) + var(--timeline-bar-row)); }
121
+ .uix-scheduling-timeline__group-head { position: relative; flex: 1 1 var(--timeline-head); display: flex; min-height: 0; background: var(--timeline-chrome-surface-quiet); }
122
+ .uix-scheduling-timeline__group-strip { flex: 0 0 calc(var(--timeline-bar-row) - 1px); }
123
+ .uix-scheduling-timeline__group-label { position: sticky; left: 0; z-index: var(--uix-z-sticky); display: flex; align-items: center; gap: var(--uix-space-3); min-width: 0; padding: 0 var(--uix-space-2); background: var(--timeline-chrome-surface-quiet); white-space: nowrap; }
124
+ .uix-scheduling-timeline__group-toggle { display: inline-flex; align-items: center; gap: var(--uix-space-2); min-height: 1.75rem; padding: 0 var(--uix-space-2); border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: inherit; font: inherit; font-weight: 600; cursor: pointer; }
125
+ .uix-scheduling-timeline__group-toggle:hover { background: var(--timeline-chrome-surface); }
126
+ /* the chevron is two borders, so it survives forced colours; it points down while the group is open */
127
+ .uix-scheduling-timeline__group-chevron { flex: none; width: 0.5rem; height: 0.5rem; margin-top: -0.1875rem; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); }
128
+ .uix-scheduling-timeline__group-toggle[aria-expanded="false"] > .uix-scheduling-timeline__group-chevron { margin-top: 0; transform: rotate(-45deg); }
129
+ .uix-scheduling-timeline__group-name { overflow: hidden; text-overflow: ellipsis; }
130
+ .uix-scheduling-timeline__group-meta { color: var(--timeline-chrome-text-quiet); font-size: var(--uix-text-meta); }
131
+ .uix-scheduling-timeline__group-summary { display: inline-flex; align-items: center; gap: var(--uix-space-2); font-size: var(--uix-text-meta); }
132
+ .uix-scheduling-timeline__group-count { font-weight: 600; font-variant-numeric: tabular-nums; }
133
+
134
+ /* windows: one element each, neutral, told apart by pattern and words */
135
+ .uix-scheduling-timeline__overlay { position: absolute; top: 0; bottom: 0; display: block; min-width: 0; margin: 0; padding: 0; border: 0; border-inline: 1px solid var(--timeline-window-edge); border-radius: 0; background-color: var(--timeline-window-surface); color: var(--timeline-window-text); font: inherit; text-align: start; }
136
+ button.uix-scheduling-timeline__overlay { pointer-events: auto; cursor: pointer; }
137
+ /* the ring is drawn inside: a clip would cut an outline that sits outside the box */
138
+ button.uix-scheduling-timeline__overlay:focus-visible { outline-offset: -2px; }
139
+ /* limited to its lanes: the clip is the rows of those lanes, written by the component in row units */
140
+ :is(.uix-scheduling-timeline__overlay, .uix-scheduling-timeline__marker)[data-scope="lanes"] { clip-path: var(--uix-timeline-clip, none); }
141
+ .uix-scheduling-timeline__overlay[data-pattern="diagonal"] { background-image: repeating-linear-gradient(45deg, var(--timeline-window-ink) 0 1px, transparent 1px 6px); }
142
+ .uix-scheduling-timeline__overlay[data-pattern="cross"] { background-image: repeating-linear-gradient(45deg, var(--timeline-window-ink) 0 1px, transparent 1px 6px), repeating-linear-gradient(-45deg, var(--timeline-window-ink) 0 1px, transparent 1px 6px); }
143
+ .uix-scheduling-timeline__overlay[data-pattern="dotted"] { background-image: radial-gradient(circle, var(--timeline-window-ink) 1px, transparent 1.5px); background-size: 5px 5px; }
144
+ /* the words sit on their own patch of the window colour, so the hatch never runs under them.
145
+ The component puts them at the top of a lane (--uix-timeline-label-top, in row units): the
146
+ first lane of each run of rows the window covers. */
147
+ .uix-scheduling-timeline__overlay-label { position: absolute; top: calc(var(--uix-timeline-label-top, 0%) + var(--timeline-gap)); left: var(--timeline-gap); display: flex; gap: var(--uix-space-1); max-width: calc(100% - 2 * var(--timeline-gap)); padding: 0 var(--uix-space-1); border-radius: var(--uix-radius-xs); background: var(--timeline-window-surface); overflow: hidden; white-space: nowrap; font-size: var(--uix-text-meta); line-height: 1.25rem; }
148
+ .uix-scheduling-timeline__overlay-kind { flex: none; font-weight: 600; }
149
+ .uix-scheduling-timeline__overlay-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
150
+ .uix-scheduling-timeline__overlay-scope { flex: 0 2 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-style: italic; }
151
+
152
+ /* now: one neutral line with its word; a point marker: one dashed line */
153
+ .uix-scheduling-timeline__now { position: absolute; top: 0; bottom: 0; z-index: var(--uix-z-raised); width: 2px; transform: translateX(-50%); background: var(--timeline-chrome-now); }
154
+ .uix-scheduling-timeline__now-label { position: sticky; top: var(--timeline-axis-height); display: block; width: max-content; margin-left: var(--uix-space-1); padding: 0 var(--uix-space-1); border-radius: var(--uix-radius-xs); background: var(--timeline-chrome-now); color: var(--timeline-chrome-now-text); font-size: var(--uix-text-eyebrow); line-height: 1.5; white-space: nowrap; }
155
+ .uix-scheduling-timeline__marker { position: absolute; top: 0; bottom: 0; z-index: var(--uix-z-raised); border-left: 2px dashed var(--timeline-marker-line); }
156
+
157
+ /* bars */
31
158
  .uix-scheduling-timeline__items { position: absolute; inset: 0; margin: 0; padding: 0; list-style: none; }
32
- .uix-scheduling-timeline__slot { position: absolute; top: calc(var(--uix-space-2) + var(--uix-timeline-row, 0) * 2.25rem); height: 2rem; min-width: 0.5rem; }
159
+ /* a bar is never a smaller target than --timeline-bar-min (24px) on either side. The component
160
+ measures that length and packs bars as at least that wide, so two drawn at it never cover each other. */
161
+ .uix-scheduling-timeline__slot { position: absolute; top: calc(var(--uix-space-2) + var(--uix-timeline-row, 0) * var(--timeline-bar-row)); height: var(--timeline-bar); min-width: var(--timeline-bar-min); pointer-events: auto; }
162
+ /* the default look is the `committed` status with no band: a plain outlined bar */
33
163
  .uix-scheduling-timeline__item {
34
- display: flex; align-items: center; gap: var(--uix-space-2); width: 100%; height: 100%; padding: 0 var(--uix-space-2); overflow: hidden;
35
- border: 0; border-left: 3px solid var(--uix-info); border-radius: var(--uix-radius-sm); background: var(--uix-info-bg); color: var(--uix-info-text);
36
- font: inherit; font-size: var(--uix-text-meta); text-align: start; white-space: nowrap; cursor: pointer;
164
+ position: relative; display: flex; align-items: center; gap: var(--uix-space-1); width: 100%; height: 100%; padding: 0 var(--uix-space-2); overflow: hidden;
165
+ border: 1px solid var(--timeline-status-edge); border-radius: var(--uix-radius-sm); background: var(--timeline-status-surface); color: var(--timeline-status-text);
166
+ font: inherit; font-size: var(--uix-text-meta); line-height: 1.25rem; text-align: start; cursor: pointer;
37
167
  }
38
- .uix-scheduling-timeline__item-title { overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
39
- .uix-scheduling-timeline__item-meta { overflow: hidden; text-overflow: ellipsis; opacity: .85; }
40
- .uix-scheduling-timeline__item[data-state="conflicted"], .uix-scheduling-timeline__item[data-state="blackout-violation"] { border-color: var(--uix-danger); background: var(--uix-danger-bg); color: var(--uix-danger-text); }
41
- .uix-scheduling-timeline__item[data-state="in-progress"] { border-color: var(--uix-success); background: var(--uix-success-bg); color: var(--uix-text); }
42
- /* overlap with another bar in the lane: hatched, so it reads without colour */
43
- .uix-scheduling-timeline__item[data-conflict] { background-image: repeating-linear-gradient(135deg, transparent 0 6px, color-mix(in srgb, currentColor 20%, transparent) 6px 9px); }
168
+ /* the text wraps and only its first line shows: a long label is cut at a word, never in one.
169
+ The empty box in front is a place to break before the first word, so a first word that does
170
+ not fit goes to the hidden second line too: no part of a word (or of a time) is ever shown. */
171
+ .uix-scheduling-timeline__item-title { flex: 1 1 auto; min-width: 0; max-height: 1.25rem; overflow: hidden; font-weight: 600; }
172
+ .uix-scheduling-timeline__item-title::before { content: ""; display: inline-block; }
173
+ .uix-scheduling-timeline__item-meta { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
174
+ .uix-scheduling-timeline__item-markers { flex: none; display: inline-flex; align-items: center; gap: var(--timeline-gap); }
175
+
176
+ /* status: line style, weight and dimming only */
177
+ .uix-scheduling-timeline__item[data-status="tentative"] { border-style: dashed; border-color: var(--timeline-status-edge-strong); }
178
+ .uix-scheduling-timeline__item[data-status="live"] { border-color: var(--timeline-status-edge-strong); }
179
+ /* the live dot is a border, so it survives forced colours */
180
+ .uix-scheduling-timeline__item[data-status="live"]::before { content: ""; flex: none; border: 0.1875rem solid currentColor; border-radius: 50%; }
181
+ .uix-scheduling-timeline__item[data-status="done"] { border-style: none none solid; background: var(--timeline-status-surface-quiet); color: var(--timeline-status-text-dim); }
182
+ .uix-scheduling-timeline__item[data-status="dead"] { border-style: dotted; border-color: var(--timeline-status-edge-strong); color: var(--timeline-status-text-dim); }
183
+ .uix-scheduling-timeline__item[data-status="dead"] > .uix-scheduling-timeline__item-title { text-decoration: line-through; }
184
+
185
+ /* band: `medium` is a heavier leading edge, `high` is the one fill */
186
+ .uix-scheduling-timeline__item[data-band="medium"] { border-left: 3px solid var(--timeline-band-medium-edge); }
187
+ .uix-scheduling-timeline__item[data-band="high"] { border-color: var(--timeline-band-high-fill); background: var(--timeline-band-high-fill); color: var(--timeline-band-high-text); }
188
+ .uix-scheduling-timeline__item[data-band="high"]:is([data-status="tentative"], [data-status="dead"]) { border-color: var(--timeline-band-high-text); }
189
+ /* done keeps the whole fill on the high band (dimming it would cost the text its contrast). Its
190
+ cue is a line in the text colour inside the fill, near the bottom: on the edge it would be the
191
+ colour of the row and the bar would only look shorter. */
192
+ .uix-scheduling-timeline__item[data-band="high"][data-status="done"] { border-style: solid; background-image: linear-gradient(to top, transparent 0 3px, var(--timeline-band-high-text) 3px 5px, transparent 5px); }
193
+
194
+ /* shares time with another bar of its lane (only while `flagOverlaps` is on): hatched in its own text colour */
195
+ .uix-scheduling-timeline__item[data-conflict]::after { content: ""; position: absolute; inset: 0; background-image: repeating-linear-gradient(135deg, transparent 0 6px, currentColor 6px 9px); opacity: 0.2; pointer-events: none; }
44
196
  .uix-scheduling-timeline__item[data-clipped-start] { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left-style: dashed; }
45
- .uix-scheduling-timeline__item[data-clipped-end] { border-top-right-radius: 0; border-bottom-right-radius: 0; }
197
+ .uix-scheduling-timeline__item[data-clipped-end] { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right-style: dashed; }
46
198
  .uix-scheduling-timeline__item[data-movable] { cursor: grab; touch-action: none; }
47
- .uix-scheduling-timeline__item[data-dragging] { cursor: grabbing; box-shadow: var(--uix-shadow-popover); position: relative; z-index: var(--uix-z-raised); }
48
- .uix-scheduling-timeline__item:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 2px; position: relative; z-index: var(--uix-z-raised); }
49
- .uix-scheduling-timeline__empty { margin: 0; color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
50
- .uix-scheduling-timeline__state { padding: var(--uix-space-8); border: 1px dashed var(--uix-border-strong); border-radius: var(--uix-radius-lg); text-align: center; }
199
+ .uix-scheduling-timeline__item[data-dragging] { cursor: grabbing; box-shadow: var(--uix-shadow-popover); z-index: var(--uix-z-raised); }
200
+ .uix-scheduling-timeline__item:focus-visible { z-index: var(--uix-z-raised); }
201
+
202
+ /* markers on a bar: a shape (or the icon of the consumer) with text; the text is in the accessible name */
203
+ .uix-scheduling-timeline__item-marker { flex: none; display: inline-flex; align-items: center; color: var(--timeline-marker-ink); }
204
+ .uix-scheduling-timeline__item .uix-scheduling-timeline__item-marker { color: inherit; }
205
+ .uix-scheduling-timeline__item-marker-icon { display: inline-flex; width: 0.6875rem; height: 0.6875rem; }
206
+ .uix-scheduling-timeline__item-marker-icon > svg { width: 100%; height: 100%; }
207
+ .uix-scheduling-timeline__item-marker-label { margin-left: var(--uix-space-1); }
208
+ .uix-scheduling-timeline__item-marker[data-glyph]::before { content: ""; width: 0.625rem; height: 0.625rem; background: currentColor; }
209
+ .uix-scheduling-timeline__item-marker[data-glyph="neutral"]::before { border-radius: 50%; }
210
+ .uix-scheduling-timeline__item-marker[data-glyph="warning"]::before { clip-path: polygon(50% 0, 100% 100%, 0 100%); }
211
+ .uix-scheduling-timeline__item-marker[data-glyph="refused"]::before { clip-path: polygon(20% 0, 50% 30%, 80% 0, 100% 20%, 70% 50%, 100% 80%, 80% 100%, 50% 70%, 20% 100%, 0 80%, 30% 50%, 0 20%); }
212
+
213
+ /* a pending move: an outline where the bar would go; the bar itself stays put. Only an edge, so
214
+ what is under it stays readable, and in two tones: the dashes in the text colour, with a line
215
+ of the surface colour on each side. On the row the dashes carry it (3:1 and more); over a bar,
216
+ the high band included, whichever tone the fill is not close to does. It is lifted as far as
217
+ a focused bar is, and comes after the bars, so the bar being moved does not hide it. */
218
+ .uix-scheduling-timeline__ghost { position: absolute; top: calc(var(--uix-space-2) + var(--uix-timeline-row, 0) * var(--timeline-bar-row)); height: var(--timeline-bar); min-width: var(--timeline-bar-min); border: 2px dashed var(--timeline-chrome-ghost); border-radius: var(--uix-radius-sm); pointer-events: none; z-index: var(--uix-z-raised); }
219
+ .uix-scheduling-timeline__ghost::before, .uix-scheduling-timeline__ghost::after { content: ""; position: absolute; inset: 0; border: 2px solid var(--timeline-chrome-ghost-halo); border-radius: inherit; }
220
+ .uix-scheduling-timeline__ghost::before { inset: -4px; }
221
+ .uix-scheduling-timeline__ghost[data-sent] { border-style: dotted; }
222
+
223
+ /* the narrowest bar, measured by the component (never drawn) */
224
+ .uix-scheduling-timeline__bar-min { position: absolute; top: 0; left: 0; width: var(--timeline-bar-min); height: 0; visibility: hidden; pointer-events: none; }
225
+ /* the three row units, measured by a virtual timeline to turn a scroll position into rows */
226
+ .uix-scheduling-timeline__probe { position: absolute; top: 0; left: 0; width: 0; height: 0; overflow: hidden; visibility: hidden; pointer-events: none; }
227
+ .uix-scheduling-timeline__probe > span { display: block; height: var(--timeline-bar-row); }
228
+ .uix-scheduling-timeline__probe > span:nth-child(2) { height: var(--timeline-lane-pad); }
229
+ .uix-scheduling-timeline__probe > span:nth-child(3) { height: var(--timeline-head); }
230
+
231
+ .uix-scheduling-timeline__empty { margin: 0; color: var(--timeline-chrome-text-quiet); font-size: var(--uix-text-meta); }
232
+ .uix-scheduling-timeline__state { padding: var(--uix-space-8); border: 1px dashed var(--timeline-chrome-line-strong); border-radius: var(--uix-radius-lg); text-align: center; }
233
+ /* ── Print (HAR-1541) ────────────────────────────────────────────────────────────────────────
234
+ The same markup on paper: nothing scrolls or sticks, the axis is as wide as the page, the
235
+ colours are the paper's (light, whatever theme the screen has), and nothing depends on a
236
+ background, which a browser drops unless the reader asks for background graphics. The first
237
+ rule is the colour map again. */
238
+ @media print {
239
+ .uix-scheduling-timeline {
240
+ --timeline-chrome-text: var(--uix-print-ink);
241
+ --timeline-chrome-text-quiet: var(--uix-print-ink-quiet);
242
+ --timeline-chrome-surface: var(--uix-print-paper);
243
+ --timeline-chrome-surface-quiet: var(--uix-print-paper-quiet);
244
+ --timeline-chrome-line: var(--uix-print-line);
245
+ --timeline-chrome-line-strong: var(--uix-print-ink-quiet);
246
+ --timeline-chrome-now: var(--uix-print-ink);
247
+ --timeline-chrome-now-text: var(--uix-print-paper);
248
+ --timeline-chrome-ghost: var(--uix-print-ink-quiet);
249
+ --timeline-chrome-ghost-halo: var(--uix-print-paper);
250
+ --timeline-status-surface: var(--uix-print-paper);
251
+ --timeline-status-surface-quiet: var(--uix-print-paper-quiet);
252
+ --timeline-status-edge: var(--uix-print-line);
253
+ --timeline-status-edge-strong: var(--uix-print-ink-quiet);
254
+ --timeline-status-text: var(--uix-print-ink);
255
+ --timeline-status-text-dim: var(--uix-print-ink-quiet);
256
+ --timeline-band-medium-edge: var(--uix-print-ink-quiet);
257
+ --timeline-band-high-fill: var(--uix-print-paper);
258
+ --timeline-band-high-edge: var(--uix-print-danger);
259
+ --timeline-band-high-text: var(--uix-print-ink);
260
+ --timeline-window-surface: var(--uix-print-paper);
261
+ --timeline-window-ink: var(--uix-print-ink-quiet);
262
+ --timeline-window-edge: var(--uix-print-ink-quiet);
263
+ --timeline-window-text: var(--uix-print-ink);
264
+ --timeline-marker-ink: var(--uix-print-ink);
265
+ --timeline-marker-line: var(--uix-print-ink-quiet);
266
+ }
267
+ /* The timeline brings its own paper and asks for its surfaces to be printed as they are, so a bar
268
+ covers the window and the grid lines behind it whether or not the reader prints background graphics. */
269
+ :where(.uix-scheduling-timeline) { background: var(--timeline-chrome-surface); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
270
+ /* nothing scrolls on paper: the lanes are as wide as the page (every position is a share of
271
+ the axis, so the bars follow) and as tall as their rows. `!important` answers the sizes
272
+ the component writes on the elements themselves. */
273
+ .uix-scheduling-timeline__scroller { overflow: visible !important; max-height: none !important; }
274
+ .uix-scheduling-timeline__grid { width: 100%; min-width: 0; }
275
+ .uix-scheduling-timeline__track { flex: 1 1 0; min-width: 0 !important; }
276
+ /* nothing sticks: the axis, the lane names and the group heads stay where they are in the flow */
277
+ .uix-scheduling-timeline__row--axis,.uix-scheduling-timeline__lane-label,.uix-scheduling-timeline__group-label,.uix-scheduling-timeline__now-label { position: static; }
278
+ /* a lane and a group head stay on one page; a group head stays with its first lane */
279
+ .uix-scheduling-timeline__row { break-inside: avoid; }
280
+ .uix-scheduling-timeline__row--group,.uix-scheduling-timeline__row--axis { break-after: avoid; }
281
+ /* the line for "now" and a move not yet sent belong to the moment, not to the page */
282
+ .uix-scheduling-timeline__now,.uix-scheduling-timeline__ghost { display: none; }
283
+ /* the grid lines are backgrounds: on paper each is an edge */
284
+ .uix-scheduling-timeline__gridline { width: 0; background: none; border-left: 1px solid var(--timeline-chrome-line); }
285
+ .uix-scheduling-timeline__gridline[data-major] { background: none; border-left-color: var(--timeline-chrome-line-strong); }
286
+ .uix-scheduling-timeline__gridline[data-minor] { background: none; border-left-style: dashed; }
287
+ /* paper has no fill to rely on: the high band is a heavy edge in the one signal colour, in every state.
288
+ Its fill is the paper's on paper (the map above), so it still covers what is behind it, and its edge
289
+ has a name of its own there. */
290
+ .uix-scheduling-timeline__item[data-band="high"] { border-width: 2px; border-color: var(--timeline-band-high-edge); }
291
+ .uix-scheduling-timeline__item[data-band="high"]:is([data-status="tentative"], [data-status="dead"], [data-status="done"]) { border-color: var(--timeline-band-high-edge); }
292
+ /* done on the high band is a line painted inside the fill on a screen: on paper it is the cue of every
293
+ done bar, the bottom edge alone */
294
+ .uix-scheduling-timeline__item[data-band="high"][data-status="done"] { border-style: none none solid; background-image: none; }
295
+ /* a window's pattern is a background and may not print: its edges say the same */
296
+ .uix-scheduling-timeline__overlay[data-pattern="diagonal"] { border-style: none dashed; }
297
+ .uix-scheduling-timeline__overlay[data-pattern="cross"] { border-style: none double; border-width: 0 3px; }
298
+ .uix-scheduling-timeline__overlay[data-pattern="dotted"] { border-style: none dotted; border-width: 0 2px; }
299
+ /* a note on the column of a day is not cut at the column: on a narrow page a day is narrower than its note */
300
+ .uix-scheduling-timeline__column-note { overflow: visible; }
301
+ /* a bar too short for all the words of a window gives up the scope first and keeps the name */
302
+ .uix-scheduling-timeline__overlay-name { flex-shrink: 0; }
303
+ /* painted shapes ask to be printed even without background graphics: a marker's glyph and the hatch of bars that share time */
304
+ .uix-scheduling-timeline__item-marker[data-glyph]::before,.uix-scheduling-timeline__item[data-conflict]::after { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
305
+ }
51
306
  }
@@ -92,6 +92,8 @@
92
92
  }
93
93
  .uix-search-suggest__footer:first-child { margin-top: 0; border-top: 0; border-radius: var(--uix-radius-sm); }
94
94
  .uix-search-suggest__footer[data-active] { background: var(--uix-bg-hover); }
95
+ /* a line under the options that is not an option (HAR-1648): "More matches. Keep typing…" */
96
+ .uix-search-suggest__note { padding: var(--uix-space-2) var(--uix-space-3); border-top: 1px solid var(--uix-border); color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
95
97
 
96
98
  .uix-search-suggest__state {
97
99
  display: flex; align-items: center; justify-content: center; gap: var(--uix-space-2); flex-wrap: wrap;
@@ -27,3 +27,11 @@
27
27
  .uix-tree__item[aria-expanded="false"] > .uix-tree__row .uix-tree__toggle { transform: rotate(-90deg); }
28
28
  .uix-tree__item[aria-expanded="false"] > .uix-tree__group { display: none; }
29
29
  }
30
+
31
+ /* Children loaded on expand (HAR-1382): the loading row and the failed row under a node. Both
32
+ are ordinary tree rows, so the arrow keys reach them; the failed row is the Retry control. */
33
+ @layer uix.components {
34
+ .uix-tree__status { display: inline-flex; align-items: center; gap: var(--uix-space-2); color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
35
+ .uix-tree__status--error { color: var(--uix-danger-text); }
36
+ .uix-tree__retry { color: var(--uix-link); text-decoration: underline; text-underline-offset: .15em; }
37
+ }
@@ -103,5 +103,12 @@
103
103
  "scrollbar-track": { "$type": "color", "$value": "transparent" },
104
104
 
105
105
  "scrim": { "$type": "color", "$value": "rgba(0,0,0,.5)", "$description": "Backdrop behind modal / drawer / side-peek overlays and the avatar edit-overlay — dims the content beneath. Theme-invariant: a black scrim reads on light and dark alike (no dark override)." },
106
- "scrim-strong": { "$type": "color", "$value": "rgba(0,0,0,.7)", "$description": "Heavier scrim for media-focus surfaces (lightbox), where the backdrop should recede fully behind the image." }
106
+ "scrim-strong": { "$type": "color", "$value": "rgba(0,0,0,.7)", "$description": "Heavier scrim for media-focus surfaces (lightbox), where the backdrop should recede fully behind the image." },
107
+
108
+ "print-ink": { "$type": "color", "$value": "#0A0A0A", "$description": "Text and strong lines on paper. Theme-invariant (no dark override): paper is light whatever theme the screen has. Read only inside @media print, by components that print (HAR-1541)." },
109
+ "print-ink-quiet": { "$type": "color", "$value": "#525252", "$description": "Secondary text on paper: times, counts, dimmed items. 7.8:1 on print-paper." },
110
+ "print-paper": { "$type": "color", "$value": "#FFFFFF", "$description": "The page. Used where a printed component must cover what is under it." },
111
+ "print-paper-quiet": { "$type": "color", "$value": "#F0F0F0", "$description": "A quiet tint for headers when the reader prints background graphics. Nothing may depend on it: it is dropped when they do not." },
112
+ "print-line": { "$type": "color", "$value": "#8A8A8A", "$description": "Cell and row lines on paper. 3.4:1 on print-paper." },
113
+ "print-danger": { "$type": "color", "$value": "#C40012", "$description": "The one signal colour on paper (a heavy edge, never a fill, so it survives printing without background graphics). 6.2:1 on print-paper." }
107
114
  }