neba 1.13.0 → 1.16.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 (341) hide show
  1. package/README.md +22 -30
  2. package/dist/a2ui/catalog.json +1466 -0
  3. package/dist/a2ui/components.d.ts +21 -0
  4. package/dist/a2ui/components.js +1 -0
  5. package/dist/a2ui/index.d.ts +62 -0
  6. package/dist/a2ui/index.js +1 -0
  7. package/dist/a2ui/schema.d.ts +47 -0
  8. package/dist/a2ui/schema.js +1 -0
  9. package/dist/components/accordion/Accordion.d.ts +5 -1
  10. package/dist/components/accordion/Accordion.js +1 -1
  11. package/dist/components/agent-steps/AgentSteps.d.ts +80 -0
  12. package/dist/components/agent-steps/AgentSteps.js +1 -0
  13. package/dist/components/agent-steps/index.d.ts +2 -0
  14. package/dist/components/agent-steps/index.js +1 -0
  15. package/dist/components/alert/Alert.js +1 -1
  16. package/dist/components/anchor/Anchor.d.ts +3 -2
  17. package/dist/components/anchor/Anchor.js +1 -1
  18. package/dist/components/animate-appear/AnimateAppear.js +1 -1
  19. package/dist/components/animate-blink/AnimateBlink.d.ts +3 -0
  20. package/dist/components/animate-blink/AnimateBlink.js +1 -1
  21. package/dist/components/animate-counter/AnimateCounter.d.ts +3 -1
  22. package/dist/components/animate-counter/AnimateCounter.js +1 -1
  23. package/dist/components/animate-fade/AnimateFade.js +1 -1
  24. package/dist/components/animate-float/AnimateFloat.js +1 -1
  25. package/dist/components/animate-grow/AnimateGrow.js +1 -1
  26. package/dist/components/animate-headline/AnimateHeadline.d.ts +9 -2
  27. package/dist/components/animate-headline/AnimateHeadline.js +1 -1
  28. package/dist/components/animate-lighting/AnimateLighting.d.ts +1 -1
  29. package/dist/components/animate-lighting/AnimateLighting.js +1 -1
  30. package/dist/components/animate-marquee/AnimateMarquee.d.ts +15 -6
  31. package/dist/components/animate-marquee/AnimateMarquee.js +1 -1
  32. package/dist/components/animate-reveal/AnimateReveal.js +1 -1
  33. package/dist/components/animate-rotate/AnimateRotate.js +1 -1
  34. package/dist/components/animate-scramble/AnimateScramble.d.ts +5 -3
  35. package/dist/components/animate-scramble/AnimateScramble.js +1 -1
  36. package/dist/components/animate-shake/AnimateShake.js +1 -1
  37. package/dist/components/animate-slide/AnimateSlide.js +1 -1
  38. package/dist/components/animate-split/AnimateSplit.d.ts +3 -1
  39. package/dist/components/animate-split/AnimateSplit.js +1 -1
  40. package/dist/components/animate-typing/AnimateTyping.d.ts +12 -4
  41. package/dist/components/animate-typing/AnimateTyping.js +1 -1
  42. package/dist/components/animate-zoom/AnimateZoom.js +1 -1
  43. package/dist/components/app-logo/AppLogo.js +1 -1
  44. package/dist/components/approval/Approval.d.ts +115 -0
  45. package/dist/components/approval/Approval.js +1 -0
  46. package/dist/components/approval/index.d.ts +2 -0
  47. package/dist/components/approval/index.js +1 -0
  48. package/dist/components/area-chart/AreaChart.d.ts +16 -5
  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 +2 -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 +4 -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 +11 -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.d.ts +1 -1
  116. package/dist/components/gauge-chart/GaugeChart.js +1 -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.d.ts +20 -1
  121. package/dist/components/heatmap-chart/HeatmapChart.js +1 -1
  122. package/dist/components/highlight/Highlight.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 +137 -11
  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/line-chart/LineChart.d.ts +16 -5
  136. package/dist/components/line-chart/LineChart.js +1 -1
  137. package/dist/components/list/List.js +1 -1
  138. package/dist/components/menu/Menu.d.ts +25 -11
  139. package/dist/components/menu/Menu.js +1 -1
  140. package/dist/components/menubar/Menubar.js +1 -1
  141. package/dist/components/meter/Meter.d.ts +6 -0
  142. package/dist/components/meter/Meter.js +1 -1
  143. package/dist/components/mockup/Mockup.js +1 -1
  144. package/dist/components/navigation-menu/NavigationMenu.d.ts +10 -1
  145. package/dist/components/navigation-menu/NavigationMenu.js +1 -1
  146. package/dist/components/number-field/NumberField.d.ts +20 -6
  147. package/dist/components/number-field/NumberField.js +1 -1
  148. package/dist/components/otp-field/OtpField.d.ts +7 -0
  149. package/dist/components/otp-field/OtpField.js +1 -1
  150. package/dist/components/overlay/Overlay.d.ts +1 -1
  151. package/dist/components/overlay/Overlay.js +1 -1
  152. package/dist/components/page-layout/PageLayout.d.ts +11 -0
  153. package/dist/components/page-layout/PageLayout.js +1 -1
  154. package/dist/components/pagination/Pagination.js +1 -1
  155. package/dist/components/panes/Panes.d.ts +11 -0
  156. package/dist/components/panes/Panes.js +1 -1
  157. package/dist/components/pie-chart/PieChart.d.ts +34 -3
  158. package/dist/components/pie-chart/PieChart.js +1 -1
  159. package/dist/components/pill/Pill.d.ts +8 -0
  160. package/dist/components/pill/Pill.js +1 -1
  161. package/dist/components/popconfirm/Popconfirm.d.ts +10 -3
  162. package/dist/components/popconfirm/Popconfirm.js +1 -1
  163. package/dist/components/popover/Popover.d.ts +8 -1
  164. package/dist/components/popover/Popover.js +1 -1
  165. package/dist/components/portal/Portal.js +1 -1
  166. package/dist/components/progress-box/ProgressBox.js +1 -1
  167. package/dist/components/progress-circular/ProgressCircular.js +1 -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 +10 -0
  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.js +1 -1
  188. package/dist/components/shortcut/Shortcut.js +1 -1
  189. package/dist/components/show/Show.d.ts +6 -5
  190. package/dist/components/show/Show.js +1 -1
  191. package/dist/components/sidebar/Sidebar.d.ts +7 -5
  192. package/dist/components/sidebar/Sidebar.js +1 -1
  193. package/dist/components/sidebar/SidebarTrigger.js +1 -1
  194. package/dist/components/skeleton/Skeleton.d.ts +1 -1
  195. package/dist/components/skeleton/Skeleton.js +1 -1
  196. package/dist/components/slider/Slider.d.ts +13 -1
  197. package/dist/components/slider/Slider.js +1 -1
  198. package/dist/components/sources/Sources.d.ts +78 -0
  199. package/dist/components/sources/Sources.js +1 -0
  200. package/dist/components/sources/index.d.ts +2 -0
  201. package/dist/components/sources/index.js +1 -0
  202. package/dist/components/sparkline/Sparkline.d.ts +14 -1
  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 +17 -1
  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.js +1 -1
  238. package/dist/components/tree-view/TreeView.d.ts +6 -0
  239. package/dist/components/tree-view/TreeView.js +1 -1
  240. package/dist/components/typography/Typography.js +1 -1
  241. package/dist/components/visually-hidden/VisuallyHidden.d.ts +9 -7
  242. package/dist/components/visually-hidden/VisuallyHidden.js +1 -1
  243. package/dist/components/window-pane/WindowPane.d.ts +2 -2
  244. package/dist/components/window-pane/WindowPane.js +1 -1
  245. package/dist/hooks/useElementSize.d.ts +3 -1
  246. package/dist/hooks/useElementSize.js +1 -1
  247. package/dist/hooks/useOnScreen.d.ts +3 -0
  248. package/dist/hooks/useOnScreen.js +1 -1
  249. package/dist/index.d.ts +9 -0
  250. package/dist/index.js +1 -1
  251. package/dist/internal/animate.d.ts +63 -16
  252. package/dist/internal/animate.js +1 -1
  253. package/dist/internal/calendar.d.ts +6 -1
  254. package/dist/internal/calendar.js +1 -1
  255. package/dist/internal/chart-brush.d.ts +53 -0
  256. package/dist/internal/chart-brush.js +1 -0
  257. package/dist/internal/chart-frame.d.ts +186 -30
  258. package/dist/internal/chart-frame.js +1 -1
  259. package/dist/internal/chart-line.js +1 -1
  260. package/dist/internal/chart.d.ts +133 -6
  261. package/dist/internal/chart.js +1 -1
  262. package/dist/internal/children.d.ts +15 -0
  263. package/dist/internal/children.js +1 -0
  264. package/dist/internal/color.d.ts +4 -0
  265. package/dist/internal/color.js +1 -1
  266. package/dist/internal/csv.d.ts +12 -1
  267. package/dist/internal/csv.js +1 -1
  268. package/dist/internal/data-table.d.ts +28 -10
  269. package/dist/internal/data-table.js +1 -1
  270. package/dist/internal/date.d.ts +12 -5
  271. package/dist/internal/date.js +1 -1
  272. package/dist/internal/defaults.d.ts +6 -1
  273. package/dist/internal/defaults.js +1 -1
  274. package/dist/internal/drag.d.ts +13 -1
  275. package/dist/internal/drag.js +1 -1
  276. package/dist/internal/drop.d.ts +61 -0
  277. package/dist/internal/drop.js +1 -0
  278. package/dist/internal/fieldset.d.ts +13 -0
  279. package/dist/internal/fieldset.js +1 -0
  280. package/dist/internal/glow.d.ts +102 -0
  281. package/dist/internal/glow.js +1 -0
  282. package/dist/internal/highlight.js +1 -1
  283. package/dist/internal/i18n.d.ts +232 -11
  284. package/dist/internal/i18n.js +1 -1
  285. package/dist/internal/icons.d.ts +13 -2
  286. package/dist/internal/icons.js +1 -1
  287. package/dist/internal/inert.d.ts +13 -0
  288. package/dist/internal/inert.js +1 -0
  289. package/dist/internal/initials.d.ts +8 -3
  290. package/dist/internal/initials.js +1 -1
  291. package/dist/internal/keys.d.ts +13 -0
  292. package/dist/internal/keys.js +1 -1
  293. package/dist/internal/link.d.ts +13 -15
  294. package/dist/internal/link.js +1 -1
  295. package/dist/internal/media.d.ts +9 -17
  296. package/dist/internal/media.js +1 -1
  297. package/dist/internal/menu.d.ts +6 -1
  298. package/dist/internal/mockup.d.ts +5 -4
  299. package/dist/internal/mockup.js +1 -1
  300. package/dist/internal/observe.d.ts +12 -0
  301. package/dist/internal/observe.js +1 -1
  302. package/dist/internal/page-layout.d.ts +10 -8
  303. package/dist/internal/page-layout.js +1 -1
  304. package/dist/internal/picker.d.ts +1 -1
  305. package/dist/internal/picker.js +1 -1
  306. package/dist/internal/progress.d.ts +11 -5
  307. package/dist/internal/progress.js +1 -1
  308. package/dist/internal/responsive.d.ts +0 -8
  309. package/dist/internal/responsive.js +1 -1
  310. package/dist/internal/run.d.ts +71 -0
  311. package/dist/internal/run.js +1 -0
  312. package/dist/internal/screen.d.ts +12 -0
  313. package/dist/internal/screen.js +1 -0
  314. package/dist/internal/styles.d.ts +185 -14
  315. package/dist/internal/styles.js +1 -1
  316. package/dist/internal/text.d.ts +3 -3
  317. package/dist/internal/text.js +1 -1
  318. package/dist/internal/window.js +1 -1
  319. package/dist/locales/ar.js +1 -1
  320. package/dist/locales/de.js +1 -1
  321. package/dist/locales/es.js +1 -1
  322. package/dist/locales/fr.js +1 -1
  323. package/dist/locales/hi.js +1 -1
  324. package/dist/locales/id.js +1 -1
  325. package/dist/locales/it.js +1 -1
  326. package/dist/locales/ja.js +1 -1
  327. package/dist/locales/ko.js +1 -1
  328. package/dist/locales/nl.js +1 -1
  329. package/dist/locales/pl.js +1 -1
  330. package/dist/locales/pt.js +1 -1
  331. package/dist/locales/ru.js +1 -1
  332. package/dist/locales/th.js +1 -1
  333. package/dist/locales/tr.js +1 -1
  334. package/dist/locales/vi.js +1 -1
  335. package/dist/locales/zh-hans.js +1 -1
  336. package/dist/locales/zh-hant.js +1 -1
  337. package/dist/styles.css +1 -1
  338. package/dist/tailwind.css +516 -53
  339. package/dist/types.d.ts +167 -7
  340. package/dist/types.js +0 -8
  341. package/package.json +43 -9
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
@@ -254,7 +297,7 @@
254
297
  clears 4:1 on white and 5.3:1 on the dark sheet.
255
298
 
256
299
  The order is the mechanism, not a preference: slots are handed out in
257
- 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
258
301
  only ones that touch in a stack or a legend — the ones that were checked.
259
302
  A ninth series is not a ninth colour; it is an "Other" row, or a second
260
303
  chart. Series one is the primary hue because a one-series chart should look
@@ -336,6 +379,20 @@
336
379
  --neba-chart-div-on-3: var(--neba-fg);
337
380
  --neba-chart-div-on-4: var(--neba-fg);
338
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);
339
396
  }
340
397
 
341
398
  /* ---------------------------------------------------------------------------
@@ -343,31 +400,93 @@
343
400
  *
344
401
  * Fills stay mid-tone rather than pale, so a filled button on a dark surface
345
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.
346
446
  * ------------------------------------------------------------------------- */
347
447
 
348
448
  @media (prefers-color-scheme: dark) {
349
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
+
350
465
  --neba-surface: oklch(19% 0.012 262);
351
466
  --neba-fg: oklch(95% 0.008 262);
352
- --neba-muted-fg: oklch(66% 0.014 262);
353
- --neba-border: oklch(30% 0.012 262);
467
+ --neba-muted-fg: oklch(72% 0.014 262);
468
+ --neba-border: oklch(38% 0.012 262);
354
469
 
355
470
  --neba-disabled-bg: oklch(27% 0.012 262);
356
- --neba-disabled-fg: oklch(48% 0.012 262);
357
- --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);
358
473
 
359
474
  --neba-fill-alpha: 87%;
360
475
  --neba-blur: blur(9px) saturate(1.3);
476
+ --neba-ring-alpha: 55%;
361
477
 
362
478
  --neba-plate-edge: rgb(255 255 255 / 0.12);
363
479
  --neba-plate-top: rgb(255 255 255 / 0.16);
364
480
  --neba-sheen: linear-gradient(148deg, rgb(255 255 255 / 0.09), rgb(255 255 255 / 0) 52%);
365
481
 
366
- --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);
367
487
 
368
- --neba-panel: rgb(255 255 255 / 0.07);
369
- --neba-panel-hover: rgb(255 255 255 / 0.1);
370
- --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);
371
490
 
372
491
  --neba-glow-on-fill: rgb(255 255 255 / 0.13);
373
492
  --neba-flash-on-fill: rgb(255 255 255 / 0.18);
@@ -455,32 +574,60 @@
455
574
  --neba-chart-div-on-3: var(--neba-surface);
456
575
  --neba-chart-div-on-4: var(--neba-surface);
457
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);
458
586
  }
459
587
  }
460
588
 
461
589
  .dark,
462
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
+
463
606
  --neba-surface: oklch(19% 0.012 262);
464
607
  --neba-fg: oklch(95% 0.008 262);
465
- --neba-muted-fg: oklch(66% 0.014 262);
466
- --neba-border: oklch(30% 0.012 262);
608
+ --neba-muted-fg: oklch(72% 0.014 262);
609
+ --neba-border: oklch(38% 0.012 262);
467
610
 
468
611
  --neba-disabled-bg: oklch(27% 0.012 262);
469
- --neba-disabled-fg: oklch(48% 0.012 262);
470
- --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);
471
614
 
472
615
  --neba-fill-alpha: 87%;
473
616
  --neba-blur: blur(9px) saturate(1.3);
617
+ --neba-ring-alpha: 55%;
474
618
 
475
619
  --neba-plate-edge: rgb(255 255 255 / 0.12);
476
620
  --neba-plate-top: rgb(255 255 255 / 0.16);
477
621
  --neba-sheen: linear-gradient(148deg, rgb(255 255 255 / 0.09), rgb(255 255 255 / 0) 52%);
478
622
 
479
- --neba-glass-bg: rgb(255 255 255 / 0.07);
623
+ --neba-glass-bg: rgb(255 255 255 / 0.05);
624
+
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);
480
628
 
481
- --neba-panel: rgb(255 255 255 / 0.07);
482
- --neba-panel-hover: rgb(255 255 255 / 0.1);
483
- --neba-panel-press: rgb(255 255 255 / 0.13);
629
+ --neba-groove: rgb(255 255 255 / 0.05);
630
+ --neba-groove-hover: rgb(255 255 255 / 0.07);
484
631
 
485
632
  --neba-glow-on-fill: rgb(255 255 255 / 0.13);
486
633
  --neba-flash-on-fill: rgb(255 255 255 / 0.18);
@@ -559,6 +706,15 @@
559
706
  --neba-chart-div-on-3: var(--neba-surface);
560
707
  --neba-chart-div-on-4: var(--neba-surface);
561
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);
562
718
  }
563
719
 
564
720
  /* ---------------------------------------------------------------------------
@@ -569,6 +725,22 @@
569
725
  * is repeated for every theme root because a custom property resolves its
570
726
  * `var()`s on the element that declares it — declaring these only on `:root`
571
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.
572
744
  * ------------------------------------------------------------------------- */
573
745
 
574
746
  :root,
@@ -610,6 +782,7 @@
610
782
  --neba-primary-soft: color-mix(in srgb, var(--neba-primary-accent) 10%, transparent);
611
783
  --neba-primary-soft-hover: color-mix(in srgb, var(--neba-primary-accent) 17%, transparent);
612
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));
613
786
  --neba-primary-panel: color-mix(in srgb, var(--neba-primary-accent) 8%, var(--neba-glass-bg));
614
787
  --neba-primary-panel-hover: color-mix(
615
788
  in srgb,
@@ -623,7 +796,11 @@
623
796
  );
624
797
  --neba-primary-line: color-mix(in srgb, var(--neba-primary-accent) 22%, transparent);
625
798
  --neba-primary-line-hover: color-mix(in srgb, var(--neba-primary-accent) 40%, transparent);
626
- --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
+ );
627
804
 
628
805
  --neba-secondary-fill: color-mix(
629
806
  in srgb,
@@ -643,6 +820,7 @@
643
820
  --neba-secondary-soft: color-mix(in srgb, var(--neba-secondary-accent) 10%, transparent);
644
821
  --neba-secondary-soft-hover: color-mix(in srgb, var(--neba-secondary-accent) 17%, transparent);
645
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));
646
824
  --neba-secondary-panel: color-mix(in srgb, var(--neba-secondary-accent) 8%, var(--neba-glass-bg));
647
825
  --neba-secondary-panel-hover: color-mix(
648
826
  in srgb,
@@ -656,7 +834,11 @@
656
834
  );
657
835
  --neba-secondary-line: color-mix(in srgb, var(--neba-secondary-accent) 22%, transparent);
658
836
  --neba-secondary-line-hover: color-mix(in srgb, var(--neba-secondary-accent) 40%, transparent);
659
- --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
+ );
660
842
 
661
843
  --neba-success-fill: color-mix(
662
844
  in srgb,
@@ -676,6 +858,7 @@
676
858
  --neba-success-soft: color-mix(in srgb, var(--neba-success-accent) 10%, transparent);
677
859
  --neba-success-soft-hover: color-mix(in srgb, var(--neba-success-accent) 17%, transparent);
678
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));
679
862
  --neba-success-panel: color-mix(in srgb, var(--neba-success-accent) 8%, var(--neba-glass-bg));
680
863
  --neba-success-panel-hover: color-mix(
681
864
  in srgb,
@@ -689,7 +872,11 @@
689
872
  );
690
873
  --neba-success-line: color-mix(in srgb, var(--neba-success-accent) 22%, transparent);
691
874
  --neba-success-line-hover: color-mix(in srgb, var(--neba-success-accent) 40%, transparent);
692
- --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
+ );
693
880
 
694
881
  --neba-warning-fill: color-mix(
695
882
  in srgb,
@@ -709,6 +896,7 @@
709
896
  --neba-warning-soft: color-mix(in srgb, var(--neba-warning-accent) 12%, transparent);
710
897
  --neba-warning-soft-hover: color-mix(in srgb, var(--neba-warning-accent) 20%, transparent);
711
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));
712
900
  --neba-warning-panel: color-mix(in srgb, var(--neba-warning-accent) 8%, var(--neba-glass-bg));
713
901
  --neba-warning-panel-hover: color-mix(
714
902
  in srgb,
@@ -722,7 +910,11 @@
722
910
  );
723
911
  --neba-warning-line: color-mix(in srgb, var(--neba-warning-accent) 26%, transparent);
724
912
  --neba-warning-line-hover: color-mix(in srgb, var(--neba-warning-accent) 44%, transparent);
725
- --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
+ );
726
918
 
727
919
  --neba-danger-fill: color-mix(
728
920
  in srgb,
@@ -742,6 +934,7 @@
742
934
  --neba-danger-soft: color-mix(in srgb, var(--neba-danger-accent) 10%, transparent);
743
935
  --neba-danger-soft-hover: color-mix(in srgb, var(--neba-danger-accent) 17%, transparent);
744
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));
745
938
  --neba-danger-panel: color-mix(in srgb, var(--neba-danger-accent) 8%, var(--neba-glass-bg));
746
939
  --neba-danger-panel-hover: color-mix(
747
940
  in srgb,
@@ -755,7 +948,11 @@
755
948
  );
756
949
  --neba-danger-line: color-mix(in srgb, var(--neba-danger-accent) 22%, transparent);
757
950
  --neba-danger-line-hover: color-mix(in srgb, var(--neba-danger-accent) 40%, transparent);
758
- --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
+ );
759
956
 
760
957
  --neba-info-fill: color-mix(in srgb, var(--neba-info-solid) var(--neba-fill-alpha), transparent);
761
958
  --neba-info-fill-hover: color-mix(
@@ -771,12 +968,13 @@
771
968
  --neba-info-soft: color-mix(in srgb, var(--neba-info-accent) 10%, transparent);
772
969
  --neba-info-soft-hover: color-mix(in srgb, var(--neba-info-accent) 17%, transparent);
773
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));
774
972
  --neba-info-panel: color-mix(in srgb, var(--neba-info-accent) 8%, var(--neba-glass-bg));
775
973
  --neba-info-panel-hover: color-mix(in srgb, var(--neba-info-accent) 16%, var(--neba-glass-bg));
776
974
  --neba-info-panel-press: color-mix(in srgb, var(--neba-info-accent) 24%, var(--neba-glass-bg));
777
975
  --neba-info-line: color-mix(in srgb, var(--neba-info-accent) 22%, transparent);
778
976
  --neba-info-line-hover: color-mix(in srgb, var(--neba-info-accent) 40%, transparent);
779
- --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);
780
978
 
781
979
  /* A chart's chrome: the grid, the axis rules, the baseline.
782
980
 
@@ -800,6 +998,33 @@
800
998
  --neba-chart-gap: var(--neba-surface);
801
999
  }
802
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
+
803
1028
  /* ---------------------------------------------------------------------------
804
1029
  * Sheet titles
805
1030
  *
@@ -812,9 +1037,16 @@
812
1037
  * This is in CSS rather than in Tailwind because the selector is the point:
813
1038
  * `[&_:where(h1,h2,h3,h4,h5,h6)]:[font:inherit]` is expressible and nobody
814
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.
815
1047
  * ------------------------------------------------------------------------- */
816
1048
 
817
- .neba-title :where(h1, h2, h3, h4, h5, h6) {
1049
+ .neba-title.neba-title :where(h1, h2, h3, h4, h5, h6) {
818
1050
  font: inherit;
819
1051
  margin: 0;
820
1052
  }
@@ -924,6 +1156,81 @@
924
1156
  text-decoration-color: var(--n-underline-hover);
925
1157
  }
926
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
+
927
1234
  /* ---------------------------------------------------------------------------
928
1235
  * Code blocks
929
1236
  *
@@ -994,8 +1301,28 @@
994
1301
  --n-code-mark-edge: color-mix(in oklab, var(--n-code-fg) 45%, transparent);
995
1302
  }
996
1303
 
997
- .neba-code[data-code-theme='dark'],
998
- :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'] {
999
1326
  --n-code-bg: oklch(19.5% 0.014 262);
1000
1327
  --n-code-fg: oklch(90% 0.012 262);
1001
1328
 
@@ -1013,26 +1340,6 @@
1013
1340
  --n-code-del: oklch(75% 0.15 25);
1014
1341
  }
1015
1342
 
1016
- @media (prefers-color-scheme: dark) {
1017
- :root:not([data-theme='light']) .neba-code[data-code-theme='auto'] {
1018
- --n-code-bg: oklch(19.5% 0.014 262);
1019
- --n-code-fg: oklch(90% 0.012 262);
1020
-
1021
- --n-code-comment: oklch(60% 0.024 262);
1022
- --n-code-keyword: oklch(76% 0.13 324);
1023
- --n-code-string: oklch(80% 0.13 148);
1024
- --n-code-number: oklch(81% 0.12 62);
1025
- --n-code-function: oklch(78% 0.12 258);
1026
- --n-code-type: oklch(82% 0.1 210);
1027
- --n-code-variable: oklch(82% 0.09 32);
1028
- --n-code-tag: oklch(77% 0.13 22);
1029
- --n-code-attr: oklch(85% 0.11 92);
1030
- --n-code-meta: oklch(70% 0.05 262);
1031
- --n-code-add: oklch(80% 0.14 152);
1032
- --n-code-del: oklch(75% 0.15 25);
1033
- }
1034
- }
1035
-
1036
1343
  /*
1037
1344
  * `mono` is the one theme that follows the page, and the only one with no hue
1038
1345
  * in it at all: the structure is carried by weight and by how far a run is
@@ -1269,8 +1576,16 @@
1269
1576
  border-inline-start-color: var(--n-code-mark-edge);
1270
1577
  }
1271
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
+ */
1272
1586
  .neba-code-line[data-line]::before {
1273
1587
  content: attr(data-line);
1588
+ content: attr(data-line) / '';
1274
1589
  order: 0;
1275
1590
  flex: none;
1276
1591
  width: var(--n-code-gutter, 2ch);
@@ -1283,6 +1598,7 @@
1283
1598
 
1284
1599
  .neba-code-line[data-prompt]::after {
1285
1600
  content: attr(data-prompt);
1601
+ content: attr(data-prompt) / '';
1286
1602
  order: 1;
1287
1603
  flex: none;
1288
1604
  margin-inline-end: 0.75em;
@@ -1880,6 +2196,22 @@
1880
2196
  border-radius: inherit;
1881
2197
  }
1882
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
+
1883
2215
  /* ---------------------------------------------------------------------------
1884
2216
  * Interaction light
1885
2217
  *
@@ -1922,6 +2254,21 @@
1922
2254
  }
1923
2255
  }
1924
2256
 
2257
+ /*
2258
+ * And out again while the reader is typing at it.
2259
+ *
2260
+ * `internal/glow.ts` writes the mark on a keystroke and takes it off on the
2261
+ * next `pointermove`, which is the whole rule: a pointer resting on a field is
2262
+ * where the hand left it — over the text — so the one moment the bloom is least
2263
+ * wanted is the one moment it cannot move out of the way on its own.
2264
+ *
2265
+ * After the hover rule and not before it: the two selectors are the same
2266
+ * specificity, so the one written second is the one that decides.
2267
+ */
2268
+ .neba-glow[data-typing]::before {
2269
+ opacity: 0;
2270
+ }
2271
+
1925
2272
  /*
1926
2273
  * Release afterglow. The asymmetry is the whole trick: 0ms in, ~900ms out, so
1927
2274
  * the flash lands on the frame of the press and is still visibly draining a
@@ -2136,6 +2483,24 @@
2136
2483
  animation-direction: var(--n-anim-direction, normal);
2137
2484
  animation-fill-mode: both;
2138
2485
  animation-play-state: var(--n-anim-state, running);
2486
+
2487
+ /* The shape of an effect is its own. The slots inherit like any custom
2488
+ property, and only the ones an effect uses are written, so an effect nested
2489
+ in another took the outer one's start state: a fade inside an AnimateReveal
2490
+ ran from full opacity to full opacity. `initial` leaves each keyframe on its
2491
+ own fallback, and a value written on the element itself still wins. The
2492
+ play state is not reset here, because a split's pieces and a staggered
2493
+ child read it from their root; a `transition` writes its own instead. */
2494
+ --n-anim-opacity: initial;
2495
+ --n-anim-scale: initial;
2496
+ --n-anim-x: initial;
2497
+ --n-anim-y: initial;
2498
+ --n-anim-angle: initial;
2499
+ --n-anim-angle-to: initial;
2500
+ --n-anim-clip: initial;
2501
+ --n-anim-ease: initial;
2502
+ --n-anim-timeline: initial;
2503
+ --n-anim-range: initial;
2139
2504
  }
2140
2505
 
2141
2506
  /*
@@ -2237,6 +2602,15 @@
2237
2602
  animation-name: neba-anim-blink;
2238
2603
  }
2239
2604
 
2605
+ /* Held still, a blink rests at full opacity rather than wherever the pulse had
2606
+ got to: a paused recording light left half drawn, or invisible at a floor of
2607
+ zero, is a rendering fault rather than a pause. It starts over when it runs
2608
+ again. The second selector is a staggered blink's pieces. */
2609
+ .neba-anim-blink[data-state='paused'],
2610
+ [data-state='paused'] > .neba-anim-blink {
2611
+ animation-name: none;
2612
+ }
2613
+
2240
2614
  /*
2241
2615
  * Reveal: an edge travelling across something that is already there.
2242
2616
  *
@@ -2503,10 +2877,52 @@
2503
2877
  animation: neba-anim-caret 1s step-end infinite;
2504
2878
  }
2505
2879
 
2880
+ /* ---------------------------------------------------------------------------
2881
+ * Text arriving from somewhere else
2882
+ *
2883
+ * One word of a streamed answer, fading in as it lands. The animation is on the
2884
+ * word's own element and runs **once, on mount**, which is the whole mechanism:
2885
+ * React keys the words by position, so a word already on screen keeps its
2886
+ * element as the text grows and never fades again, and only the ones that have
2887
+ * just arrived are new elements. The last word grows a character at a time
2888
+ * inside the element it already has, so it does not flicker either.
2889
+ *
2890
+ * Opacity and nothing else, for the reason every floating surface in this
2891
+ * library arrives that way: a word that also travels drags the rest of the line
2892
+ * with it, and a reader is in the middle of the sentence it is joining.
2893
+ * ------------------------------------------------------------------------- */
2894
+
2895
+ @keyframes neba-stream-word-in {
2896
+ from {
2897
+ opacity: 0;
2898
+ }
2899
+ }
2900
+
2901
+ .neba-stream-word {
2902
+ animation: neba-stream-word-in var(--neba-duration) var(--neba-ease) both;
2903
+ }
2904
+
2905
+ /* The block at the end of a stream. The same caret an `AnimateTyping` draws,
2906
+ because two blinking blocks at two rates on one screen is two cursors. */
2907
+ .neba-stream-caret {
2908
+ display: inline-block;
2909
+ inline-size: 0.5em;
2910
+ block-size: 1em;
2911
+ vertical-align: text-bottom;
2912
+ border-radius: 1px;
2913
+ background-color: var(--n-accent);
2914
+ animation: neba-anim-caret 1s step-end infinite;
2915
+ }
2916
+
2506
2917
  /* Respect a reduced-motion preference. Both layers track the pointer, so they
2507
2918
  are motion in the sense that matters here — drop them entirely. */
2508
2919
  @media (prefers-reduced-motion: reduce) {
2509
- :root {
2920
+ /* Every root the durations are declared on, and not only `:root`: a nested
2921
+ `.light` box declares them again, and on `:root` alone the preference was
2922
+ undone for everything inside one. */
2923
+ :root,
2924
+ .light,
2925
+ [data-theme='light'] {
2510
2926
  --neba-duration: 0ms;
2511
2927
  --neba-duration-fill: 0ms;
2512
2928
  }
@@ -2559,15 +2975,42 @@
2559
2975
  * about whether anything is happening; an entrance that does not play has
2560
2976
  * still delivered its content, which is the only thing it was ever carrying.
2561
2977
  *
2562
- * `animation: none` rather than a zero duration, because the shorthand also
2563
- * clears `fill-mode` — and it is the fill that was holding an untriggered
2564
- * element on its own first frame at `opacity: 0`. Left in place, respecting a
2565
- * reader's preference would blank the page.
2978
+ * So an effect that runs is cut to its last frame, a millisecond once on the
2979
+ * clock. That is where `mode="out"` leaves content gone and a rotation leaves
2980
+ * it at its `to` angle, and a run that ends fires `animationend`, which a
2981
+ * pattern that unmounts on it is waiting for. `animation: none` switched all
2982
+ * of that off and left leaving content on screen.
2983
+ *
2984
+ * An effect that has not been let go, or is held, has no animation at all and
2985
+ * shows the element as it is. The fill would otherwise keep it on its first
2986
+ * frame, which for an entrance waiting on a hover is an invisible element. A
2987
+ * float never ends, so it has no last frame and simply stops.
2566
2988
  */
2567
- .neba-anim,
2989
+ .neba-anim {
2990
+ animation-duration: 1ms;
2991
+ animation-delay: 0ms;
2992
+ animation-iteration-count: 1;
2993
+ }
2994
+
2995
+ @supports (animation-timeline: view()) {
2996
+ .neba-anim {
2997
+ animation-timeline: auto;
2998
+ }
2999
+ }
3000
+
3001
+ /* The word has arrived either way; the fade was only ever how it got there.
3002
+ A millisecond, so it still ends on its last frame. */
3003
+ .neba-stream-word {
3004
+ animation-duration: 1ms;
3005
+ }
3006
+
3007
+ .neba-anim[data-state='paused'],
3008
+ [data-state='paused'] .neba-anim,
3009
+ .neba-anim-float,
2568
3010
  .neba-marquee-track,
2569
3011
  .neba-headline-item,
2570
- .neba-typing-caret {
3012
+ .neba-typing-caret,
3013
+ .neba-stream-caret {
2571
3014
  animation: none;
2572
3015
  }
2573
3016
 
@@ -2577,4 +3020,24 @@
2577
3020
  background: var(--n-anim-glow, var(--n-accent));
2578
3021
  animation: none;
2579
3022
  }
3023
+
3024
+ /* A marquee that cannot move is a row of content, so it is drawn as one:
3025
+ the copies go, and the strip stops clipping and wraps. Stopped with its
3026
+ copies still laid down, it cut long content off at the edge and showed
3027
+ short content two or four times over. The component renders one copy too;
3028
+ this is what holds in the first paint, before it has asked. */
3029
+ .neba-marquee {
3030
+ overflow: visible;
3031
+ flex-wrap: wrap;
3032
+ }
3033
+
3034
+ .neba-marquee-track {
3035
+ flex: 1 1 auto;
3036
+ flex-wrap: wrap;
3037
+ min-width: 0;
3038
+ }
3039
+
3040
+ .neba-marquee-track[aria-hidden='true'] {
3041
+ display: none;
3042
+ }
2580
3043
  }