neba 1.12.0 → 1.15.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 (348) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +50 -56
  3. package/dist/a2ui/catalog.json +1466 -0
  4. package/dist/a2ui/components.d.ts +21 -0
  5. package/dist/a2ui/components.js +1 -0
  6. package/dist/a2ui/index.d.ts +62 -0
  7. package/dist/a2ui/index.js +1 -0
  8. package/dist/a2ui/schema.d.ts +47 -0
  9. package/dist/a2ui/schema.js +1 -0
  10. package/dist/components/accordion/Accordion.d.ts +23 -1
  11. package/dist/components/accordion/Accordion.js +1 -1
  12. package/dist/components/agent-steps/AgentSteps.d.ts +80 -0
  13. package/dist/components/agent-steps/AgentSteps.js +1 -0
  14. package/dist/components/agent-steps/index.d.ts +2 -0
  15. package/dist/components/agent-steps/index.js +1 -0
  16. package/dist/components/alert/Alert.js +1 -1
  17. package/dist/components/anchor/Anchor.d.ts +3 -2
  18. package/dist/components/anchor/Anchor.js +1 -1
  19. package/dist/components/animate-appear/AnimateAppear.js +1 -1
  20. package/dist/components/animate-blink/AnimateBlink.d.ts +3 -0
  21. package/dist/components/animate-blink/AnimateBlink.js +1 -1
  22. package/dist/components/animate-counter/AnimateCounter.d.ts +3 -1
  23. package/dist/components/animate-counter/AnimateCounter.js +1 -1
  24. package/dist/components/animate-fade/AnimateFade.js +1 -1
  25. package/dist/components/animate-float/AnimateFloat.js +1 -1
  26. package/dist/components/animate-grow/AnimateGrow.js +1 -1
  27. package/dist/components/animate-headline/AnimateHeadline.d.ts +9 -2
  28. package/dist/components/animate-headline/AnimateHeadline.js +1 -1
  29. package/dist/components/animate-lighting/AnimateLighting.d.ts +1 -1
  30. package/dist/components/animate-lighting/AnimateLighting.js +1 -1
  31. package/dist/components/animate-marquee/AnimateMarquee.d.ts +19 -8
  32. package/dist/components/animate-marquee/AnimateMarquee.js +1 -1
  33. package/dist/components/animate-reveal/AnimateReveal.js +1 -1
  34. package/dist/components/animate-rotate/AnimateRotate.js +1 -1
  35. package/dist/components/animate-scramble/AnimateScramble.d.ts +5 -3
  36. package/dist/components/animate-scramble/AnimateScramble.js +1 -1
  37. package/dist/components/animate-shake/AnimateShake.js +1 -1
  38. package/dist/components/animate-slide/AnimateSlide.js +1 -1
  39. package/dist/components/animate-split/AnimateSplit.d.ts +3 -1
  40. package/dist/components/animate-split/AnimateSplit.js +1 -1
  41. package/dist/components/animate-typing/AnimateTyping.d.ts +12 -4
  42. package/dist/components/animate-typing/AnimateTyping.js +1 -1
  43. package/dist/components/animate-zoom/AnimateZoom.js +1 -1
  44. package/dist/components/app-logo/AppLogo.js +1 -1
  45. package/dist/components/approval/Approval.d.ts +115 -0
  46. package/dist/components/approval/Approval.js +1 -0
  47. package/dist/components/approval/index.d.ts +2 -0
  48. package/dist/components/approval/index.js +1 -0
  49. package/dist/components/area-chart/AreaChart.js +1 -1
  50. package/dist/components/aspect-ratio/AspectRatio.d.ts +6 -4
  51. package/dist/components/aspect-ratio/AspectRatio.js +1 -1
  52. package/dist/components/avatar/Avatar.js +1 -1
  53. package/dist/components/badge/Badge.js +1 -1
  54. package/dist/components/bar-chart/BarChart.d.ts +1 -1
  55. package/dist/components/bar-chart/BarChart.js +1 -1
  56. package/dist/components/blockquote/Blockquote.js +1 -1
  57. package/dist/components/bottom-navigation/BottomNavigation.d.ts +12 -1
  58. package/dist/components/bottom-navigation/BottomNavigation.js +1 -1
  59. package/dist/components/box/Box.d.ts +5 -0
  60. package/dist/components/box/Box.js +1 -1
  61. package/dist/components/breadcrumb/Breadcrumb.d.ts +10 -0
  62. package/dist/components/breadcrumb/Breadcrumb.js +1 -1
  63. package/dist/components/button/Button.d.ts +11 -0
  64. package/dist/components/button/Button.js +1 -1
  65. package/dist/components/button-group/ButtonGroup.js +1 -1
  66. package/dist/components/calendar/Calendar.js +1 -1
  67. package/dist/components/card/Card.js +1 -1
  68. package/dist/components/carousel/Carousel.d.ts +17 -0
  69. package/dist/components/carousel/Carousel.js +1 -1
  70. package/dist/components/chat-bubble/ChatBubble.js +1 -1
  71. package/dist/components/checkbox/Checkbox.js +1 -1
  72. package/dist/components/chip/Chip.d.ts +3 -1
  73. package/dist/components/chip/Chip.js +1 -1
  74. package/dist/components/code-block/CodeBlock.d.ts +6 -3
  75. package/dist/components/code-block/CodeBlock.js +1 -1
  76. package/dist/components/collapsible/Collapsible.d.ts +11 -1
  77. package/dist/components/collapsible/Collapsible.js +1 -1
  78. package/dist/components/color-picker/ColorPicker.js +1 -1
  79. package/dist/components/combobox/Combobox.d.ts +29 -1
  80. package/dist/components/combobox/Combobox.js +1 -1
  81. package/dist/components/command-palette/CommandPalette.js +1 -1
  82. package/dist/components/confirm/Confirm.js +1 -1
  83. package/dist/components/container/Container.js +1 -1
  84. package/dist/components/context-window/ContextWindow.d.ts +103 -0
  85. package/dist/components/context-window/ContextWindow.js +1 -0
  86. package/dist/components/context-window/index.d.ts +2 -0
  87. package/dist/components/context-window/index.js +1 -0
  88. package/dist/components/data-list/DataList.js +1 -1
  89. package/dist/components/data-table/DataTable.d.ts +12 -1
  90. package/dist/components/data-table/DataTable.js +1 -1
  91. package/dist/components/date-picker/DatePicker.js +1 -1
  92. package/dist/components/date-range-picker/DateRangePicker.js +1 -1
  93. package/dist/components/date-time-picker/DateTimePicker.js +1 -1
  94. package/dist/components/dialog/Dialog.d.ts +12 -0
  95. package/dist/components/dialog/Dialog.js +1 -1
  96. package/dist/components/divider/Divider.js +1 -1
  97. package/dist/components/drawer/Drawer.d.ts +7 -0
  98. package/dist/components/drawer/Drawer.js +1 -1
  99. package/dist/components/empty/Empty.js +1 -1
  100. package/dist/components/fieldset/Fieldset.js +1 -1
  101. package/dist/components/file-picker/FilePicker.d.ts +12 -4
  102. package/dist/components/file-picker/FilePicker.js +1 -1
  103. package/dist/components/flex/Flex.d.ts +8 -2
  104. package/dist/components/flex/Flex.js +1 -1
  105. package/dist/components/floating-action-button/FloatingActionButton.d.ts +16 -1
  106. package/dist/components/floating-action-button/FloatingActionButton.js +1 -1
  107. package/dist/components/floating-bottom-navigation/FloatingBottomNavigation.js +1 -1
  108. package/dist/components/footer/Footer.d.ts +8 -0
  109. package/dist/components/footer/Footer.js +1 -1
  110. package/dist/components/form/Form.d.ts +4 -2
  111. package/dist/components/form/Form.js +1 -1
  112. package/dist/components/gallery/Gallery.d.ts +56 -3
  113. package/dist/components/gallery/Gallery.js +1 -1
  114. package/dist/components/gallery/GalleryViewer.js +1 -1
  115. package/dist/components/gauge-chart/GaugeChart.js +1 -1
  116. package/dist/components/grid/Grid.d.ts +11 -1
  117. package/dist/components/grid/Grid.js +1 -1
  118. package/dist/components/grid-container/GridContainer.js +1 -1
  119. package/dist/components/header/Header.js +1 -1
  120. package/dist/components/heatmap-chart/HeatmapChart.js +1 -1
  121. package/dist/components/highlight/Highlight.js +1 -1
  122. package/dist/components/hover-card/HoverCard.js +1 -1
  123. package/dist/components/how-to-steps/HowToSteps.d.ts +26 -1
  124. package/dist/components/how-to-steps/HowToSteps.js +1 -1
  125. package/dist/components/icon/Icon.js +1 -1
  126. package/dist/components/icon-button/IconButton.d.ts +0 -20
  127. package/dist/components/icon-button/IconButton.js +1 -1
  128. package/dist/components/image/Image.d.ts +162 -8
  129. package/dist/components/image/Image.js +1 -1
  130. package/dist/components/image/index.d.ts +1 -1
  131. package/dist/components/inline-citation/InlineCitation.d.ts +53 -0
  132. package/dist/components/inline-citation/InlineCitation.js +1 -0
  133. package/dist/components/inline-citation/index.d.ts +2 -0
  134. package/dist/components/inline-citation/index.js +1 -0
  135. package/dist/components/list/List.js +1 -1
  136. package/dist/components/menu/Menu.d.ts +7 -4
  137. package/dist/components/menu/Menu.js +1 -1
  138. package/dist/components/menubar/Menubar.js +1 -1
  139. package/dist/components/meter/Meter.d.ts +13 -0
  140. package/dist/components/meter/Meter.js +1 -1
  141. package/dist/components/mockup/Mockup.js +1 -1
  142. package/dist/components/navigation-menu/NavigationMenu.d.ts +10 -1
  143. package/dist/components/navigation-menu/NavigationMenu.js +1 -1
  144. package/dist/components/number-field/NumberField.d.ts +20 -6
  145. package/dist/components/number-field/NumberField.js +1 -1
  146. package/dist/components/otp-field/OtpField.d.ts +7 -0
  147. package/dist/components/otp-field/OtpField.js +1 -1
  148. package/dist/components/overlay/Overlay.d.ts +1 -1
  149. package/dist/components/overlay/Overlay.js +1 -1
  150. package/dist/components/page-layout/PageLayout.d.ts +11 -0
  151. package/dist/components/page-layout/PageLayout.js +1 -1
  152. package/dist/components/pagination/Pagination.js +1 -1
  153. package/dist/components/panes/Panes.d.ts +11 -0
  154. package/dist/components/panes/Panes.js +1 -1
  155. package/dist/components/pie-chart/PieChart.d.ts +5 -3
  156. package/dist/components/pie-chart/PieChart.js +1 -1
  157. package/dist/components/pill/Pill.d.ts +8 -0
  158. package/dist/components/pill/Pill.js +1 -1
  159. package/dist/components/popconfirm/Popconfirm.d.ts +10 -3
  160. package/dist/components/popconfirm/Popconfirm.js +1 -1
  161. package/dist/components/popover/Popover.d.ts +8 -1
  162. package/dist/components/popover/Popover.js +1 -1
  163. package/dist/components/portal/Portal.js +1 -1
  164. package/dist/components/progress-box/ProgressBox.js +1 -1
  165. package/dist/components/progress-circular/ProgressCircular.d.ts +10 -1
  166. package/dist/components/progress-circular/ProgressCircular.js +1 -1
  167. package/dist/components/progress-linear/ProgressLinear.d.ts +7 -1
  168. package/dist/components/progress-linear/ProgressLinear.js +1 -1
  169. package/dist/components/prompt-input/PromptInput.d.ts +104 -0
  170. package/dist/components/prompt-input/PromptInput.js +1 -0
  171. package/dist/components/prompt-input/index.d.ts +2 -0
  172. package/dist/components/prompt-input/index.js +1 -0
  173. package/dist/components/provider/NebaProvider.d.ts +3 -3
  174. package/dist/components/provider/NebaProvider.js +1 -1
  175. package/dist/components/radio-group/RadioGroup.js +1 -1
  176. package/dist/components/rating/Rating.d.ts +4 -1
  177. package/dist/components/rating/Rating.js +1 -1
  178. package/dist/components/reasoning/Reasoning.d.ts +78 -0
  179. package/dist/components/reasoning/Reasoning.js +1 -0
  180. package/dist/components/reasoning/index.d.ts +2 -0
  181. package/dist/components/reasoning/index.js +1 -0
  182. package/dist/components/scatter-chart/ScatterChart.js +1 -1
  183. package/dist/components/scroll-area/ScrollArea.js +1 -1
  184. package/dist/components/scroll-zone/ScrollZone.d.ts +18 -7
  185. package/dist/components/scroll-zone/ScrollZone.js +1 -1
  186. package/dist/components/segmented-button/SegmentedButton.js +1 -1
  187. package/dist/components/select/Select.d.ts +13 -0
  188. package/dist/components/select/Select.js +1 -1
  189. package/dist/components/shortcut/Shortcut.js +1 -1
  190. package/dist/components/show/Show.d.ts +6 -5
  191. package/dist/components/show/Show.js +1 -1
  192. package/dist/components/sidebar/Sidebar.d.ts +7 -5
  193. package/dist/components/sidebar/Sidebar.js +1 -1
  194. package/dist/components/sidebar/SidebarTrigger.js +1 -1
  195. package/dist/components/skeleton/Skeleton.d.ts +1 -1
  196. package/dist/components/skeleton/Skeleton.js +1 -1
  197. package/dist/components/slider/Slider.d.ts +49 -2
  198. package/dist/components/slider/Slider.js +1 -1
  199. package/dist/components/sources/Sources.d.ts +78 -0
  200. package/dist/components/sources/Sources.js +1 -0
  201. package/dist/components/sources/index.d.ts +2 -0
  202. package/dist/components/sources/index.js +1 -0
  203. package/dist/components/sparkline/Sparkline.js +1 -1
  204. package/dist/components/spoiler/Spoiler.js +1 -1
  205. package/dist/components/stack/Stack.d.ts +2 -2
  206. package/dist/components/stack/Stack.js +1 -1
  207. package/dist/components/statistic/Statistic.js +1 -1
  208. package/dist/components/streaming-text/StreamingText.d.ts +75 -0
  209. package/dist/components/streaming-text/StreamingText.js +1 -0
  210. package/dist/components/streaming-text/index.d.ts +2 -0
  211. package/dist/components/streaming-text/index.js +1 -0
  212. package/dist/components/switch/Switch.js +1 -1
  213. package/dist/components/table/Table.d.ts +8 -2
  214. package/dist/components/table/Table.js +1 -1
  215. package/dist/components/tabs/Tabs.d.ts +18 -0
  216. package/dist/components/tabs/Tabs.js +1 -1
  217. package/dist/components/text-field/TextField.js +1 -1
  218. package/dist/components/text-link/TextLink.js +1 -1
  219. package/dist/components/time-picker/TimePicker.js +1 -1
  220. package/dist/components/timeline/Timeline.d.ts +8 -0
  221. package/dist/components/timeline/Timeline.js +1 -1
  222. package/dist/components/timeline-chart/TimelineChart.js +1 -1
  223. package/dist/components/toast/Toast.d.ts +9 -3
  224. package/dist/components/toast/Toast.js +1 -1
  225. package/dist/components/toggle/Toggle.js +1 -1
  226. package/dist/components/toggle-group/ToggleGroup.js +1 -1
  227. package/dist/components/tool-call/ToolCall.d.ts +104 -0
  228. package/dist/components/tool-call/ToolCall.js +1 -0
  229. package/dist/components/tool-call/index.d.ts +2 -0
  230. package/dist/components/tool-call/index.js +1 -0
  231. package/dist/components/toolbar/Toolbar.d.ts +8 -0
  232. package/dist/components/toolbar/Toolbar.js +1 -1
  233. package/dist/components/tooltip/Tooltip.js +1 -1
  234. package/dist/components/tour/Tour.d.ts +2 -0
  235. package/dist/components/tour/Tour.js +1 -1
  236. package/dist/components/transfer/Transfer.js +1 -1
  237. package/dist/components/tree-select/TreeSelect.d.ts +2 -0
  238. package/dist/components/tree-select/TreeSelect.js +1 -1
  239. package/dist/components/tree-view/TreeView.d.ts +6 -0
  240. package/dist/components/tree-view/TreeView.js +1 -1
  241. package/dist/components/typography/Typography.js +1 -1
  242. package/dist/components/visually-hidden/VisuallyHidden.d.ts +9 -7
  243. package/dist/components/visually-hidden/VisuallyHidden.js +1 -1
  244. package/dist/components/window-pane/WindowPane.d.ts +2 -2
  245. package/dist/components/window-pane/WindowPane.js +1 -1
  246. package/dist/hooks/useElementSize.d.ts +3 -1
  247. package/dist/hooks/useElementSize.js +1 -1
  248. package/dist/hooks/useOnScreen.d.ts +3 -0
  249. package/dist/hooks/useOnScreen.js +1 -1
  250. package/dist/index.d.ts +9 -0
  251. package/dist/index.js +1 -1
  252. package/dist/internal/animate.d.ts +63 -16
  253. package/dist/internal/animate.js +1 -1
  254. package/dist/internal/cache.d.ts +24 -0
  255. package/dist/internal/cache.js +1 -0
  256. package/dist/internal/calendar.d.ts +21 -43
  257. package/dist/internal/calendar.js +1 -1
  258. package/dist/internal/chart-frame.d.ts +80 -25
  259. package/dist/internal/chart-frame.js +1 -1
  260. package/dist/internal/chart-line.js +1 -1
  261. package/dist/internal/chart.d.ts +55 -4
  262. package/dist/internal/chart.js +1 -1
  263. package/dist/internal/children.d.ts +15 -0
  264. package/dist/internal/children.js +1 -0
  265. package/dist/internal/color.d.ts +4 -0
  266. package/dist/internal/color.js +1 -1
  267. package/dist/internal/csv.d.ts +12 -1
  268. package/dist/internal/csv.js +1 -1
  269. package/dist/internal/data-table.d.ts +28 -10
  270. package/dist/internal/data-table.js +1 -1
  271. package/dist/internal/date.d.ts +12 -5
  272. package/dist/internal/date.js +1 -1
  273. package/dist/internal/defaults.d.ts +6 -1
  274. package/dist/internal/defaults.js +1 -1
  275. package/dist/internal/drag.d.ts +21 -7
  276. package/dist/internal/drag.js +1 -1
  277. package/dist/internal/drop.d.ts +61 -0
  278. package/dist/internal/drop.js +1 -0
  279. package/dist/internal/fieldset.d.ts +13 -0
  280. package/dist/internal/fieldset.js +1 -0
  281. package/dist/internal/format.d.ts +21 -7
  282. package/dist/internal/format.js +1 -1
  283. package/dist/internal/glow.d.ts +58 -0
  284. package/dist/internal/glow.js +1 -0
  285. package/dist/internal/grid.d.ts +16 -0
  286. package/dist/internal/grid.js +1 -1
  287. package/dist/internal/highlight.js +1 -1
  288. package/dist/internal/i18n.d.ts +302 -11
  289. package/dist/internal/i18n.js +1 -1
  290. package/dist/internal/icons.d.ts +38 -2
  291. package/dist/internal/icons.js +1 -1
  292. package/dist/internal/inert.d.ts +13 -0
  293. package/dist/internal/inert.js +1 -0
  294. package/dist/internal/initials.d.ts +8 -3
  295. package/dist/internal/initials.js +1 -1
  296. package/dist/internal/keys.d.ts +13 -0
  297. package/dist/internal/keys.js +1 -1
  298. package/dist/internal/link.d.ts +13 -14
  299. package/dist/internal/link.js +1 -1
  300. package/dist/internal/media.d.ts +9 -17
  301. package/dist/internal/media.js +1 -1
  302. package/dist/internal/menu.d.ts +6 -1
  303. package/dist/internal/mockup.d.ts +5 -4
  304. package/dist/internal/mockup.js +1 -1
  305. package/dist/internal/observe.d.ts +12 -0
  306. package/dist/internal/observe.js +1 -1
  307. package/dist/internal/page-layout.d.ts +10 -8
  308. package/dist/internal/page-layout.js +1 -1
  309. package/dist/internal/picker.d.ts +8 -3
  310. package/dist/internal/picker.js +1 -1
  311. package/dist/internal/progress.d.ts +35 -6
  312. package/dist/internal/progress.js +1 -1
  313. package/dist/internal/responsive.d.ts +0 -8
  314. package/dist/internal/responsive.js +1 -1
  315. package/dist/internal/run.d.ts +71 -0
  316. package/dist/internal/run.js +1 -0
  317. package/dist/internal/screen.d.ts +12 -0
  318. package/dist/internal/screen.js +1 -0
  319. package/dist/internal/styles.d.ts +245 -4
  320. package/dist/internal/styles.js +1 -1
  321. package/dist/internal/text.d.ts +3 -13
  322. package/dist/internal/text.js +1 -1
  323. package/dist/internal/wheel.d.ts +38 -0
  324. package/dist/internal/wheel.js +1 -0
  325. package/dist/internal/window.js +1 -1
  326. package/dist/locales/ar.js +1 -1
  327. package/dist/locales/de.js +1 -1
  328. package/dist/locales/es.js +1 -1
  329. package/dist/locales/fr.js +1 -1
  330. package/dist/locales/hi.js +1 -1
  331. package/dist/locales/id.js +1 -1
  332. package/dist/locales/it.js +1 -1
  333. package/dist/locales/ja.js +1 -1
  334. package/dist/locales/ko.js +1 -1
  335. package/dist/locales/nl.js +1 -1
  336. package/dist/locales/pl.js +1 -1
  337. package/dist/locales/pt.js +1 -1
  338. package/dist/locales/ru.js +1 -1
  339. package/dist/locales/th.js +1 -1
  340. package/dist/locales/tr.js +1 -1
  341. package/dist/locales/vi.js +1 -1
  342. package/dist/locales/zh-hans.js +1 -1
  343. package/dist/locales/zh-hant.js +1 -1
  344. package/dist/styles.css +1 -1
  345. package/dist/tailwind.css +632 -59
  346. package/dist/types.d.ts +34 -7
  347. package/dist/types.js +0 -8
  348. package/package.json +44 -10
package/dist/tailwind.css CHANGED
@@ -59,6 +59,23 @@
59
59
  :root,
60
60
  .light,
61
61
  [data-theme='light'] {
62
+ /* CodeBlock's `auto` palette, declared on every theme root as the colour tokens
63
+ are, so a block follows the nearest root rather than the document's. */
64
+ --neba-code-auto-bg: oklch(98.4% 0.003 262);
65
+ --neba-code-auto-fg: oklch(30% 0.016 262);
66
+ --neba-code-auto-comment: oklch(58% 0.022 262);
67
+ --neba-code-auto-keyword: oklch(48% 0.19 322);
68
+ --neba-code-auto-string: oklch(46% 0.12 148);
69
+ --neba-code-auto-number: oklch(50% 0.14 48);
70
+ --neba-code-auto-function: oklch(48% 0.17 264);
71
+ --neba-code-auto-type: oklch(45% 0.1 214);
72
+ --neba-code-auto-variable: oklch(45% 0.12 28);
73
+ --neba-code-auto-tag: oklch(48% 0.18 22);
74
+ --neba-code-auto-attr: oklch(47% 0.13 82);
75
+ --neba-code-auto-meta: oklch(52% 0.05 262);
76
+ --neba-code-auto-add: oklch(45% 0.14 152);
77
+ --neba-code-auto-del: oklch(48% 0.18 25);
78
+
62
79
  /* Neutral surfaces and content */
63
80
  --neba-surface: oklch(100% 0 0);
64
81
  --neba-fg: oklch(24% 0.015 262);
@@ -83,6 +100,13 @@
83
100
  --neba-fill-alpha: 88%;
84
101
  --neba-blur: blur(9px) saturate(1.5);
85
102
 
103
+ /* How much of the family's accent a focus ring is. The ring sits 2px off the
104
+ control, so what it is read against is the page, and on a white sheet 55%
105
+ of any accent is 2.2 to 2.6:1 — under the 3:1 a focus indicator needs, on
106
+ the controls where the ring is all the focus there is. 80% clears 3.4:1 for
107
+ every family. The dark sheet keeps 55%, which already clears 3.2:1. */
108
+ --neba-ring-alpha: 80%;
109
+
86
110
  /* Frosted, not polished. A single tile of greyscale fractal noise laid over
87
111
  the fill in `overlay` is what separates sandblasted acrylic from a pane of
88
112
  glass — and it is the one thing a flat fill can never fake. Kept at 12% in
@@ -126,6 +150,25 @@
126
150
  --neba-panel-hover: rgb(255 255 255 / 0.82);
127
151
  --neba-panel-press: rgb(255 255 255 / 0.92);
128
152
 
153
+ /* The one surface in the library that is a groove rather than a sheet: the
154
+ track a Switch's thumb runs along.
155
+
156
+ It is the ladder above turned over, and it has to be. The panel is white
157
+ laid on the page, so on a light theme the track came out *lighter* than
158
+ what it sits on — and the thumb is `--neba-surface`, which is white. A
159
+ white disc on a white track is what made an off switch read as an empty
160
+ pill: 1.02:1, which is no edge at all. The thumb is the light on the track,
161
+ and light needs something under it to be light against.
162
+
163
+ So the groove darkens what is behind it instead of brightening it, which is
164
+ also what a groove is. It stays translucent for the same reason every other
165
+ surface here does — the blur behind it is what makes it acrylic rather than
166
+ paint — and it stays neutral because the family is what "on" says. On the
167
+ dark theme the panel was already darker than the thumb, so these are the
168
+ panel's own two steps and nothing moves. */
169
+ --neba-groove: rgb(16 24 40 / 0.14);
170
+ --neba-groove-hover: rgb(16 24 40 / 0.19);
171
+
129
172
  /* Pointer spotlight and release afterglow, over a filled surface. Tinted
130
173
  variants use their own accent instead — see the derived block. The flash is
131
174
  only a shade brighter than the spotlight it replaces: the press is carried
@@ -172,6 +215,13 @@
172
215
  as a move. Nothing else may use it. */
173
216
  --neba-duration-window: 240ms;
174
217
 
218
+ /* How high every portalled surface sits: a menu, a dialog, a drawer, a
219
+ tooltip, a toast. One number, because they are all the same kind of thing
220
+ — something drawn over the page the reader was looking at — and because
221
+ the value a host needs is its own. A site whose fixed header is at 1200
222
+ sets this once rather than beating fourteen `z-50`s with a selector. */
223
+ --neba-z-portal: 50;
224
+
175
225
  /* Color families -------------------------------------------------------
176
226
  Chroma sits just under the sRGB gamut boundary for each family's lightness
177
227
  — around 90% of the maximum, close enough to be vivid and far enough that
@@ -247,7 +297,7 @@
247
297
  clears 4:1 on white and 5.3:1 on the dark sheet.
248
298
 
249
299
  The order is the mechanism, not a preference: slots are handed out in
250
- sequence and never cycled, which is what keeps the *adjacent* pairs — the
300
+ sequence, repeating only past the eighth, which is what keeps the *adjacent* pairs — the
251
301
  only ones that touch in a stack or a legend — the ones that were checked.
252
302
  A ninth series is not a ninth colour; it is an "Other" row, or a second
253
303
  chart. Series one is the primary hue because a one-series chart should look
@@ -329,6 +379,20 @@
329
379
  --neba-chart-div-on-3: var(--neba-fg);
330
380
  --neba-chart-div-on-4: var(--neba-fg);
331
381
  --neba-chart-div-on-5: var(--neba-surface);
382
+
383
+ /* The same decision for a label on one of the eight series colours: a share
384
+ written on a pie slice. The eight sit at one lightness, so the two inks
385
+ are within a tenth of each other on the light sheet and the slightly
386
+ better one is taken per slot; on the dark sheet the surface wins by more
387
+ than 2:1 on every one of them. */
388
+ --neba-chart-on-1: var(--neba-fg);
389
+ --neba-chart-on-2: var(--neba-surface);
390
+ --neba-chart-on-3: var(--neba-fg);
391
+ --neba-chart-on-4: var(--neba-surface);
392
+ --neba-chart-on-5: var(--neba-fg);
393
+ --neba-chart-on-6: var(--neba-surface);
394
+ --neba-chart-on-7: var(--neba-fg);
395
+ --neba-chart-on-8: var(--neba-fg);
332
396
  }
333
397
 
334
398
  /* ---------------------------------------------------------------------------
@@ -336,31 +400,93 @@
336
400
  *
337
401
  * Fills stay mid-tone rather than pale, so a filled button on a dark surface
338
402
  * does not glare; the readable-on-surface `accent` is what lightens.
403
+ *
404
+ * Ink and hairlines are picked against the *raised* sheet, not against the bare
405
+ * one, and that is the one rule this theme does not share with the light block.
406
+ *
407
+ * The panel ladder is opacity: `--neba-panel` and its two steps lay white over
408
+ * whatever is behind them. On a white page that changes nothing — white over
409
+ * white is white — so a light-theme value measured on `--neba-surface` still
410
+ * reads the same inside a Card, inside a popup, under a table header. On a
411
+ * near-black page every step multiplies the bed's luminance instead, and the
412
+ * steps compound: a Card is 2.1x the page, and a header band inside that Card
413
+ * is 4.9x. Ink tuned against the bare sheet loses most of its contrast the
414
+ * moment it sits on a raised one, which is exactly where secondary text,
415
+ * disabled text and structural rules live.
416
+ *
417
+ * Which is also why the dark ladder is 5/7/9% rather than the 7/10/13% it was.
418
+ * Those three were picked for how a sheet reads against the page, and at the
419
+ * time the page was all they were checked against; compounded they put a table
420
+ * header at 7.5x the sheet and squeezed the whole ink hierarchy into the top of
421
+ * the range. Lowering them costs less than it looks: the light theme's own
422
+ * ladder moves a sheet's lightness by *nothing at all* — it is the hairline and
423
+ * the plate edge that say where a Card begins there — and the dark sheet still
424
+ * lifts by 0.05 in `oklch` L on top of both.
425
+ *
426
+ * The four values below are then solved against a table header inside a Card,
427
+ * the deepest bed the library composes for itself, and checked back down the
428
+ * ladder:
429
+ *
430
+ * - `--neba-muted-fg` reads 5.05:1 there and 4.34:1 with a page shell around it
431
+ * as well — the light theme reads 4.33-4.64:1 everywhere, so this is parity at
432
+ * every depth. At the old 66% on the old ladder the same header came out at
433
+ * 3.3:1, and a segmented button's unchosen label and a field's placeholder
434
+ * were not far above it.
435
+ * - `--neba-disabled-fg` lands inside the 2.0-2.4:1 the light theme reads at.
436
+ * Disabled text is exempt from 4.5:1 and is meant to be quiet, but at the old
437
+ * 48% it fell to 1.8:1 on a panel — a disabled menu row or segment was not
438
+ * quiet, it was gone.
439
+ * - `--neba-border` sits *above* the ladder. The light hairline is 0.09 below
440
+ * every bed it is drawn on; the old dark one was 0.11 above the bare sheet
441
+ * but 0.02 to 0.05 *below* a raised one, so a tree's rails and a chart's grid
442
+ * on a Card came out darker than the sheet under them — a black scratch where
443
+ * the design language asks for an edge catching the light. It is more present
444
+ * on the bare page than the light hairline is, and that is the trade: one
445
+ * value cannot be a whisper across a ladder that moves.
339
446
  * ------------------------------------------------------------------------- */
340
447
 
341
448
  @media (prefers-color-scheme: dark) {
342
449
  :root:not(.light):not([data-theme='light']) {
450
+ --neba-code-auto-bg: oklch(19.5% 0.014 262);
451
+ --neba-code-auto-fg: oklch(90% 0.012 262);
452
+ --neba-code-auto-comment: oklch(60% 0.024 262);
453
+ --neba-code-auto-keyword: oklch(76% 0.13 324);
454
+ --neba-code-auto-string: oklch(80% 0.13 148);
455
+ --neba-code-auto-number: oklch(81% 0.12 62);
456
+ --neba-code-auto-function: oklch(78% 0.12 258);
457
+ --neba-code-auto-type: oklch(82% 0.1 210);
458
+ --neba-code-auto-variable: oklch(82% 0.09 32);
459
+ --neba-code-auto-tag: oklch(77% 0.13 22);
460
+ --neba-code-auto-attr: oklch(85% 0.11 92);
461
+ --neba-code-auto-meta: oklch(70% 0.05 262);
462
+ --neba-code-auto-add: oklch(80% 0.14 152);
463
+ --neba-code-auto-del: oklch(75% 0.15 25);
464
+
343
465
  --neba-surface: oklch(19% 0.012 262);
344
466
  --neba-fg: oklch(95% 0.008 262);
345
- --neba-muted-fg: oklch(66% 0.014 262);
346
- --neba-border: oklch(30% 0.012 262);
467
+ --neba-muted-fg: oklch(72% 0.014 262);
468
+ --neba-border: oklch(38% 0.012 262);
347
469
 
348
470
  --neba-disabled-bg: oklch(27% 0.012 262);
349
- --neba-disabled-fg: oklch(48% 0.012 262);
350
- --neba-disabled-border: oklch(32% 0.012 262);
471
+ --neba-disabled-fg: oklch(52% 0.012 262);
472
+ --neba-disabled-border: oklch(40% 0.012 262);
351
473
 
352
474
  --neba-fill-alpha: 87%;
353
475
  --neba-blur: blur(9px) saturate(1.3);
476
+ --neba-ring-alpha: 55%;
354
477
 
355
478
  --neba-plate-edge: rgb(255 255 255 / 0.12);
356
479
  --neba-plate-top: rgb(255 255 255 / 0.16);
357
480
  --neba-sheen: linear-gradient(148deg, rgb(255 255 255 / 0.09), rgb(255 255 255 / 0) 52%);
358
481
 
359
- --neba-glass-bg: rgb(255 255 255 / 0.07);
482
+ --neba-glass-bg: rgb(255 255 255 / 0.05);
483
+
484
+ --neba-panel: rgb(255 255 255 / 0.05);
485
+ --neba-panel-hover: rgb(255 255 255 / 0.07);
486
+ --neba-panel-press: rgb(255 255 255 / 0.09);
360
487
 
361
- --neba-panel: rgb(255 255 255 / 0.07);
362
- --neba-panel-hover: rgb(255 255 255 / 0.1);
363
- --neba-panel-press: rgb(255 255 255 / 0.13);
488
+ --neba-groove: rgb(255 255 255 / 0.05);
489
+ --neba-groove-hover: rgb(255 255 255 / 0.07);
364
490
 
365
491
  --neba-glow-on-fill: rgb(255 255 255 / 0.13);
366
492
  --neba-flash-on-fill: rgb(255 255 255 / 0.18);
@@ -448,32 +574,60 @@
448
574
  --neba-chart-div-on-3: var(--neba-surface);
449
575
  --neba-chart-div-on-4: var(--neba-surface);
450
576
  --neba-chart-div-on-5: var(--neba-fg);
577
+
578
+ --neba-chart-on-1: var(--neba-surface);
579
+ --neba-chart-on-2: var(--neba-surface);
580
+ --neba-chart-on-3: var(--neba-surface);
581
+ --neba-chart-on-4: var(--neba-surface);
582
+ --neba-chart-on-5: var(--neba-surface);
583
+ --neba-chart-on-6: var(--neba-surface);
584
+ --neba-chart-on-7: var(--neba-surface);
585
+ --neba-chart-on-8: var(--neba-surface);
451
586
  }
452
587
  }
453
588
 
454
589
  .dark,
455
590
  [data-theme='dark'] {
591
+ --neba-code-auto-bg: oklch(19.5% 0.014 262);
592
+ --neba-code-auto-fg: oklch(90% 0.012 262);
593
+ --neba-code-auto-comment: oklch(60% 0.024 262);
594
+ --neba-code-auto-keyword: oklch(76% 0.13 324);
595
+ --neba-code-auto-string: oklch(80% 0.13 148);
596
+ --neba-code-auto-number: oklch(81% 0.12 62);
597
+ --neba-code-auto-function: oklch(78% 0.12 258);
598
+ --neba-code-auto-type: oklch(82% 0.1 210);
599
+ --neba-code-auto-variable: oklch(82% 0.09 32);
600
+ --neba-code-auto-tag: oklch(77% 0.13 22);
601
+ --neba-code-auto-attr: oklch(85% 0.11 92);
602
+ --neba-code-auto-meta: oklch(70% 0.05 262);
603
+ --neba-code-auto-add: oklch(80% 0.14 152);
604
+ --neba-code-auto-del: oklch(75% 0.15 25);
605
+
456
606
  --neba-surface: oklch(19% 0.012 262);
457
607
  --neba-fg: oklch(95% 0.008 262);
458
- --neba-muted-fg: oklch(66% 0.014 262);
459
- --neba-border: oklch(30% 0.012 262);
608
+ --neba-muted-fg: oklch(72% 0.014 262);
609
+ --neba-border: oklch(38% 0.012 262);
460
610
 
461
611
  --neba-disabled-bg: oklch(27% 0.012 262);
462
- --neba-disabled-fg: oklch(48% 0.012 262);
463
- --neba-disabled-border: oklch(32% 0.012 262);
612
+ --neba-disabled-fg: oklch(52% 0.012 262);
613
+ --neba-disabled-border: oklch(40% 0.012 262);
464
614
 
465
615
  --neba-fill-alpha: 87%;
466
616
  --neba-blur: blur(9px) saturate(1.3);
617
+ --neba-ring-alpha: 55%;
467
618
 
468
619
  --neba-plate-edge: rgb(255 255 255 / 0.12);
469
620
  --neba-plate-top: rgb(255 255 255 / 0.16);
470
621
  --neba-sheen: linear-gradient(148deg, rgb(255 255 255 / 0.09), rgb(255 255 255 / 0) 52%);
471
622
 
472
- --neba-glass-bg: rgb(255 255 255 / 0.07);
623
+ --neba-glass-bg: rgb(255 255 255 / 0.05);
473
624
 
474
- --neba-panel: rgb(255 255 255 / 0.07);
475
- --neba-panel-hover: rgb(255 255 255 / 0.1);
476
- --neba-panel-press: rgb(255 255 255 / 0.13);
625
+ --neba-panel: rgb(255 255 255 / 0.05);
626
+ --neba-panel-hover: rgb(255 255 255 / 0.07);
627
+ --neba-panel-press: rgb(255 255 255 / 0.09);
628
+
629
+ --neba-groove: rgb(255 255 255 / 0.05);
630
+ --neba-groove-hover: rgb(255 255 255 / 0.07);
477
631
 
478
632
  --neba-glow-on-fill: rgb(255 255 255 / 0.13);
479
633
  --neba-flash-on-fill: rgb(255 255 255 / 0.18);
@@ -552,6 +706,15 @@
552
706
  --neba-chart-div-on-3: var(--neba-surface);
553
707
  --neba-chart-div-on-4: var(--neba-surface);
554
708
  --neba-chart-div-on-5: var(--neba-fg);
709
+
710
+ --neba-chart-on-1: var(--neba-surface);
711
+ --neba-chart-on-2: var(--neba-surface);
712
+ --neba-chart-on-3: var(--neba-surface);
713
+ --neba-chart-on-4: var(--neba-surface);
714
+ --neba-chart-on-5: var(--neba-surface);
715
+ --neba-chart-on-6: var(--neba-surface);
716
+ --neba-chart-on-7: var(--neba-surface);
717
+ --neba-chart-on-8: var(--neba-surface);
555
718
  }
556
719
 
557
720
  /* ---------------------------------------------------------------------------
@@ -562,6 +725,22 @@
562
725
  * is repeated for every theme root because a custom property resolves its
563
726
  * `var()`s on the element that declares it — declaring these only on `:root`
564
727
  * would freeze them to the light values inside a `.dark` subtree.
728
+ *
729
+ * `on-tint` is the one that is not a strength of the base colour, and it is
730
+ * here for the same reason `on-solid` exists. A `soft` surface is a wash of the
731
+ * family's own `accent`, so writing the label in that accent puts a colour on a
732
+ * pale copy of itself: the ink and the bed move together and the gap between
733
+ * them cannot be opened by changing either one. It tops out at 4.5:1 the moment
734
+ * the wash passes about 9%, and `soft-press` is 25%. Measured, an `on` Toggle,
735
+ * a `text` Chip and a highlighted menu row all sat between 3.4:1 and 4.5:1, in
736
+ * *both* themes — this is not a dark-mode failure.
737
+ *
738
+ * Pulling the ink 28% toward `--neba-fg` breaks the coupling, and it flips with
739
+ * the theme on its own: toward near-black on a white page, toward near-white on
740
+ * a dark one. The family's hue survives — a primary on-tint is still plainly
741
+ * blue — and the worst family on the strongest wash clears 4.5:1 in both.
742
+ * `accent` itself is untouched, so what a TextLink or a Statistic's delta is
743
+ * written in on the bare sheet does not change.
565
744
  * ------------------------------------------------------------------------- */
566
745
 
567
746
  :root,
@@ -603,6 +782,7 @@
603
782
  --neba-primary-soft: color-mix(in srgb, var(--neba-primary-accent) 10%, transparent);
604
783
  --neba-primary-soft-hover: color-mix(in srgb, var(--neba-primary-accent) 17%, transparent);
605
784
  --neba-primary-soft-press: color-mix(in srgb, var(--neba-primary-accent) 25%, transparent);
785
+ --neba-primary-on-tint: color-mix(in oklab, var(--neba-primary-accent) 72%, var(--neba-fg));
606
786
  --neba-primary-panel: color-mix(in srgb, var(--neba-primary-accent) 8%, var(--neba-glass-bg));
607
787
  --neba-primary-panel-hover: color-mix(
608
788
  in srgb,
@@ -616,7 +796,11 @@
616
796
  );
617
797
  --neba-primary-line: color-mix(in srgb, var(--neba-primary-accent) 22%, transparent);
618
798
  --neba-primary-line-hover: color-mix(in srgb, var(--neba-primary-accent) 40%, transparent);
619
- --neba-primary-ring: color-mix(in srgb, var(--neba-primary-accent) 55%, transparent);
799
+ --neba-primary-ring: color-mix(
800
+ in srgb,
801
+ var(--neba-primary-accent) var(--neba-ring-alpha),
802
+ transparent
803
+ );
620
804
 
621
805
  --neba-secondary-fill: color-mix(
622
806
  in srgb,
@@ -636,6 +820,7 @@
636
820
  --neba-secondary-soft: color-mix(in srgb, var(--neba-secondary-accent) 10%, transparent);
637
821
  --neba-secondary-soft-hover: color-mix(in srgb, var(--neba-secondary-accent) 17%, transparent);
638
822
  --neba-secondary-soft-press: color-mix(in srgb, var(--neba-secondary-accent) 25%, transparent);
823
+ --neba-secondary-on-tint: color-mix(in oklab, var(--neba-secondary-accent) 72%, var(--neba-fg));
639
824
  --neba-secondary-panel: color-mix(in srgb, var(--neba-secondary-accent) 8%, var(--neba-glass-bg));
640
825
  --neba-secondary-panel-hover: color-mix(
641
826
  in srgb,
@@ -649,7 +834,11 @@
649
834
  );
650
835
  --neba-secondary-line: color-mix(in srgb, var(--neba-secondary-accent) 22%, transparent);
651
836
  --neba-secondary-line-hover: color-mix(in srgb, var(--neba-secondary-accent) 40%, transparent);
652
- --neba-secondary-ring: color-mix(in srgb, var(--neba-secondary-accent) 55%, transparent);
837
+ --neba-secondary-ring: color-mix(
838
+ in srgb,
839
+ var(--neba-secondary-accent) var(--neba-ring-alpha),
840
+ transparent
841
+ );
653
842
 
654
843
  --neba-success-fill: color-mix(
655
844
  in srgb,
@@ -669,6 +858,7 @@
669
858
  --neba-success-soft: color-mix(in srgb, var(--neba-success-accent) 10%, transparent);
670
859
  --neba-success-soft-hover: color-mix(in srgb, var(--neba-success-accent) 17%, transparent);
671
860
  --neba-success-soft-press: color-mix(in srgb, var(--neba-success-accent) 25%, transparent);
861
+ --neba-success-on-tint: color-mix(in oklab, var(--neba-success-accent) 72%, var(--neba-fg));
672
862
  --neba-success-panel: color-mix(in srgb, var(--neba-success-accent) 8%, var(--neba-glass-bg));
673
863
  --neba-success-panel-hover: color-mix(
674
864
  in srgb,
@@ -682,7 +872,11 @@
682
872
  );
683
873
  --neba-success-line: color-mix(in srgb, var(--neba-success-accent) 22%, transparent);
684
874
  --neba-success-line-hover: color-mix(in srgb, var(--neba-success-accent) 40%, transparent);
685
- --neba-success-ring: color-mix(in srgb, var(--neba-success-accent) 55%, transparent);
875
+ --neba-success-ring: color-mix(
876
+ in srgb,
877
+ var(--neba-success-accent) var(--neba-ring-alpha),
878
+ transparent
879
+ );
686
880
 
687
881
  --neba-warning-fill: color-mix(
688
882
  in srgb,
@@ -702,6 +896,7 @@
702
896
  --neba-warning-soft: color-mix(in srgb, var(--neba-warning-accent) 12%, transparent);
703
897
  --neba-warning-soft-hover: color-mix(in srgb, var(--neba-warning-accent) 20%, transparent);
704
898
  --neba-warning-soft-press: color-mix(in srgb, var(--neba-warning-accent) 28%, transparent);
899
+ --neba-warning-on-tint: color-mix(in oklab, var(--neba-warning-accent) 72%, var(--neba-fg));
705
900
  --neba-warning-panel: color-mix(in srgb, var(--neba-warning-accent) 8%, var(--neba-glass-bg));
706
901
  --neba-warning-panel-hover: color-mix(
707
902
  in srgb,
@@ -715,7 +910,11 @@
715
910
  );
716
911
  --neba-warning-line: color-mix(in srgb, var(--neba-warning-accent) 26%, transparent);
717
912
  --neba-warning-line-hover: color-mix(in srgb, var(--neba-warning-accent) 44%, transparent);
718
- --neba-warning-ring: color-mix(in srgb, var(--neba-warning-accent) 55%, transparent);
913
+ --neba-warning-ring: color-mix(
914
+ in srgb,
915
+ var(--neba-warning-accent) var(--neba-ring-alpha),
916
+ transparent
917
+ );
719
918
 
720
919
  --neba-danger-fill: color-mix(
721
920
  in srgb,
@@ -735,6 +934,7 @@
735
934
  --neba-danger-soft: color-mix(in srgb, var(--neba-danger-accent) 10%, transparent);
736
935
  --neba-danger-soft-hover: color-mix(in srgb, var(--neba-danger-accent) 17%, transparent);
737
936
  --neba-danger-soft-press: color-mix(in srgb, var(--neba-danger-accent) 25%, transparent);
937
+ --neba-danger-on-tint: color-mix(in oklab, var(--neba-danger-accent) 72%, var(--neba-fg));
738
938
  --neba-danger-panel: color-mix(in srgb, var(--neba-danger-accent) 8%, var(--neba-glass-bg));
739
939
  --neba-danger-panel-hover: color-mix(
740
940
  in srgb,
@@ -748,7 +948,11 @@
748
948
  );
749
949
  --neba-danger-line: color-mix(in srgb, var(--neba-danger-accent) 22%, transparent);
750
950
  --neba-danger-line-hover: color-mix(in srgb, var(--neba-danger-accent) 40%, transparent);
751
- --neba-danger-ring: color-mix(in srgb, var(--neba-danger-accent) 55%, transparent);
951
+ --neba-danger-ring: color-mix(
952
+ in srgb,
953
+ var(--neba-danger-accent) var(--neba-ring-alpha),
954
+ transparent
955
+ );
752
956
 
753
957
  --neba-info-fill: color-mix(in srgb, var(--neba-info-solid) var(--neba-fill-alpha), transparent);
754
958
  --neba-info-fill-hover: color-mix(
@@ -764,12 +968,13 @@
764
968
  --neba-info-soft: color-mix(in srgb, var(--neba-info-accent) 10%, transparent);
765
969
  --neba-info-soft-hover: color-mix(in srgb, var(--neba-info-accent) 17%, transparent);
766
970
  --neba-info-soft-press: color-mix(in srgb, var(--neba-info-accent) 25%, transparent);
971
+ --neba-info-on-tint: color-mix(in oklab, var(--neba-info-accent) 72%, var(--neba-fg));
767
972
  --neba-info-panel: color-mix(in srgb, var(--neba-info-accent) 8%, var(--neba-glass-bg));
768
973
  --neba-info-panel-hover: color-mix(in srgb, var(--neba-info-accent) 16%, var(--neba-glass-bg));
769
974
  --neba-info-panel-press: color-mix(in srgb, var(--neba-info-accent) 24%, var(--neba-glass-bg));
770
975
  --neba-info-line: color-mix(in srgb, var(--neba-info-accent) 22%, transparent);
771
976
  --neba-info-line-hover: color-mix(in srgb, var(--neba-info-accent) 40%, transparent);
772
- --neba-info-ring: color-mix(in srgb, var(--neba-info-accent) 55%, transparent);
977
+ --neba-info-ring: color-mix(in srgb, var(--neba-info-accent) var(--neba-ring-alpha), transparent);
773
978
 
774
979
  /* A chart's chrome: the grid, the axis rules, the baseline.
775
980
 
@@ -793,6 +998,33 @@
793
998
  --neba-chart-gap: var(--neba-surface);
794
999
  }
795
1000
 
1001
+ /* ---------------------------------------------------------------------------
1002
+ * The `text` plate
1003
+ *
1004
+ * `--n-soft*` is a wash — an alpha of the family's accent — so what it comes out
1005
+ * as depends on whatever is painted behind it, and `--n-on-tint` is solved for
1006
+ * *one* wash over the page. A Badge, an Avatar and an AppLogo are marks a caller
1007
+ * puts **inside** other things, and the moment one lands on something that is
1008
+ * itself painted the sum is a bed nobody solved an ink for. Measured, a `text`
1009
+ * Badge read 3.9:1 on a selected row and 1.45:1 on a filled surface — which is
1010
+ * not faint, it is gone.
1011
+ *
1012
+ * So the wash is laid on the page's own surface rather than on the caller's: an
1013
+ * opaque colour with the wash as a one-stop gradient over it, which is the
1014
+ * arrangement Table uses for a row. The plate then reads what it always read on
1015
+ * the page, and reads it on every bed, because there is no longer a bed to
1016
+ * depend on.
1017
+ *
1018
+ * It is here rather than in Tailwind for `styles.css`'s usual reason: the
1019
+ * arbitrary-property form of a gradient whose two stops are the same `var()` is
1020
+ * expressible and nobody would want to read it.
1021
+ * ------------------------------------------------------------------------- */
1022
+
1023
+ .neba-tint-plate {
1024
+ background-color: var(--neba-surface);
1025
+ background-image: linear-gradient(var(--n-soft-hover), var(--n-soft-hover));
1026
+ }
1027
+
796
1028
  /* ---------------------------------------------------------------------------
797
1029
  * Sheet titles
798
1030
  *
@@ -805,9 +1037,16 @@
805
1037
  * This is in CSS rather than in Tailwind because the selector is the point:
806
1038
  * `[&_:where(h1,h2,h3,h4,h5,h6)]:[font:inherit]` is expressible and nobody
807
1039
  * would want to read it twice.
1040
+ *
1041
+ * The class is written twice for `neba-link`'s reason. One class plus a
1042
+ * `:where()` is one class, and `.prose h2` and `.vp-doc h2` are one class plus a
1043
+ * tag — so the browser's own bold was being replaced by the *article's*, and a
1044
+ * `<Card title={<h2>…</h2>}>` inside rendered Markdown came out at 24px where
1045
+ * the card asked for 15px. Two classes clears both and still loses to anything
1046
+ * a caller writes with two of their own.
808
1047
  * ------------------------------------------------------------------------- */
809
1048
 
810
- .neba-title :where(h1, h2, h3, h4, h5, h6) {
1049
+ .neba-title.neba-title :where(h1, h2, h3, h4, h5, h6) {
811
1050
  font: inherit;
812
1051
  margin: 0;
813
1052
  }
@@ -917,6 +1156,81 @@
917
1156
  text-decoration-color: var(--n-underline-hover);
918
1157
  }
919
1158
 
1159
+ /* ---------------------------------------------------------------------------
1160
+ * Text a finger types into, on iOS
1161
+ *
1162
+ * Safari on iOS zooms the page in whenever a field under 16px takes the focus,
1163
+ * and leaves it zoomed after the keyboard goes. The library's `md` field is
1164
+ * 13px, so tapping into the ordinary field on the ordinary phone moved the whole
1165
+ * page. The only fix that holds is the size: `maximum-scale` in a viewport tag
1166
+ * also takes pinch-zoom away from everybody who needs it.
1167
+ *
1168
+ * `-webkit-touch-callout` is the property only WebKit on iOS and iPadOS has,
1169
+ * which keeps every other browser, and desktop Safari, at the size the design
1170
+ * set. `1em` is the size the field inherits from its shell, so a field already
1171
+ * over 16px keeps its own; the doubled class is `.neba-link`'s reason.
1172
+ * ------------------------------------------------------------------------- */
1173
+
1174
+ @supports (-webkit-touch-callout: none) {
1175
+ .neba-input.neba-input {
1176
+ font-size: max(1em, 16px);
1177
+ }
1178
+ }
1179
+
1180
+ /* ---------------------------------------------------------------------------
1181
+ * Forced colours
1182
+ *
1183
+ * A forced palette — Windows' contrast themes — replaces every colour with a
1184
+ * handful of system ones and draws no `box-shadow` and no background image at
1185
+ * all. Three things the library draws are lost to that: the plate that is the
1186
+ * edge of a filled control, the bar and the thumb that say a value by being a
1187
+ * colour, and the fill of a switch that says it is on. Nothing here applies in
1188
+ * any other mode, so the ordinary look does not change.
1189
+ *
1190
+ * The indicators opt out of the palette with `forced-color-adjust: none` and
1191
+ * are drawn in the system's own `Highlight`, which is what a forced theme uses
1192
+ * to say "chosen" and so cannot clash with it.
1193
+ * ------------------------------------------------------------------------- */
1194
+
1195
+ @media (forced-colors: active) {
1196
+ /* A hairline where the plate was. The focus ring still replaces it. */
1197
+ .neba-glow.neba-glow:not(:focus-visible),
1198
+ .neba-switch.neba-switch:not(:focus-visible) {
1199
+ outline: 1px solid ButtonText;
1200
+ outline-offset: -1px;
1201
+ }
1202
+
1203
+ /* Behind `@supports` as well, which every browser that can be in a forced
1204
+ palette passes: a colour that cannot opt out of the palette would be
1205
+ replaced by it, and a fill the same colour as its track says nothing. */
1206
+ @supports (forced-color-adjust: none) {
1207
+ .neba-progress-track.neba-progress-track,
1208
+ .neba-slider-rail.neba-slider-rail {
1209
+ forced-color-adjust: none;
1210
+ background-color: Canvas;
1211
+ outline: 1px solid CanvasText;
1212
+ outline-offset: -1px;
1213
+ }
1214
+
1215
+ .neba-progress-indicator.neba-progress-indicator,
1216
+ .neba-slider-indicator.neba-slider-indicator,
1217
+ .neba-switch.neba-switch[data-checked] {
1218
+ forced-color-adjust: none;
1219
+ background-color: Highlight;
1220
+ }
1221
+
1222
+ .neba-switch-thumb.neba-switch-thumb,
1223
+ .neba-slider-thumb.neba-slider-thumb {
1224
+ forced-color-adjust: none;
1225
+ background-color: ButtonText;
1226
+ }
1227
+
1228
+ .neba-switch.neba-switch[data-checked] .neba-switch-thumb {
1229
+ background-color: HighlightText;
1230
+ }
1231
+ }
1232
+ }
1233
+
920
1234
  /* ---------------------------------------------------------------------------
921
1235
  * Code blocks
922
1236
  *
@@ -987,8 +1301,28 @@
987
1301
  --n-code-mark-edge: color-mix(in oklab, var(--n-code-fg) 45%, transparent);
988
1302
  }
989
1303
 
990
- .neba-code[data-code-theme='dark'],
991
- :where(.dark, [data-theme='dark']) .neba-code[data-code-theme='auto'] {
1304
+ .neba-code[data-code-theme='auto'] {
1305
+ /* The theme roots hold both palettes, so the nearest `.dark`, `.light` or
1306
+ `data-theme` decides, and the system setting where there is none. A
1307
+ descendant selector could only ask whether any ancestor was dark, which
1308
+ painted a light panel inside a dark page with the dark palette. */
1309
+ --n-code-bg: var(--neba-code-auto-bg);
1310
+ --n-code-fg: var(--neba-code-auto-fg);
1311
+ --n-code-comment: var(--neba-code-auto-comment);
1312
+ --n-code-keyword: var(--neba-code-auto-keyword);
1313
+ --n-code-string: var(--neba-code-auto-string);
1314
+ --n-code-number: var(--neba-code-auto-number);
1315
+ --n-code-function: var(--neba-code-auto-function);
1316
+ --n-code-type: var(--neba-code-auto-type);
1317
+ --n-code-variable: var(--neba-code-auto-variable);
1318
+ --n-code-tag: var(--neba-code-auto-tag);
1319
+ --n-code-attr: var(--neba-code-auto-attr);
1320
+ --n-code-meta: var(--neba-code-auto-meta);
1321
+ --n-code-add: var(--neba-code-auto-add);
1322
+ --n-code-del: var(--neba-code-auto-del);
1323
+ }
1324
+
1325
+ .neba-code[data-code-theme='dark'] {
992
1326
  --n-code-bg: oklch(19.5% 0.014 262);
993
1327
  --n-code-fg: oklch(90% 0.012 262);
994
1328
 
@@ -1006,26 +1340,6 @@
1006
1340
  --n-code-del: oklch(75% 0.15 25);
1007
1341
  }
1008
1342
 
1009
- @media (prefers-color-scheme: dark) {
1010
- :root:not([data-theme='light']) .neba-code[data-code-theme='auto'] {
1011
- --n-code-bg: oklch(19.5% 0.014 262);
1012
- --n-code-fg: oklch(90% 0.012 262);
1013
-
1014
- --n-code-comment: oklch(60% 0.024 262);
1015
- --n-code-keyword: oklch(76% 0.13 324);
1016
- --n-code-string: oklch(80% 0.13 148);
1017
- --n-code-number: oklch(81% 0.12 62);
1018
- --n-code-function: oklch(78% 0.12 258);
1019
- --n-code-type: oklch(82% 0.1 210);
1020
- --n-code-variable: oklch(82% 0.09 32);
1021
- --n-code-tag: oklch(77% 0.13 22);
1022
- --n-code-attr: oklch(85% 0.11 92);
1023
- --n-code-meta: oklch(70% 0.05 262);
1024
- --n-code-add: oklch(80% 0.14 152);
1025
- --n-code-del: oklch(75% 0.15 25);
1026
- }
1027
- }
1028
-
1029
1343
  /*
1030
1344
  * `mono` is the one theme that follows the page, and the only one with no hue
1031
1345
  * in it at all: the structure is carried by weight and by how far a run is
@@ -1262,8 +1576,16 @@
1262
1576
  border-inline-start-color: var(--n-code-mark-edge);
1263
1577
  }
1264
1578
 
1579
+ /*
1580
+ * The numbers and the prompts are drawn, not read. Generated content is in the
1581
+ * accessibility tree, so a screen reader went down a block saying "1 const a
1582
+ * equals 1, 2 const b…". The `/ ''` is the alternative text a browser speaks
1583
+ * instead, which is nothing; a browser that does not know the syntax drops the
1584
+ * second declaration and keeps the first, and reads the number as before.
1585
+ */
1265
1586
  .neba-code-line[data-line]::before {
1266
1587
  content: attr(data-line);
1588
+ content: attr(data-line) / '';
1267
1589
  order: 0;
1268
1590
  flex: none;
1269
1591
  width: var(--n-code-gutter, 2ch);
@@ -1276,6 +1598,7 @@
1276
1598
 
1277
1599
  .neba-code-line[data-prompt]::after {
1278
1600
  content: attr(data-prompt);
1601
+ content: attr(data-prompt) / '';
1279
1602
  order: 1;
1280
1603
  flex: none;
1281
1604
  margin-inline-end: 0.75em;
@@ -1463,6 +1786,82 @@
1463
1786
  * change at the same moment — including when a consumer has moved them.
1464
1787
  * ------------------------------------------------------------------------- */
1465
1788
 
1789
+ /* Every breakpoint slot is cleared on the element that reads it, and that is
1790
+ what keeps a nested component from reading its ancestor's layout.
1791
+
1792
+ A custom property inherits, and a slot the element did not write is
1793
+ therefore whatever the nearest ancestor wrote — so a grid inside a grid item
1794
+ resolved the *item's* `--n-span-md` and came out a third of the width it had
1795
+ asked for. Only the breakpoints a caller names are emitted, which is what
1796
+ keeps the inline style to one property, so there is nothing on the element
1797
+ to overwrite the inherited value with.
1798
+
1799
+ An inline style wins over this rule, so a component that really did name a
1800
+ breakpoint is untouched. What is deliberately *not* cleared is the resolved
1801
+ slot beside each of these: `--n-cols` and `--n-gap-x` are read by a grid's
1802
+ items rather than by the grid, and inheriting is how they get there. */
1803
+ .neba-grid,
1804
+ .neba-flex {
1805
+ --n-gap-x-xs: initial;
1806
+ --n-gap-x-sm: initial;
1807
+ --n-gap-x-md: initial;
1808
+ --n-gap-x-lg: initial;
1809
+ --n-gap-x-xl: initial;
1810
+ --n-gap-y-xs: initial;
1811
+ --n-gap-y-sm: initial;
1812
+ --n-gap-y-md: initial;
1813
+ --n-gap-y-lg: initial;
1814
+ --n-gap-y-xl: initial;
1815
+ }
1816
+
1817
+ .neba-grid,
1818
+ .neba-gallery {
1819
+ --n-cols-xs: initial;
1820
+ --n-cols-sm: initial;
1821
+ --n-cols-md: initial;
1822
+ --n-cols-lg: initial;
1823
+ --n-cols-xl: initial;
1824
+ }
1825
+
1826
+ .neba-flex {
1827
+ --n-flex-dir-xs: initial;
1828
+ --n-flex-dir-sm: initial;
1829
+ --n-flex-dir-md: initial;
1830
+ --n-flex-dir-lg: initial;
1831
+ --n-flex-dir-xl: initial;
1832
+ }
1833
+
1834
+ .neba-measure {
1835
+ --n-max-w-xs: initial;
1836
+ --n-max-w-sm: initial;
1837
+ --n-max-w-md: initial;
1838
+ --n-max-w-lg: initial;
1839
+ --n-max-w-xl: initial;
1840
+ }
1841
+
1842
+ .neba-grid-item {
1843
+ --n-span-xs: initial;
1844
+ --n-span-sm: initial;
1845
+ --n-span-md: initial;
1846
+ --n-span-lg: initial;
1847
+ --n-span-xl: initial;
1848
+ --n-span-w-xs: initial;
1849
+ --n-span-w-sm: initial;
1850
+ --n-span-w-md: initial;
1851
+ --n-span-w-lg: initial;
1852
+ --n-span-w-xl: initial;
1853
+ --n-grow-xs: initial;
1854
+ --n-grow-sm: initial;
1855
+ --n-grow-md: initial;
1856
+ --n-grow-lg: initial;
1857
+ --n-grow-xl: initial;
1858
+ --n-offset-xs: initial;
1859
+ --n-offset-sm: initial;
1860
+ --n-offset-md: initial;
1861
+ --n-offset-lg: initial;
1862
+ --n-offset-xl: initial;
1863
+ }
1864
+
1466
1865
  /* The two gutters, shared: a GridContainer and a Flex ask the same question
1467
1866
  and `spacing` has to mean the same number on both.
1468
1867
 
@@ -1506,13 +1905,30 @@
1506
1905
  the container, so this re-resolves on its own at every breakpoint. */
1507
1906
  --n-track: calc((100% + var(--n-gap-x, 0px)) / var(--n-cols, 12));
1508
1907
 
1509
- /* Inseparable from the width above it: `flex` stops the row from resizing
1510
- the item, `min-width` stops a long unbroken word from doing the same, and
1511
- `box-sizing` is what makes the percentage mean the whole item. */
1908
+ /* What a span that is a share of the row works out to, named rather than
1909
+ written into `width` so that a breakpoint which goes back to a number can
1910
+ point at it. */
1911
+ --n-col-width: calc(var(--n-track) * min(var(--n-span), var(--n-cols, 12)) - var(--n-gap-x, 0px));
1912
+
1913
+ /* `auto` and `grow` are the two widths a column count cannot reach — the
1914
+ contents' own width, with and without the space left over in the row — so
1915
+ they are a cascade of their own rather than a value inside the one above.
1916
+ A caller who names a number at a wider breakpoint points this back at the
1917
+ arithmetic, which is what lets the two mix in one map. */
1918
+ --n-span-w: var(--n-span-w-xs, var(--n-col-width));
1919
+ --n-grow: var(--n-grow-xs, 0);
1920
+
1921
+ /* Inseparable from the width above it: the row must not resize the item,
1922
+ `min-width` stops a long unbroken word from doing the same, `max-width`
1923
+ holds an `auto` item inside the row it is in, and `box-sizing` is what
1924
+ makes the percentage mean the whole item. */
1512
1925
  box-sizing: border-box;
1513
- flex: 0 0 auto;
1926
+ flex-grow: var(--n-grow);
1927
+ flex-shrink: 0;
1928
+ flex-basis: auto;
1514
1929
  min-width: 0;
1515
- width: calc(var(--n-track) * min(var(--n-span), var(--n-cols, 12)) - var(--n-gap-x, 0px));
1930
+ max-width: 100%;
1931
+ width: var(--n-span-w);
1516
1932
  margin-inline-start: calc(var(--n-track) * min(var(--n-offset), var(--n-cols, 12)));
1517
1933
  }
1518
1934
 
@@ -1538,6 +1954,8 @@
1538
1954
 
1539
1955
  .neba-grid-item {
1540
1956
  --n-span: var(--n-span-sm, var(--n-span-xs, var(--n-cols, 12)));
1957
+ --n-span-w: var(--n-span-w-sm, var(--n-span-w-xs, var(--n-col-width)));
1958
+ --n-grow: var(--n-grow-sm, var(--n-grow-xs, 0));
1541
1959
  --n-offset: var(--n-offset-sm, var(--n-offset-xs, 0));
1542
1960
  }
1543
1961
  }
@@ -1564,6 +1982,8 @@
1564
1982
 
1565
1983
  .neba-grid-item {
1566
1984
  --n-span: var(--n-span-md, var(--n-span-sm, var(--n-span-xs, var(--n-cols, 12))));
1985
+ --n-span-w: var(--n-span-w-md, var(--n-span-w-sm, var(--n-span-w-xs, var(--n-col-width))));
1986
+ --n-grow: var(--n-grow-md, var(--n-grow-sm, var(--n-grow-xs, 0)));
1567
1987
  --n-offset: var(--n-offset-md, var(--n-offset-sm, var(--n-offset-xs, 0)));
1568
1988
  }
1569
1989
  }
@@ -1596,6 +2016,11 @@
1596
2016
  --n-span-lg,
1597
2017
  var(--n-span-md, var(--n-span-sm, var(--n-span-xs, var(--n-cols, 12))))
1598
2018
  );
2019
+ --n-span-w: var(
2020
+ --n-span-w-lg,
2021
+ var(--n-span-w-md, var(--n-span-w-sm, var(--n-span-w-xs, var(--n-col-width))))
2022
+ );
2023
+ --n-grow: var(--n-grow-lg, var(--n-grow-md, var(--n-grow-sm, var(--n-grow-xs, 0))));
1599
2024
  --n-offset: var(--n-offset-lg, var(--n-offset-md, var(--n-offset-sm, var(--n-offset-xs, 0))));
1600
2025
  }
1601
2026
  }
@@ -1640,6 +2065,17 @@
1640
2065
  --n-span-xl,
1641
2066
  var(--n-span-lg, var(--n-span-md, var(--n-span-sm, var(--n-span-xs, var(--n-cols, 12)))))
1642
2067
  );
2068
+ --n-span-w: var(
2069
+ --n-span-w-xl,
2070
+ var(
2071
+ --n-span-w-lg,
2072
+ var(--n-span-w-md, var(--n-span-w-sm, var(--n-span-w-xs, var(--n-col-width))))
2073
+ )
2074
+ );
2075
+ --n-grow: var(
2076
+ --n-grow-xl,
2077
+ var(--n-grow-lg, var(--n-grow-md, var(--n-grow-sm, var(--n-grow-xs, 0))))
2078
+ );
1643
2079
  --n-offset: var(
1644
2080
  --n-offset-xl,
1645
2081
  var(--n-offset-lg, var(--n-offset-md, var(--n-offset-sm, var(--n-offset-xs, 0))))
@@ -1760,6 +2196,22 @@
1760
2196
  border-radius: inherit;
1761
2197
  }
1762
2198
 
2199
+ /*
2200
+ * The same target for a control in a tight row — a Carousel's dots, a
2201
+ * NumberField's two steppers, a window's traffic lights. Grown to the full
2202
+ * height, and along the row only into half the gap on each side: grown to 24px
2203
+ * that way too, each one would lie over its neighbour, and a press on the right
2204
+ * half of a dot would land on the next one. `--n-hit-gap` is the gap the row
2205
+ * is laid out with.
2206
+ */
2207
+ .neba-hit-row::before {
2208
+ content: '';
2209
+ position: absolute;
2210
+ inset-block: min(0px, calc((var(--n-hit, 1.5rem) - 100%) / -2));
2211
+ inset-inline: calc(var(--n-hit-gap, 0px) / -2);
2212
+ border-radius: inherit;
2213
+ }
2214
+
1763
2215
  /* ---------------------------------------------------------------------------
1764
2216
  * Interaction light
1765
2217
  *
@@ -2016,6 +2468,24 @@
2016
2468
  animation-direction: var(--n-anim-direction, normal);
2017
2469
  animation-fill-mode: both;
2018
2470
  animation-play-state: var(--n-anim-state, running);
2471
+
2472
+ /* The shape of an effect is its own. The slots inherit like any custom
2473
+ property, and only the ones an effect uses are written, so an effect nested
2474
+ in another took the outer one's start state: a fade inside an AnimateReveal
2475
+ ran from full opacity to full opacity. `initial` leaves each keyframe on its
2476
+ own fallback, and a value written on the element itself still wins. The
2477
+ play state is not reset here, because a split's pieces and a staggered
2478
+ child read it from their root; a `transition` writes its own instead. */
2479
+ --n-anim-opacity: initial;
2480
+ --n-anim-scale: initial;
2481
+ --n-anim-x: initial;
2482
+ --n-anim-y: initial;
2483
+ --n-anim-angle: initial;
2484
+ --n-anim-angle-to: initial;
2485
+ --n-anim-clip: initial;
2486
+ --n-anim-ease: initial;
2487
+ --n-anim-timeline: initial;
2488
+ --n-anim-range: initial;
2019
2489
  }
2020
2490
 
2021
2491
  /*
@@ -2117,6 +2587,15 @@
2117
2587
  animation-name: neba-anim-blink;
2118
2588
  }
2119
2589
 
2590
+ /* Held still, a blink rests at full opacity rather than wherever the pulse had
2591
+ got to: a paused recording light left half drawn, or invisible at a floor of
2592
+ zero, is a rendering fault rather than a pause. It starts over when it runs
2593
+ again. The second selector is a staggered blink's pieces. */
2594
+ .neba-anim-blink[data-state='paused'],
2595
+ [data-state='paused'] > .neba-anim-blink {
2596
+ animation-name: none;
2597
+ }
2598
+
2120
2599
  /*
2121
2600
  * Reveal: an edge travelling across something that is already there.
2122
2601
  *
@@ -2304,7 +2783,12 @@
2304
2783
  animation-name: neba-anim-marquee-y;
2305
2784
  }
2306
2785
 
2307
- .neba-marquee[data-pause-on-hover]:hover .neba-marquee-track {
2786
+ /* `:focus-within` beside `:hover` for the same reason the prop exists at all. A
2787
+ pointer stops the strip so a link on it can be aimed at; a keyboard reaches
2788
+ that link by tabbing to it, and a strip that kept moving would be carrying the
2789
+ focused element off the screen. */
2790
+ .neba-marquee[data-pause-on-hover]:hover .neba-marquee-track,
2791
+ .neba-marquee[data-pause-on-hover]:focus-within .neba-marquee-track {
2308
2792
  animation-play-state: paused;
2309
2793
  }
2310
2794
 
@@ -2378,10 +2862,52 @@
2378
2862
  animation: neba-anim-caret 1s step-end infinite;
2379
2863
  }
2380
2864
 
2865
+ /* ---------------------------------------------------------------------------
2866
+ * Text arriving from somewhere else
2867
+ *
2868
+ * One word of a streamed answer, fading in as it lands. The animation is on the
2869
+ * word's own element and runs **once, on mount**, which is the whole mechanism:
2870
+ * React keys the words by position, so a word already on screen keeps its
2871
+ * element as the text grows and never fades again, and only the ones that have
2872
+ * just arrived are new elements. The last word grows a character at a time
2873
+ * inside the element it already has, so it does not flicker either.
2874
+ *
2875
+ * Opacity and nothing else, for the reason every floating surface in this
2876
+ * library arrives that way: a word that also travels drags the rest of the line
2877
+ * with it, and a reader is in the middle of the sentence it is joining.
2878
+ * ------------------------------------------------------------------------- */
2879
+
2880
+ @keyframes neba-stream-word-in {
2881
+ from {
2882
+ opacity: 0;
2883
+ }
2884
+ }
2885
+
2886
+ .neba-stream-word {
2887
+ animation: neba-stream-word-in var(--neba-duration) var(--neba-ease) both;
2888
+ }
2889
+
2890
+ /* The block at the end of a stream. The same caret an `AnimateTyping` draws,
2891
+ because two blinking blocks at two rates on one screen is two cursors. */
2892
+ .neba-stream-caret {
2893
+ display: inline-block;
2894
+ inline-size: 0.5em;
2895
+ block-size: 1em;
2896
+ vertical-align: text-bottom;
2897
+ border-radius: 1px;
2898
+ background-color: var(--n-accent);
2899
+ animation: neba-anim-caret 1s step-end infinite;
2900
+ }
2901
+
2381
2902
  /* Respect a reduced-motion preference. Both layers track the pointer, so they
2382
2903
  are motion in the sense that matters here — drop them entirely. */
2383
2904
  @media (prefers-reduced-motion: reduce) {
2384
- :root {
2905
+ /* Every root the durations are declared on, and not only `:root`: a nested
2906
+ `.light` box declares them again, and on `:root` alone the preference was
2907
+ undone for everything inside one. */
2908
+ :root,
2909
+ .light,
2910
+ [data-theme='light'] {
2385
2911
  --neba-duration: 0ms;
2386
2912
  --neba-duration-fill: 0ms;
2387
2913
  }
@@ -2434,15 +2960,42 @@
2434
2960
  * about whether anything is happening; an entrance that does not play has
2435
2961
  * still delivered its content, which is the only thing it was ever carrying.
2436
2962
  *
2437
- * `animation: none` rather than a zero duration, because the shorthand also
2438
- * clears `fill-mode` — and it is the fill that was holding an untriggered
2439
- * element on its own first frame at `opacity: 0`. Left in place, respecting a
2440
- * reader's preference would blank the page.
2963
+ * So an effect that runs is cut to its last frame, a millisecond once on the
2964
+ * clock. That is where `mode="out"` leaves content gone and a rotation leaves
2965
+ * it at its `to` angle, and a run that ends fires `animationend`, which a
2966
+ * pattern that unmounts on it is waiting for. `animation: none` switched all
2967
+ * of that off and left leaving content on screen.
2968
+ *
2969
+ * An effect that has not been let go, or is held, has no animation at all and
2970
+ * shows the element as it is. The fill would otherwise keep it on its first
2971
+ * frame, which for an entrance waiting on a hover is an invisible element. A
2972
+ * float never ends, so it has no last frame and simply stops.
2441
2973
  */
2442
- .neba-anim,
2974
+ .neba-anim {
2975
+ animation-duration: 1ms;
2976
+ animation-delay: 0ms;
2977
+ animation-iteration-count: 1;
2978
+ }
2979
+
2980
+ @supports (animation-timeline: view()) {
2981
+ .neba-anim {
2982
+ animation-timeline: auto;
2983
+ }
2984
+ }
2985
+
2986
+ /* The word has arrived either way; the fade was only ever how it got there.
2987
+ A millisecond, so it still ends on its last frame. */
2988
+ .neba-stream-word {
2989
+ animation-duration: 1ms;
2990
+ }
2991
+
2992
+ .neba-anim[data-state='paused'],
2993
+ [data-state='paused'] .neba-anim,
2994
+ .neba-anim-float,
2443
2995
  .neba-marquee-track,
2444
2996
  .neba-headline-item,
2445
- .neba-typing-caret {
2997
+ .neba-typing-caret,
2998
+ .neba-stream-caret {
2446
2999
  animation: none;
2447
3000
  }
2448
3001
 
@@ -2452,4 +3005,24 @@
2452
3005
  background: var(--n-anim-glow, var(--n-accent));
2453
3006
  animation: none;
2454
3007
  }
3008
+
3009
+ /* A marquee that cannot move is a row of content, so it is drawn as one:
3010
+ the copies go, and the strip stops clipping and wraps. Stopped with its
3011
+ copies still laid down, it cut long content off at the edge and showed
3012
+ short content two or four times over. The component renders one copy too;
3013
+ this is what holds in the first paint, before it has asked. */
3014
+ .neba-marquee {
3015
+ overflow: visible;
3016
+ flex-wrap: wrap;
3017
+ }
3018
+
3019
+ .neba-marquee-track {
3020
+ flex: 1 1 auto;
3021
+ flex-wrap: wrap;
3022
+ min-width: 0;
3023
+ }
3024
+
3025
+ .neba-marquee-track[aria-hidden='true'] {
3026
+ display: none;
3027
+ }
2455
3028
  }