nexus-shared 3.0.0 → 3.2.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 (162) hide show
  1. package/CHANGELOG.md +442 -0
  2. package/README.md +1123 -1000
  3. package/dist/Client.Index.d.ts +6 -0
  4. package/dist/Client.Index.js +6 -0
  5. package/dist/Components/Chats/ChatParts.d.ts +2 -2
  6. package/dist/Components/Chats/ChatParts.js +3 -7
  7. package/dist/Components/Chats/ChatThread.js +3 -3
  8. package/dist/Components/Documents/Button.d.ts +6 -1
  9. package/dist/Components/Documents/Button.js +2 -2
  10. package/dist/Components/Documents/Menu.js +21 -9
  11. package/dist/Components/Documents/SplitButton.d.ts +3 -1
  12. package/dist/Components/Documents/SplitButton.js +7 -5
  13. package/dist/Components/Documents/TabButtons.d.ts +5 -2
  14. package/dist/Components/Documents/TabButtons.js +2 -2
  15. package/dist/Components/Forms/ApiForm.d.ts +1 -1
  16. package/dist/Components/Forms/ApiForm.js +1 -1
  17. package/dist/Components/Forms/Crud.js +486 -206
  18. package/dist/Components/Forms/CrudHistory.d.ts +66 -0
  19. package/dist/Components/Forms/CrudHistory.js +140 -0
  20. package/dist/Components/Forms/CrudParts.d.ts +5 -1
  21. package/dist/Components/Forms/CrudParts.js +38 -5
  22. package/dist/Components/Forms/ExcelImport.d.ts +23 -7
  23. package/dist/Components/Forms/ExcelImport.js +189 -60
  24. package/dist/Components/Forms/Form.js +9 -14
  25. package/dist/Components/Forms/SubmitForm.d.ts +11 -2
  26. package/dist/Components/Forms/SubmitForm.js +23 -12
  27. package/dist/Components/Inputs/CodeBox.d.ts +69 -0
  28. package/dist/Components/Inputs/CodeBox.js +316 -0
  29. package/dist/Components/Inputs/DatePicker.js +4 -1
  30. package/dist/Components/Inputs/DateRangePicker.js +4 -1
  31. package/dist/Components/Inputs/DateTimePicker.js +4 -1
  32. package/dist/Components/Inputs/Dropdown.js +4 -1
  33. package/dist/Components/Inputs/DropdownParts.d.ts +1 -1
  34. package/dist/Components/Inputs/DropdownParts.js +4 -6
  35. package/dist/Components/Inputs/InputRenderer.js +3 -0
  36. package/dist/Components/Inputs/NumberBox.js +26 -2
  37. package/dist/Components/Inputs/RowsInput.js +2 -2
  38. package/dist/Components/Inputs/Slider.js +4 -1
  39. package/dist/Components/Inputs/TabularForm.js +42 -10
  40. package/dist/Components/Inputs/TextArea.js +4 -1
  41. package/dist/Components/Inputs/TextBox.js +4 -1
  42. package/dist/Components/Inputs/TimePanel.js +4 -3
  43. package/dist/Components/Inputs/TimePicker.js +4 -1
  44. package/dist/Components/Inputs/TreeView.d.ts +55 -5
  45. package/dist/Components/Inputs/TreeView.js +372 -23
  46. package/dist/Components/Layouts/Popup.js +1 -4
  47. package/dist/Components/Layouts/ThemePicker.d.ts +21 -3
  48. package/dist/Components/Layouts/ThemePicker.js +89 -6
  49. package/dist/Components/Layouts/ThemeScript.d.ts +4 -2
  50. package/dist/Components/Layouts/ThemeScript.js +4 -2
  51. package/dist/Components/Layouts/ThemeSwitcher.d.ts +6 -3
  52. package/dist/Components/Layouts/ThemeSwitcher.js +9 -8
  53. package/dist/Components/Viewers/Avatar.d.ts +21 -0
  54. package/dist/Components/Viewers/Avatar.js +21 -0
  55. package/dist/Components/Viewers/DataTable.js +252 -62
  56. package/dist/Components/Viewers/DataTableColumns.d.ts +7 -3
  57. package/dist/Components/Viewers/DataTableColumns.js +25 -68
  58. package/dist/Components/Viewers/DataTableFilters.d.ts +21 -0
  59. package/dist/Components/Viewers/DataTableFilters.js +68 -0
  60. package/dist/Components/Viewers/DataTableParts.d.ts +28 -10
  61. package/dist/Components/Viewers/DataTableParts.js +42 -13
  62. package/dist/Helpers/AnimationHelpers.d.ts +11 -0
  63. package/dist/Helpers/AnimationHelpers.js +20 -2
  64. package/dist/Helpers/ApiModules.d.ts +3 -34
  65. package/dist/Helpers/ApiModules.js +3 -3
  66. package/dist/Helpers/ApiRoutes.d.ts +68 -26
  67. package/dist/Helpers/ApiRoutes.js +31 -27
  68. package/dist/Helpers/ArrayHelpers.d.ts +0 -0
  69. package/dist/Helpers/ArrayHelpers.js +2 -0
  70. package/dist/Helpers/CodeHelpers.d.ts +97 -0
  71. package/dist/Helpers/CodeHelpers.js +332 -0
  72. package/dist/Helpers/CrudBackend.d.ts +101 -8
  73. package/dist/Helpers/CrudBackend.js +172 -11
  74. package/dist/Helpers/CrudColumns.d.ts +51 -0
  75. package/dist/Helpers/CrudColumns.js +69 -0
  76. package/dist/Helpers/CrudHelpers.d.ts +50 -5
  77. package/dist/Helpers/CrudHelpers.js +109 -12
  78. package/dist/Helpers/ExcelBackend.d.ts +3 -3
  79. package/dist/Helpers/ExcelBackend.js +8 -2
  80. package/dist/Helpers/ExcelHelpers.d.ts +36 -8
  81. package/dist/Helpers/ExcelHelpers.js +55 -13
  82. package/dist/Helpers/FormStore.d.ts +9 -5
  83. package/dist/Helpers/FormStore.js +25 -5
  84. package/dist/Helpers/InputParamsHelpers.js +8 -0
  85. package/dist/Helpers/MessageBuilder.js +3 -0
  86. package/dist/Helpers/NexusHelpers.d.ts +0 -0
  87. package/dist/Helpers/NexusHelpers.js +1 -0
  88. package/dist/Helpers/NumberHelpers.d.ts +7 -1
  89. package/dist/Helpers/NumberHelpers.js +41 -3
  90. package/dist/Helpers/PopoverHelpers.d.ts +17 -5
  91. package/dist/Helpers/PopoverHelpers.js +52 -17
  92. package/dist/Helpers/PopoverHooks.d.ts +24 -0
  93. package/dist/Helpers/PopoverHooks.js +62 -0
  94. package/dist/Helpers/RequestCache.d.ts +35 -0
  95. package/dist/Helpers/RequestCache.js +113 -0
  96. package/dist/Helpers/RowsStore.d.ts +7 -2
  97. package/dist/Helpers/RowsStore.js +15 -3
  98. package/dist/Helpers/TableFilters.d.ts +25 -0
  99. package/dist/Helpers/TableFilters.js +162 -0
  100. package/dist/Helpers/TableHelpers.d.ts +7 -4
  101. package/dist/Helpers/TableHelpers.js +15 -6
  102. package/dist/Helpers/TableJoins.d.ts +70 -0
  103. package/dist/Helpers/TableJoins.js +250 -0
  104. package/dist/Helpers/TableStore.d.ts +16 -3
  105. package/dist/Helpers/TableStore.js +25 -5
  106. package/dist/Helpers/ThemeHelpers.d.ts +21 -4
  107. package/dist/Helpers/ThemeHelpers.js +46 -9
  108. package/dist/Helpers/TreeStore.d.ts +225 -11
  109. package/dist/Helpers/TreeStore.js +683 -90
  110. package/dist/Helpers/UserPreferences.d.ts +22 -0
  111. package/dist/Helpers/UserPreferences.js +116 -0
  112. package/dist/Interfaces/CrudInterfaces.d.ts +67 -10
  113. package/dist/Interfaces/ExcelInterfaces.d.ts +12 -0
  114. package/dist/Interfaces/FormInterfaces.d.ts +37 -6
  115. package/dist/Interfaces/InputInterfaces.d.ts +24 -0
  116. package/dist/Interfaces/MessageInterfaces.d.ts +1 -1
  117. package/dist/Interfaces/PreferenceInterfaces.d.ts +51 -0
  118. package/dist/Interfaces/PreferenceInterfaces.js +25 -0
  119. package/dist/Interfaces/TableInterfaces.d.ts +145 -16
  120. package/dist/Interfaces/ThemeInterfaces.d.ts +25 -0
  121. package/dist/Interfaces/ThemeInterfaces.js +15 -0
  122. package/dist/Services/ApiProxy.d.ts +36 -1
  123. package/dist/Services/ApiProxy.js +182 -52
  124. package/dist/Services/ApiProxyRules.d.ts +203 -0
  125. package/dist/Services/ApiProxyRules.js +426 -0
  126. package/dist/Services/ExcelApi.d.ts +3 -1
  127. package/dist/Services/ExcelApi.js +1 -1
  128. package/dist/Services/JoinSources.d.ts +48 -0
  129. package/dist/Services/JoinSources.js +74 -0
  130. package/dist/Services/ServerApi.d.ts +13 -2
  131. package/dist/Services/ServerApi.js +8 -3
  132. package/dist/Services/ThemeService.d.ts +18 -2
  133. package/dist/Services/ThemeService.js +80 -16
  134. package/dist/Shared.Index.d.ts +6 -0
  135. package/dist/Shared.Index.js +6 -0
  136. package/package.json +4 -2
  137. package/src/Styles/Nexus.Avatar.css +79 -0
  138. package/src/Styles/Nexus.Base.css +51 -4
  139. package/src/Styles/Nexus.Button.css +27 -0
  140. package/src/Styles/Nexus.Calendar.css +4 -6
  141. package/src/Styles/Nexus.Chat.css +5 -51
  142. package/src/Styles/Nexus.Choice.css +4 -6
  143. package/src/Styles/Nexus.Crud.css +16 -9
  144. package/src/Styles/Nexus.Dropdown.css +12 -61
  145. package/src/Styles/Nexus.Editor.css +4 -6
  146. package/src/Styles/Nexus.Excel.css +34 -15
  147. package/src/Styles/Nexus.Form.css +25 -67
  148. package/src/Styles/Nexus.Index.css +1 -0
  149. package/src/Styles/Nexus.Input.css +794 -637
  150. package/src/Styles/Nexus.Markdown.css +0 -28
  151. package/src/Styles/Nexus.Menu.css +8 -30
  152. package/src/Styles/Nexus.Popup.css +22 -29
  153. package/src/Styles/Nexus.RichText.css +2 -32
  154. package/src/Styles/Nexus.Rows.css +51 -10
  155. package/src/Styles/Nexus.Slider.css +3 -5
  156. package/src/Styles/Nexus.Tab.Buttons.css +20 -7
  157. package/src/Styles/Nexus.Table.css +1387 -1296
  158. package/src/Styles/Nexus.Theme.Picker.css +342 -232
  159. package/src/Styles/Nexus.Time.css +9 -12
  160. package/src/Styles/Nexus.Toast.css +6 -8
  161. package/src/Styles/Nexus.Tokens.css +17 -0
  162. package/src/Styles/Nexus.Tree.css +76 -11
@@ -1,232 +1,342 @@
1
- @layer nexus.components {
2
- /* Popover panel: a native [popover] element, placed by the component that opens it. In dark mode it is a step
3
- lighter than the surface it floats over, so it reads as raised.
4
- (The shared panel lives in the theme picker's file because that is where it was first needed; it belongs to
5
- every popover, not to the picker. Moving it would have to move its place in Nexus.Index.css with it.) */
6
- .nx-popover {
7
- /* The arrow's base and depth. Keep in step with ARROW_BASE and ARROW_DEPTH in Helpers/PopoverHelpers.ts, which
8
- work out the room the arrow needs, and with the two paths below, which are drawn for exactly these. */
9
- --nx-popover-arrow: 26px;
10
- --nx-popover-arrow-depth: 11px;
11
- /* The panel's edge, shared with the arrow's own: a panel that changes it changes both. */
12
- --nx-popover-edge: var(--nx-border);
13
-
14
- margin: 0;
15
- /* The browser's own [popover] rule says `overflow: auto`, which would cut the arrow off at the panel's edge and
16
- put a pair of scroll bars on a panel that fits. A panel that has to scroll scrolls a block inside itself. */
17
- overflow: visible;
18
- padding: var(--nx-space-4);
19
- border: 1px solid var(--nx-popover-edge);
20
- border-radius: var(--nx-radius-lg);
21
- background: light-dark(var(--nx-surface), var(--nx-surface-2));
22
- color: var(--nx-text);
23
- box-shadow: var(--nx-shadow);
24
- }
25
-
26
- /*
27
- * The arrow that ties a panel to the button or the place it opened from: an isosceles tail, 26px across and 11px
28
- * deep, with a slightly rounded tip. It is drawn in two layers, because a shape with a line along two of its sides
29
- * and nothing along the third cannot be one: `::before` is the whole tail in the panel's edge colour, `::after`
30
- * the same tail a pixel smaller on both slopes and a pixel longer at the base, in the panel's own background. What
31
- * is left of the first between them is the 1px line along the slopes; the second covers the panel's border across
32
- * the base, so the tail opens into the panel with no seam and no line across its mouth. Both paths are drawn for
33
- * the sizes above: change one and the other two have to be worked out again.
34
- *
35
- * `placeUnderAnchor` and `placeAtPoint` (Helpers/PopoverHelpers.ts) say which edge carries the tail and where
36
- * along that edge its tip sits, and leave both out when it would point at nothing. Nothing here animates: the tail
37
- * is drawn with the panel, so it is there in the first frame whether the opening animation runs or not.
38
- */
39
- .nx-popover[data-nx-arrow]::before,
40
- .nx-popover[data-nx-arrow]::after {
41
- content: "";
42
- position: absolute;
43
- /* The middle is only a fallback: the two are always written together, and taken away together. */
44
- left: var(--nx-popover-arrow-x, 50%);
45
- width: var(--nx-popover-arrow);
46
- /* A pixel past the tail's depth: the panel's own border, which the fill covers. */
47
- height: calc(var(--nx-popover-arrow-depth) + 1px);
48
- margin-left: calc(var(--nx-popover-arrow) / -2);
49
- }
50
-
51
- /* The tail's outline: tip rounded by 2px, feet on the panel's border line. */
52
- .nx-popover[data-nx-arrow]::before {
53
- background: var(--nx-popover-edge);
54
- clip-path: path("M 0 11 L 11.708 1.093 A 2 2 0 0 1 14.292 1.093 L 26 11 Z");
55
- }
56
-
57
- /* The fill: the same tail, 1px in from each slope (so the outline is left as a line) and 1px further down. */
58
- .nx-popover[data-nx-arrow]::after {
59
- background: inherit;
60
- clip-path: path("M 0.367 12 L 12.354 1.857 A 1 1 0 0 1 13.646 1.857 L 25.633 12 Z");
61
- }
62
-
63
- /* Below its anchor: the tail stands on the panel's top edge, pointing up. */
64
- .nx-popover[data-nx-arrow="below"]::before,
65
- .nx-popover[data-nx-arrow="below"]::after {
66
- top: calc(-1px - var(--nx-popover-arrow-depth));
67
- }
68
-
69
- /* Above its anchor: the same tail turned over, on the bottom edge. */
70
- .nx-popover[data-nx-arrow="above"]::before,
71
- .nx-popover[data-nx-arrow="above"]::after {
72
- bottom: calc(-1px - var(--nx-popover-arrow-depth));
73
- transform: scaleY(-1);
74
- }
75
-
76
- .nx-theme-switcher__panel {
77
- position: fixed;
78
- inset: auto;
79
- width: min(340px, calc(100vw - 16px));
80
- max-height: calc(100vh - 80px);
81
- }
82
-
83
- /* A shut popover is hidden by the browser, so the panel takes its layout only while it is open. */
84
- .nx-theme-switcher__panel:popover-open {
85
- display: flex;
86
- }
87
-
88
- /* The picker scrolls, not the panel: a panel that scrolls its own box would cut its arrow off. */
89
- .nx-theme-switcher__panel > .nx-theme-picker {
90
- flex: 1 1 auto;
91
- min-height: 0;
92
- overflow-y: auto;
93
- overscroll-behavior: contain;
94
- }
95
-
96
- .nx-theme-picker {
97
- display: grid;
98
- gap: var(--nx-space-4);
99
- }
100
-
101
- .nx-theme-picker__group {
102
- min-width: 0;
103
- margin: 0;
104
- padding: 0;
105
- border: 0;
106
- }
107
-
108
- .nx-theme-picker__legend {
109
- margin: 0 0 var(--nx-space-2);
110
- padding: 0;
111
- color: var(--nx-text-muted);
112
- font-size: var(--nx-text-xs);
113
- font-weight: 600;
114
- letter-spacing: 0.06em;
115
- text-transform: uppercase;
116
- }
117
-
118
- /* Theme cards */
119
- .nx-theme-picker__grid {
120
- display: grid;
121
- grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
122
- gap: var(--nx-space-2);
123
- }
124
-
125
- .nx-theme-option {
126
- display: grid;
127
- gap: 6px;
128
- padding: 5px;
129
- border: 1px solid var(--nx-border);
130
- border-radius: var(--nx-radius);
131
- background: var(--nx-surface);
132
- color: var(--nx-text);
133
- cursor: pointer;
134
- transition:
135
- border-color var(--nx-duration),
136
- box-shadow var(--nx-duration);
137
- }
138
-
139
- .nx-theme-option:hover {
140
- border-color: var(--nx-border-strong);
141
- }
142
-
143
- .nx-theme-option:has(:checked) {
144
- border-color: var(--nx-primary);
145
- box-shadow: 0 0 0 1px var(--nx-primary);
146
- }
147
-
148
- .nx-theme-option:has(:focus-visible) {
149
- box-shadow: 0 0 0 1px var(--nx-primary), var(--nx-focus-ring);
150
- }
151
-
152
- /* Drawn with the previewed theme's tokens, because the element carries its data-nx-theme. */
153
- .nx-theme-option__preview {
154
- display: block;
155
- padding: 7px;
156
- border-radius: var(--nx-radius-sm);
157
- background: var(--nx-bg);
158
- }
159
-
160
- .nx-theme-option__window {
161
- display: grid;
162
- gap: 5px;
163
- padding: 7px;
164
- border: 1px solid var(--nx-border);
165
- border-radius: var(--nx-radius-sm);
166
- background: var(--nx-surface);
167
- }
168
-
169
- .nx-theme-option__line {
170
- width: 78%;
171
- height: 5px;
172
- border-radius: 3px;
173
- background: var(--nx-text);
174
- }
175
-
176
- .nx-theme-option__line--muted {
177
- width: 52%;
178
- background: var(--nx-text-muted);
179
- }
180
-
181
- .nx-theme-option__swatches {
182
- display: flex;
183
- align-items: center;
184
- gap: 4px;
185
- margin-top: 2px;
186
- }
187
-
188
- .nx-theme-option__pill {
189
- width: 26px;
190
- height: 10px;
191
- margin-right: auto;
192
- border-radius: var(--nx-radius-full);
193
- background: var(--nx-primary);
194
- }
195
-
196
- .nx-theme-option__dot {
197
- width: 9px;
198
- height: 9px;
199
- border-radius: 50%;
200
- }
201
-
202
- .nx-theme-option__dot--success {
203
- background: var(--nx-success);
204
- }
205
-
206
- .nx-theme-option__dot--warning {
207
- background: var(--nx-warning);
208
- }
209
-
210
- .nx-theme-option__dot--danger {
211
- background: var(--nx-danger);
212
- }
213
-
214
- .nx-theme-option__name {
215
- display: flex;
216
- align-items: center;
217
- justify-content: space-between;
218
- gap: 4px;
219
- padding: 0 3px 1px;
220
- font-size: var(--nx-text-sm);
221
- font-weight: 500;
222
- }
223
-
224
- .nx-theme-option__check {
225
- color: var(--nx-primary);
226
- visibility: hidden;
227
- }
228
-
229
- .nx-theme-option:has(:checked) .nx-theme-option__check {
230
- visibility: visible;
231
- }
232
- }
1
+ @layer nexus.components {
2
+ /* Popover panel: a native [popover] element, placed by the component that opens it. In dark mode it is a step
3
+ lighter than the surface it floats over, so it reads as raised.
4
+ (The shared panel lives in the theme picker's file because that is where it was first needed; it belongs to
5
+ every popover, not to the picker. Moving it would have to move its place in Nexus.Index.css with it.) */
6
+ .nx-popover {
7
+ /* The arrow's base and depth. Keep in step with ARROW_BASE and ARROW_DEPTH in Helpers/PopoverHelpers.ts, which
8
+ work out the room the arrow needs, and with the two paths below, which are drawn for exactly these. */
9
+ --nx-popover-arrow: 26px;
10
+ --nx-popover-arrow-depth: 11px;
11
+ /* The panel's edge, shared with the arrow's own: a panel that changes it changes both. */
12
+ --nx-popover-edge: var(--nx-border);
13
+ margin: 0;
14
+ /* The browser's own [popover] rule says `overflow: auto`, which would cut the arrow off at the panel's edge and
15
+ put a pair of scroll bars on a panel that fits. A panel that has to scroll scrolls a block inside itself. */
16
+ overflow: visible;
17
+ /* The room inside the edge: a panel sets `--nx-popover-pad` (a menu 4px, the table's panels 8px), never `padding`,
18
+ which this rule, loaded after theirs, would win over; its scroller (below) carries the right-hand part. */
19
+ padding: var(--nx-popover-pad, var(--nx-space-4));
20
+ border: 1px solid var(--nx-popover-edge);
21
+ border-radius: var(--nx-radius-lg);
22
+ background: light-dark(var(--nx-surface), var(--nx-surface-2));
23
+ color: var(--nx-text);
24
+ box-shadow: var(--nx-shadow);
25
+ /*
26
+ * Every panel fades in as it opens and out as it closes, briefly, and nothing waits for it: a closing panel keeps
27
+ * its layout (`display`) and its place on top (`overlay`) only while it fades, and takes no clicks meanwhile.
28
+ * With animations off it simply comes and goes.
29
+ */
30
+ transition:
31
+ opacity 120ms ease-out,
32
+ display 120ms allow-discrete,
33
+ overlay 120ms allow-discrete;
34
+ }
35
+
36
+ .nx-popover:not(:popover-open) {
37
+ opacity: 0;
38
+ pointer-events: none;
39
+ }
40
+
41
+ @starting-style {
42
+ .nx-popover:popover-open {
43
+ opacity: 0;
44
+ }
45
+ }
46
+
47
+ [data-nx-motion="reduce"] .nx-popover {
48
+ transition: none;
49
+ }
50
+
51
+ /*
52
+ * A panel whose content can be longer than the window marks the part that scrolls with `.nx-popover__scroll`, a
53
+ * child of the panel. The panel is then never taller than the room it was placed in (`--nx-popover-room`, written
54
+ * by the placing helpers: the room on the side of its button it opened on), and the scroller takes what is left.
55
+ * The scroller runs to the panel's right wall, so its scroll bar is the wall's and not a padding's width inside it,
56
+ * and it carries the panel's right padding inside itself; the panel's other children keep the same inset with a
57
+ * margin. Its right corners take the panel's rounding, so the bar is cut by the curve instead of drawn over it.
58
+ * A scroller cuts off what reaches past its edge, and a focus ring is drawn outside its box (3px; a checkbox's
59
+ * outline 4px). So on its other three sides it reaches 4px into the panel's padding and pads itself by as much:
60
+ * what it holds stays where it was, and a field at its edge shows its whole ring (every panel's padding is 4px or
61
+ * more). Focus scrolls a field as far clear of the edges (`scroll-padding`), so one left flush with an edge by a
62
+ * scroll shows its ring too.
63
+ */
64
+ .nx-popover:has(> .nx-popover__scroll) {
65
+ max-height: var(--nx-popover-room, calc(100vh - 16px));
66
+ padding-right: 0;
67
+ }
68
+
69
+ /* A shut popover is hidden by the browser, so the panel takes its layout only while it is open. */
70
+ .nx-popover:has(> .nx-popover__scroll):popover-open {
71
+ display: flex;
72
+ flex-direction: column;
73
+ }
74
+
75
+ .nx-popover:has(> .nx-popover__scroll) > :not(.nx-popover__scroll) {
76
+ margin-right: var(--nx-popover-pad, var(--nx-space-4));
77
+ }
78
+
79
+ .nx-popover__scroll {
80
+ flex: 1 1 auto;
81
+ min-height: 0;
82
+ margin: calc(var(--nx-space-1) * -1);
83
+ margin-right: 0;
84
+ padding: var(--nx-space-1);
85
+ padding-right: var(--nx-popover-pad, var(--nx-space-4));
86
+ scroll-padding: var(--nx-space-1);
87
+ overflow: hidden auto;
88
+ overscroll-behavior: contain;
89
+ border-radius: 0 calc(var(--nx-radius-lg) - 1px) calc(var(--nx-radius-lg) - 1px) 0;
90
+ }
91
+
92
+ /*
93
+ * Where the scroller reaches one of the panel's left corners (it is the panel's first or last child, as a menu's
94
+ * list is), its own corner follows the panel's curve: the panel's inner radius less the room between their edges,
95
+ * which is the panel's padding less the 4px taken back. A menu's 4px puts the two edges together, so its corner takes
96
+ * the whole 9px; an 8px padding leaves 5px, the theme panel's 16px none. Square, a scrolled item's highlight ran
97
+ * over the rounded border.
98
+ */
99
+ .nx-popover > .nx-popover__scroll:first-child {
100
+ border-top-left-radius: max(0px, var(--nx-radius-lg) - 1px - (var(--nx-popover-pad, var(--nx-space-4)) - var(--nx-space-1)));
101
+ }
102
+
103
+ .nx-popover > .nx-popover__scroll:last-child {
104
+ border-bottom-left-radius: max(0px, var(--nx-radius-lg) - 1px - (var(--nx-popover-pad, var(--nx-space-4)) - var(--nx-space-1)));
105
+ }
106
+
107
+ /*
108
+ * The arrow that ties a panel to the button or the place it opened from: an isosceles tail, 26px across and 11px
109
+ * deep, with a slightly rounded tip. It is drawn in two layers, because a shape with a line along two of its sides
110
+ * and nothing along the third cannot be one: `::before` is the whole tail in the panel's edge colour, `::after`
111
+ * the same tail a pixel smaller on both slopes and a pixel longer at the base, in the panel's own background. What
112
+ * is left of the first between them is the 1px line along the slopes; the second covers the panel's border across
113
+ * the base, so the tail opens into the panel with no seam and no line across its mouth. Both paths are drawn for
114
+ * the sizes above: change one and the other two have to be worked out again.
115
+ *
116
+ * `placeUnderAnchor` and `placeAtPoint` (Helpers/PopoverHelpers.ts) say which edge carries the tail and where
117
+ * along that edge its tip sits, and leave both out when it would point at nothing. Nothing here animates: the tail
118
+ * is drawn with the panel, so it is there in the first frame whether the opening animation runs or not.
119
+ */
120
+ .nx-popover[data-nx-arrow]::before,
121
+ .nx-popover[data-nx-arrow]::after {
122
+ content: "";
123
+ position: absolute;
124
+ /* The middle is only a fallback: the two are always written together, and taken away together. */
125
+ left: var(--nx-popover-arrow-x, 50%);
126
+ width: var(--nx-popover-arrow);
127
+ /* A pixel past the tail's depth: the panel's own border, which the fill covers. */
128
+ height: calc(var(--nx-popover-arrow-depth) + 1px);
129
+ margin-left: calc(var(--nx-popover-arrow) / -2);
130
+ }
131
+
132
+ /* The tail's outline: tip rounded by 2px, feet on the panel's border line. */
133
+ .nx-popover[data-nx-arrow]::before {
134
+ background: var(--nx-popover-edge);
135
+ clip-path: path("M 0 11 L 11.708 1.093 A 2 2 0 0 1 14.292 1.093 L 26 11 Z");
136
+ }
137
+
138
+ /* The fill: the same tail, 1px in from each slope (so the outline is left as a line) and 1px further down. */
139
+ .nx-popover[data-nx-arrow]::after {
140
+ background: inherit;
141
+ clip-path: path("M 0.367 12 L 12.354 1.857 A 1 1 0 0 1 13.646 1.857 L 25.633 12 Z");
142
+ }
143
+
144
+ /* Below its anchor: the tail stands on the panel's top edge, pointing up. */
145
+ .nx-popover[data-nx-arrow="below"]::before,
146
+ .nx-popover[data-nx-arrow="below"]::after {
147
+ top: calc(-1px - var(--nx-popover-arrow-depth));
148
+ }
149
+
150
+ /* Above its anchor: the same tail turned over, on the bottom edge. */
151
+ .nx-popover[data-nx-arrow="above"]::before,
152
+ .nx-popover[data-nx-arrow="above"]::after {
153
+ bottom: calc(-1px - var(--nx-popover-arrow-depth));
154
+ transform: scaleY(-1);
155
+ }
156
+
157
+ /* Wide enough for the sections to sit two to a row. */
158
+ .nx-theme-switcher__panel {
159
+ position: fixed;
160
+ inset: auto;
161
+ width: min(680px, calc(100vw - 16px));
162
+ }
163
+
164
+ .nx-theme-picker,
165
+ .nx-theme-switcher__body,
166
+ .nx-theme-picker__block {
167
+ display: grid;
168
+ gap: var(--nx-space-4);
169
+ }
170
+
171
+ /*
172
+ * In the panel every section is a cell of one grid: two to a row, the theme across both, and one under the other
173
+ * where the window is narrow. The two pickers' own wrappers step aside so their sections share that grid.
174
+ */
175
+ .nx-theme-switcher__body {
176
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
177
+ align-items: start;
178
+ column-gap: var(--nx-space-6);
179
+ }
180
+
181
+ .nx-theme-switcher__body > .nx-theme-picker {
182
+ display: contents;
183
+ }
184
+
185
+ .nx-theme-picker__block {
186
+ align-content: start;
187
+ min-width: 0;
188
+ gap: var(--nx-space-3);
189
+ }
190
+
191
+ .nx-theme-picker__block--wide {
192
+ grid-column: 1 / -1;
193
+ }
194
+
195
+ /* A section's name, a line above it, a step stronger than the small headings under it. */
196
+ .nx-theme-picker__section {
197
+ margin: 0;
198
+ padding-top: var(--nx-space-4);
199
+ border-top: 1px solid var(--nx-border);
200
+ color: var(--nx-text);
201
+ font-size: var(--nx-text-sm);
202
+ font-weight: 600;
203
+ }
204
+
205
+ /* The first section starts at the top of its picker, with nothing above it to set it off from. */
206
+ .nx-theme-picker:first-child > .nx-theme-picker__block:first-child > .nx-theme-picker__section {
207
+ padding-top: 0;
208
+ border-top: 0;
209
+ }
210
+
211
+ .nx-theme-picker__group {
212
+ min-width: 0;
213
+ margin: 0;
214
+ padding: 0;
215
+ border: 0;
216
+ }
217
+
218
+ .nx-theme-picker__legend {
219
+ margin: 0 0 var(--nx-space-2);
220
+ padding: 0;
221
+ color: var(--nx-text-muted);
222
+ font-size: var(--nx-text-xs);
223
+ font-weight: 600;
224
+ letter-spacing: 0.06em;
225
+ text-transform: uppercase;
226
+ }
227
+
228
+ /* Theme cards */
229
+ .nx-theme-picker__grid {
230
+ display: grid;
231
+ grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
232
+ gap: var(--nx-space-2);
233
+ }
234
+
235
+ .nx-theme-option {
236
+ display: grid;
237
+ gap: 6px;
238
+ padding: 5px;
239
+ border: 1px solid var(--nx-border);
240
+ border-radius: var(--nx-radius);
241
+ background: var(--nx-surface);
242
+ color: var(--nx-text);
243
+ cursor: pointer;
244
+ transition:
245
+ border-color var(--nx-duration),
246
+ box-shadow var(--nx-duration);
247
+ }
248
+
249
+ .nx-theme-option:hover {
250
+ border-color: var(--nx-border-strong);
251
+ }
252
+
253
+ .nx-theme-option:has(:checked) {
254
+ border-color: var(--nx-primary);
255
+ box-shadow: 0 0 0 1px var(--nx-primary);
256
+ }
257
+
258
+ .nx-theme-option:has(:focus-visible) {
259
+ box-shadow: 0 0 0 1px var(--nx-primary), var(--nx-focus-ring);
260
+ }
261
+
262
+ /* Drawn with the previewed theme's tokens, because the element carries its data-nx-theme. */
263
+ .nx-theme-option__preview {
264
+ display: block;
265
+ padding: 7px;
266
+ border-radius: var(--nx-radius-sm);
267
+ background: var(--nx-bg);
268
+ }
269
+
270
+ .nx-theme-option__window {
271
+ display: grid;
272
+ gap: 5px;
273
+ padding: 7px;
274
+ border: 1px solid var(--nx-border);
275
+ border-radius: var(--nx-radius-sm);
276
+ background: var(--nx-surface);
277
+ }
278
+
279
+ .nx-theme-option__line {
280
+ width: 78%;
281
+ height: 5px;
282
+ border-radius: 3px;
283
+ background: var(--nx-text);
284
+ }
285
+
286
+ .nx-theme-option__line--muted {
287
+ width: 52%;
288
+ background: var(--nx-text-muted);
289
+ }
290
+
291
+ .nx-theme-option__swatches {
292
+ display: flex;
293
+ align-items: center;
294
+ gap: 4px;
295
+ margin-top: 2px;
296
+ }
297
+
298
+ .nx-theme-option__pill {
299
+ width: 26px;
300
+ height: 10px;
301
+ margin-right: auto;
302
+ border-radius: var(--nx-radius-full);
303
+ background: var(--nx-primary);
304
+ }
305
+
306
+ .nx-theme-option__dot {
307
+ width: 9px;
308
+ height: 9px;
309
+ border-radius: 50%;
310
+ }
311
+
312
+ .nx-theme-option__dot--success {
313
+ background: var(--nx-success);
314
+ }
315
+
316
+ .nx-theme-option__dot--warning {
317
+ background: var(--nx-warning);
318
+ }
319
+
320
+ .nx-theme-option__dot--danger {
321
+ background: var(--nx-danger);
322
+ }
323
+
324
+ .nx-theme-option__name {
325
+ display: flex;
326
+ align-items: center;
327
+ justify-content: space-between;
328
+ gap: 4px;
329
+ padding: 0 3px 1px;
330
+ font-size: var(--nx-text-sm);
331
+ font-weight: 500;
332
+ }
333
+
334
+ .nx-theme-option__check {
335
+ color: var(--nx-primary);
336
+ visibility: hidden;
337
+ }
338
+
339
+ .nx-theme-option:has(:checked) .nx-theme-option__check {
340
+ visibility: visible;
341
+ }
342
+ }
@@ -23,7 +23,6 @@
23
23
  --nx-time-item: 32px;
24
24
  --nx-time-rows: 7;
25
25
  --nx-time-bg: light-dark(var(--nx-surface), var(--nx-surface-2));
26
- --nx-time-scrollbar: color-mix(in oklab, var(--nx-text-subtle) 75%, transparent);
27
26
 
28
27
  display: grid;
29
28
  color: var(--nx-text);
@@ -46,8 +45,6 @@
46
45
  overflow-y: auto;
47
46
  overscroll-behavior: contain;
48
47
  outline: none;
49
- scrollbar-width: thin;
50
- scrollbar-color: transparent transparent;
51
48
  transition: scrollbar-color var(--nx-duration);
52
49
  }
53
50
 
@@ -56,8 +53,12 @@
56
53
  min-width: 56px;
57
54
  }
58
55
 
59
- .nx-time__column:is(:hover, :focus-within) {
60
- scrollbar-color: var(--nx-time-scrollbar) transparent;
56
+ /*
57
+ * The columns stand side by side, so a bar in each would draw a stripe between every pair: the thumb stays hidden
58
+ * until the pointer or the keys are in the column, and then it is the shared bar (Nexus.Base.css).
59
+ */
60
+ .nx-time__column:not(:hover, :focus-within) {
61
+ scrollbar-color: transparent transparent;
61
62
  }
62
63
 
63
64
  .nx-time__column + .nx-time__column {
@@ -124,8 +125,6 @@
124
125
  overflow-y: auto;
125
126
  overscroll-behavior: contain;
126
127
  outline: none;
127
- scrollbar-width: thin;
128
- scrollbar-color: var(--nx-time-scrollbar) transparent;
129
128
  }
130
129
 
131
130
  .nx-time__slot {
@@ -138,10 +137,8 @@
138
137
  color: var(--nx-text-muted);
139
138
  }
140
139
 
141
- @media (prefers-reduced-motion: reduce) {
142
- .nx-time__item,
143
- .nx-time__column {
144
- transition: none;
145
- }
140
+ [data-nx-motion="reduce"] .nx-time__item,
141
+ [data-nx-motion="reduce"] .nx-time__column {
142
+ transition: none;
146
143
  }
147
144
  }
@@ -252,14 +252,12 @@
252
252
  }
253
253
  }
254
254
 
255
- @media (prefers-reduced-motion: reduce) {
256
- .nx-toast {
257
- transition: none;
258
- }
255
+ [data-nx-motion="reduce"] .nx-toast {
256
+ transition: none;
257
+ }
259
258
 
260
- .nx-toast:popover-open,
261
- .nx-toast--closing:popover-open {
262
- animation: none;
263
- }
259
+ [data-nx-motion="reduce"] .nx-toast:popover-open,
260
+ [data-nx-motion="reduce"] .nx-toast--closing:popover-open {
261
+ animation: none;
264
262
  }
265
263
  }