@eifi1/ui-kit 0.48.2 → 0.50.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 (377) hide show
  1. package/README.md +34 -16
  2. package/dist/admin/admin-action-confirm.js +1 -1
  3. package/dist/admin/admin-action-confirm.js.map +1 -1
  4. package/dist/admin/invitations-panel.js +5 -1
  5. package/dist/admin/invitations-panel.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +24 -12
  7. package/dist/admin/user-roster.js +21 -6
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/accept-invitation.js +2 -2
  10. package/dist/auth/accept-invitation.js.map +1 -1
  11. package/dist/auth/not-found-page.js +1 -1
  12. package/dist/auth/not-found-page.js.map +1 -1
  13. package/dist/auth/reset-password-form.js +2 -2
  14. package/dist/auth/reset-password-form.js.map +1 -1
  15. package/dist/auth/verify-email.d.ts +9 -1
  16. package/dist/auth/verify-email.js +5 -3
  17. package/dist/auth/verify-email.js.map +1 -1
  18. package/dist/billing/plan-card.js +2 -2
  19. package/dist/billing/plan-card.js.map +1 -1
  20. package/dist/components/amount-input.d.ts +2 -1
  21. package/dist/components/amount-input.js +3 -2
  22. package/dist/components/amount-input.js.map +1 -1
  23. package/dist/components/autocomplete.js +1 -0
  24. package/dist/components/autocomplete.js.map +1 -1
  25. package/dist/components/breadcrumbs.d.ts +19 -1
  26. package/dist/components/breadcrumbs.js +11 -4
  27. package/dist/components/breadcrumbs.js.map +1 -1
  28. package/dist/components/button-look.d.ts +37 -7
  29. package/dist/components/button-look.js +33 -6
  30. package/dist/components/button-look.js.map +1 -1
  31. package/dist/components/button.d.ts +4 -1
  32. package/dist/components/button.js +2 -2
  33. package/dist/components/button.js.map +1 -1
  34. package/dist/components/calendar-heatmap.js +2 -1
  35. package/dist/components/calendar-heatmap.js.map +1 -1
  36. package/dist/components/chart-edge.d.ts +10 -0
  37. package/dist/components/chart-edge.js +57 -0
  38. package/dist/components/chart-edge.js.map +1 -0
  39. package/dist/components/chart.js +2 -1
  40. package/dist/components/chart.js.map +1 -1
  41. package/dist/components/chip.d.ts +8 -1
  42. package/dist/components/chip.js +4 -1
  43. package/dist/components/chip.js.map +1 -1
  44. package/dist/components/combobox-core.js +1 -0
  45. package/dist/components/combobox-core.js.map +1 -1
  46. package/dist/components/combobox-suggestions.js +1 -0
  47. package/dist/components/combobox-suggestions.js.map +1 -1
  48. package/dist/components/confirm-context.d.ts +5 -0
  49. package/dist/components/confirm-context.js +7 -0
  50. package/dist/components/confirm-context.js.map +1 -0
  51. package/dist/components/confirm-dialog.js +3 -3
  52. package/dist/components/confirm-dialog.js.map +1 -1
  53. package/dist/components/currency-select.d.ts +28 -2
  54. package/dist/components/currency-select.js +56 -10
  55. package/dist/components/currency-select.js.map +1 -1
  56. package/dist/components/danger-confirm.d.ts +2 -2
  57. package/dist/components/danger-confirm.js +1 -1
  58. package/dist/components/danger-confirm.js.map +1 -1
  59. package/dist/components/data-table-fit.js +21 -3
  60. package/dist/components/data-table-fit.js.map +1 -1
  61. package/dist/components/data-table-mobile.d.ts +58 -3
  62. package/dist/components/data-table-mobile.js +147 -16
  63. package/dist/components/data-table-mobile.js.map +1 -1
  64. package/dist/components/data-table-row-actions.d.ts +2 -1
  65. package/dist/components/data-table-row-actions.js +7 -0
  66. package/dist/components/data-table-row-actions.js.map +1 -1
  67. package/dist/components/data-table-row.d.ts +1 -1
  68. package/dist/components/data-table-row.js.map +1 -1
  69. package/dist/components/data-table.d.ts +34 -1
  70. package/dist/components/data-table.js +37 -10
  71. package/dist/components/data-table.js.map +1 -1
  72. package/dist/components/dropdown.js +1 -0
  73. package/dist/components/dropdown.js.map +1 -1
  74. package/dist/components/error-boundary.js +2 -2
  75. package/dist/components/error-boundary.js.map +1 -1
  76. package/dist/components/floating-panel.d.ts +36 -13
  77. package/dist/components/floating-panel.js +27 -7
  78. package/dist/components/floating-panel.js.map +1 -1
  79. package/dist/components/form-actions.d.ts +1 -1
  80. package/dist/components/form-actions.js +1 -1
  81. package/dist/components/form-actions.js.map +1 -1
  82. package/dist/components/full-bleed-dialog.js +1 -0
  83. package/dist/components/full-bleed-dialog.js.map +1 -1
  84. package/dist/components/icon-button.d.ts +11 -0
  85. package/dist/components/icon-button.js +7 -5
  86. package/dist/components/icon-button.js.map +1 -1
  87. package/dist/components/modal.js +1 -0
  88. package/dist/components/modal.js.map +1 -1
  89. package/dist/components/nav-pills.d.ts +8 -1
  90. package/dist/components/nav-pills.js +61 -38
  91. package/dist/components/nav-pills.js.map +1 -1
  92. package/dist/components/number-input.d.ts +2 -1
  93. package/dist/components/number-input.js +6 -2
  94. package/dist/components/number-input.js.map +1 -1
  95. package/dist/components/numpad-sheet.js +2 -0
  96. package/dist/components/numpad-sheet.js.map +1 -1
  97. package/dist/components/page-header-context.d.ts +8 -0
  98. package/dist/components/page-header-context.js +7 -0
  99. package/dist/components/page-header-context.js.map +1 -0
  100. package/dist/components/page-header.d.ts +14 -2
  101. package/dist/components/page-header.js +10 -1
  102. package/dist/components/page-header.js.map +1 -1
  103. package/dist/components/picker-sheet.js +2 -0
  104. package/dist/components/picker-sheet.js.map +1 -1
  105. package/dist/components/pie-chart.js +2 -1
  106. package/dist/components/pie-chart.js.map +1 -1
  107. package/dist/components/popover.js +1 -0
  108. package/dist/components/popover.js.map +1 -1
  109. package/dist/components/progress-bar.js +5 -4
  110. package/dist/components/progress-bar.js.map +1 -1
  111. package/dist/components/reauth-dialog.js +1 -1
  112. package/dist/components/reauth-dialog.js.map +1 -1
  113. package/dist/components/row-actions-labels.js +3 -1
  114. package/dist/components/row-actions-labels.js.map +1 -1
  115. package/dist/components/row-actions.d.ts +43 -9
  116. package/dist/components/row-actions.js +78 -15
  117. package/dist/components/row-actions.js.map +1 -1
  118. package/dist/components/section-nav.d.ts +37 -0
  119. package/dist/components/section-nav.js +33 -0
  120. package/dist/components/section-nav.js.map +1 -0
  121. package/dist/components/series-chart.js +8 -3
  122. package/dist/components/series-chart.js.map +1 -1
  123. package/dist/components/share-card.js +1 -1
  124. package/dist/components/share-card.js.map +1 -1
  125. package/dist/components/signature-pad.js +1 -1
  126. package/dist/components/signature-pad.js.map +1 -1
  127. package/dist/components/sparkline.js +2 -1
  128. package/dist/components/sparkline.js.map +1 -1
  129. package/dist/components/stat-tile.js +14 -7
  130. package/dist/components/stat-tile.js.map +1 -1
  131. package/dist/components/status-dot.d.ts +5 -1
  132. package/dist/components/status-dot.js +4 -1
  133. package/dist/components/status-dot.js.map +1 -1
  134. package/dist/components/tooltip.js +1 -1
  135. package/dist/components/tooltip.js.map +1 -1
  136. package/dist/components/translation-review-editor.js +2 -2
  137. package/dist/components/translation-review-editor.js.map +1 -1
  138. package/dist/components/translation-review.js +2 -2
  139. package/dist/components/translation-review.js.map +1 -1
  140. package/dist/components/treemap.d.ts +5 -2
  141. package/dist/components/treemap.js +26 -6
  142. package/dist/components/treemap.js.map +1 -1
  143. package/dist/components/use-table-state.js +37 -6
  144. package/dist/components/use-table-state.js.map +1 -1
  145. package/dist/data-table.js.map +1 -1
  146. package/dist/demo/demo-banner.js +1 -1
  147. package/dist/demo/demo-banner.js.map +1 -1
  148. package/dist/demo/demo-ended.js +1 -1
  149. package/dist/demo/demo-ended.js.map +1 -1
  150. package/dist/demo/demo-start.js +1 -1
  151. package/dist/demo/demo-start.js.map +1 -1
  152. package/dist/eslint/index.js +1 -1
  153. package/dist/feedback/feedback-inbox.js +1 -1
  154. package/dist/feedback/feedback-inbox.js.map +1 -1
  155. package/dist/hooks/use-dismiss.d.ts +9 -1
  156. package/dist/hooks/use-dismiss.js +25 -5
  157. package/dist/hooks/use-dismiss.js.map +1 -1
  158. package/dist/hooks/use-document-title.d.ts +14 -0
  159. package/dist/hooks/use-document-title.js +22 -0
  160. package/dist/hooks/use-document-title.js.map +1 -0
  161. package/dist/i18n/defaults.js +2 -0
  162. package/dist/i18n/defaults.js.map +1 -1
  163. package/dist/i18n/german.js +20 -3
  164. package/dist/i18n/german.js.map +1 -1
  165. package/dist/i18n/kit-labels.d.ts +34 -1
  166. package/dist/i18n/kit-labels.js +43 -2
  167. package/dist/i18n/kit-labels.js.map +1 -1
  168. package/dist/i18n/locales/en.js +22 -3
  169. package/dist/i18n/locales/en.js.map +3 -3
  170. package/dist/i18n/locales/es.js +20 -3
  171. package/dist/i18n/locales/es.js.map +1 -1
  172. package/dist/i18n/locales/fr.js +20 -3
  173. package/dist/i18n/locales/fr.js.map +1 -1
  174. package/dist/i18n/locales/hu.js +20 -3
  175. package/dist/i18n/locales/hu.js.map +1 -1
  176. package/dist/i18n/locales/it.js +20 -3
  177. package/dist/i18n/locales/it.js.map +1 -1
  178. package/dist/i18n/locales/zh.js +20 -3
  179. package/dist/i18n/locales/zh.js.map +1 -1
  180. package/dist/i18n/review.js +3 -0
  181. package/dist/i18n/review.js.map +1 -1
  182. package/dist/i18n/shared-labels.js +1 -0
  183. package/dist/i18n/shared-labels.js.map +1 -1
  184. package/dist/index.d.ts +8 -1
  185. package/dist/index.js +11 -0
  186. package/dist/index.js.map +1 -1
  187. package/dist/landing/landing-actions.js +2 -2
  188. package/dist/landing/landing-actions.js.map +1 -1
  189. package/dist/landing/public-header.js +2 -2
  190. package/dist/landing/public-header.js.map +1 -1
  191. package/dist/lib/clipboard-minus.d.ts +27 -0
  192. package/dist/lib/clipboard-minus.js +63 -0
  193. package/dist/lib/clipboard-minus.js.map +1 -0
  194. package/dist/lib/data-attributes.d.ts +11 -0
  195. package/dist/lib/data-attributes.js +10 -0
  196. package/dist/lib/data-attributes.js.map +1 -0
  197. package/dist/lib/document-title.d.ts +17 -0
  198. package/dist/lib/document-title.js +29 -0
  199. package/dist/lib/document-title.js.map +1 -0
  200. package/dist/lib/float-clearance.js +8 -2
  201. package/dist/lib/float-clearance.js.map +1 -1
  202. package/dist/lib/format.d.ts +36 -10
  203. package/dist/lib/format.js +57 -11
  204. package/dist/lib/format.js.map +1 -1
  205. package/dist/lib/minus.d.ts +32 -0
  206. package/dist/lib/minus.js +14 -0
  207. package/dist/lib/minus.js.map +1 -0
  208. package/dist/lib/status-tone.d.ts +30 -0
  209. package/dist/lib/status-tone.js +17 -0
  210. package/dist/lib/status-tone.js.map +1 -0
  211. package/dist/settings/settings-layout.js +3 -2
  212. package/dist/settings/settings-layout.js.map +1 -1
  213. package/dist/shell/account-menu-language.d.ts +1 -1
  214. package/dist/shell/account-menu-language.js +9 -1
  215. package/dist/shell/account-menu-language.js.map +1 -1
  216. package/dist/shell/app-shell-labels.js +2 -1
  217. package/dist/shell/app-shell-labels.js.map +1 -1
  218. package/dist/shell/app-shell.d.ts +58 -16
  219. package/dist/shell/app-shell.js +151 -43
  220. package/dist/shell/app-shell.js.map +1 -1
  221. package/dist/shell/language-code.d.ts +3 -0
  222. package/dist/shell/language-code.js +8 -0
  223. package/dist/shell/language-code.js.map +1 -0
  224. package/dist/shell/topbar-controls.d.ts +14 -6
  225. package/dist/shell/topbar-controls.js +14 -17
  226. package/dist/shell/topbar-controls.js.map +1 -1
  227. package/dist/testing/css-variables.generated.d.ts +1 -1
  228. package/dist/testing/css-variables.generated.js +1 -1
  229. package/dist/testing/css-variables.generated.js.map +1 -1
  230. package/dist/testing/react.d.ts +25 -0
  231. package/dist/testing/react.js +19 -0
  232. package/dist/testing/react.js.map +1 -0
  233. package/dist/theme/chart-palette.d.ts +82 -15
  234. package/dist/theme/chart-palette.js +51 -24
  235. package/dist/theme/chart-palette.js.map +1 -1
  236. package/dist/theme/palette-presets.d.ts +9 -0
  237. package/dist/theme/palette-presets.js +77 -62
  238. package/dist/theme/palette-presets.js.map +1 -1
  239. package/dist/tour/tour-dock.d.ts +42 -0
  240. package/dist/tour/tour-dock.js +80 -0
  241. package/dist/tour/tour-dock.js.map +1 -0
  242. package/dist/tour/tour-gates.d.ts +71 -0
  243. package/dist/tour/tour-gates.js +114 -0
  244. package/dist/tour/tour-gates.js.map +1 -0
  245. package/dist/tour/tour-guide-button.d.ts +27 -0
  246. package/dist/tour/tour-guide-button.js +45 -0
  247. package/dist/tour/tour-guide-button.js.map +1 -0
  248. package/dist/tour/tour-guide-labels.d.ts +8 -0
  249. package/dist/tour/tour-guide-labels.js +9 -0
  250. package/dist/tour/tour-guide-labels.js.map +1 -0
  251. package/dist/tour/tour-labels.js +9 -1
  252. package/dist/tour/tour-labels.js.map +1 -1
  253. package/dist/tour/tour.d.ts +133 -9
  254. package/dist/tour/tour.js +594 -70
  255. package/dist/tour/tour.js.map +1 -1
  256. package/dist/tour/use-tour-url-sync.d.ts +5 -2
  257. package/dist/tour/use-tour-url-sync.js +7 -1
  258. package/dist/tour/use-tour-url-sync.js.map +1 -1
  259. package/dist/tour.d.ts +2 -0
  260. package/dist/tour.js +3 -0
  261. package/dist/tour.js.map +1 -1
  262. package/dist/wizard/stepper-nav.d.ts +1 -1
  263. package/dist/wizard/stepper-nav.js +18 -5
  264. package/dist/wizard/stepper-nav.js.map +1 -1
  265. package/dist/wizard/types.d.ts +11 -0
  266. package/dist/wizard/types.js.map +1 -1
  267. package/dist/wizard/use-wizard.js +11 -5
  268. package/dist/wizard/use-wizard.js.map +1 -1
  269. package/package.json +9 -1
  270. package/src/admin/admin-action-confirm.tsx +1 -1
  271. package/src/admin/invitations-panel.tsx +7 -3
  272. package/src/admin/user-roster.tsx +41 -14
  273. package/src/auth/accept-invitation.tsx +3 -3
  274. package/src/auth/not-found-page.tsx +1 -1
  275. package/src/auth/reset-password-form.tsx +3 -3
  276. package/src/auth/verify-email.tsx +13 -3
  277. package/src/billing/plan-card.tsx +2 -2
  278. package/src/components/amount-input.tsx +5 -4
  279. package/src/components/autocomplete.tsx +2 -0
  280. package/src/components/breadcrumbs.tsx +32 -4
  281. package/src/components/button-look.ts +86 -15
  282. package/src/components/button.tsx +6 -3
  283. package/src/components/calendar-heatmap.tsx +2 -1
  284. package/src/components/chart-edge.ts +73 -0
  285. package/src/components/chart.tsx +3 -1
  286. package/src/components/chip.tsx +10 -1
  287. package/src/components/combobox-core.tsx +2 -0
  288. package/src/components/combobox-suggestions.tsx +2 -0
  289. package/src/components/confirm-context.ts +7 -0
  290. package/src/components/confirm-dialog.tsx +3 -3
  291. package/src/components/currency-select.tsx +95 -6
  292. package/src/components/danger-confirm.tsx +3 -3
  293. package/src/components/data-table-fit.ts +41 -6
  294. package/src/components/data-table-mobile.tsx +259 -17
  295. package/src/components/data-table-row-actions.tsx +9 -1
  296. package/src/components/data-table-row.tsx +3 -0
  297. package/src/components/data-table.tsx +91 -19
  298. package/src/components/dropdown.tsx +2 -0
  299. package/src/components/error-boundary.tsx +2 -2
  300. package/src/components/floating-panel.tsx +67 -16
  301. package/src/components/form-actions.tsx +2 -2
  302. package/src/components/full-bleed-dialog.tsx +3 -0
  303. package/src/components/icon-button.tsx +21 -8
  304. package/src/components/modal.tsx +3 -0
  305. package/src/components/nav-pills.tsx +29 -2
  306. package/src/components/number-input.tsx +8 -3
  307. package/src/components/numpad-sheet.tsx +4 -0
  308. package/src/components/page-header-context.ts +8 -0
  309. package/src/components/page-header.tsx +34 -2
  310. package/src/components/picker-sheet.tsx +4 -0
  311. package/src/components/pie-chart.tsx +2 -1
  312. package/src/components/popover.tsx +2 -0
  313. package/src/components/progress-bar.tsx +6 -3
  314. package/src/components/reauth-dialog.tsx +1 -1
  315. package/src/components/row-actions-labels.ts +2 -0
  316. package/src/components/row-actions.tsx +154 -33
  317. package/src/components/section-nav.tsx +65 -0
  318. package/src/components/series-chart.tsx +12 -3
  319. package/src/components/share-card.tsx +1 -1
  320. package/src/components/signature-pad.tsx +1 -1
  321. package/src/components/sparkline.tsx +2 -1
  322. package/src/components/stat-tile.tsx +19 -7
  323. package/src/components/status-dot.tsx +7 -1
  324. package/src/components/tooltip.tsx +3 -1
  325. package/src/components/translation-review-editor.tsx +2 -2
  326. package/src/components/translation-review.tsx +2 -2
  327. package/src/components/treemap.tsx +34 -8
  328. package/src/components/use-table-state.ts +63 -8
  329. package/src/data-table.ts +3 -3
  330. package/src/demo/demo-banner.tsx +1 -1
  331. package/src/demo/demo-ended.tsx +1 -1
  332. package/src/demo/demo-start.tsx +1 -1
  333. package/src/feedback/feedback-inbox.tsx +1 -1
  334. package/src/hooks/use-dismiss.ts +43 -4
  335. package/src/hooks/use-document-title.ts +31 -0
  336. package/src/i18n/defaults.ts +2 -0
  337. package/src/i18n/german.ts +17 -0
  338. package/src/i18n/kit-labels.tsx +79 -1
  339. package/src/i18n/locales/es.ts +17 -0
  340. package/src/i18n/locales/fr.ts +17 -0
  341. package/src/i18n/locales/hu.ts +17 -0
  342. package/src/i18n/locales/it.ts +17 -0
  343. package/src/i18n/locales/zh.ts +17 -0
  344. package/src/i18n/review.ts +3 -0
  345. package/src/i18n/shared-labels.ts +1 -0
  346. package/src/index.ts +13 -0
  347. package/src/landing/landing-actions.tsx +2 -2
  348. package/src/landing/public-header.tsx +2 -2
  349. package/src/lib/clipboard-minus.ts +94 -0
  350. package/src/lib/data-attributes.ts +14 -0
  351. package/src/lib/document-title.ts +41 -0
  352. package/src/lib/float-clearance.ts +19 -3
  353. package/src/lib/format.ts +117 -30
  354. package/src/lib/minus.ts +50 -0
  355. package/src/lib/status-tone.ts +43 -0
  356. package/src/settings/settings-layout.tsx +3 -2
  357. package/src/shell/account-menu-language.tsx +14 -2
  358. package/src/shell/app-shell-labels.ts +1 -0
  359. package/src/shell/app-shell.tsx +291 -68
  360. package/src/shell/language-code.ts +5 -0
  361. package/src/shell/topbar-controls.tsx +35 -15
  362. package/src/testing/css-variables.generated.ts +6 -6
  363. package/src/testing/react.ts +59 -0
  364. package/src/theme/chart-palette.ts +102 -20
  365. package/src/theme/palette-presets.ts +121 -84
  366. package/src/tour/tour-dock.ts +132 -0
  367. package/src/tour/tour-gates.ts +195 -0
  368. package/src/tour/tour-guide-button.tsx +64 -0
  369. package/src/tour/tour-guide-labels.ts +15 -0
  370. package/src/tour/tour-labels.ts +8 -0
  371. package/src/tour/tour.tsx +949 -86
  372. package/src/tour/use-tour-url-sync.ts +14 -4
  373. package/src/tour.ts +3 -0
  374. package/src/wizard/stepper-nav.tsx +27 -7
  375. package/src/wizard/types.ts +12 -0
  376. package/src/wizard/use-wizard.ts +16 -9
  377. package/tokens.css +28 -18
@@ -551,6 +551,7 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
551
551
  theme: "Changer de thème",
552
552
  palette: "Préréglage d’apparence",
553
553
  language: "Langue",
554
+ languageCurrent: (name) => `Choisir la langue, actuelle\u00a0: ${name}`,
554
555
  switchRole: "Changer de rôle",
555
556
  role: (value) => `Rôle\u00a0: ${value}`,
556
557
  },
@@ -631,6 +632,19 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
631
632
  awaitClickHint: "Cliquez sur l’élément mis en évidence pour continuer",
632
633
  awaitInputHint: "Saisissez une valeur dans le champ mis en évidence pour continuer", // 0.46: TourStep.awaitInput (keksdose live #436).
633
634
  step: (current, total) => `${n(current)} / ${n(total)}`,
635
+ // 0.50: guided form tours (docs/tours-harmonization.md §11).
636
+ leaveEmpty: "Laisser vide",
637
+ optional: "Facultatif",
638
+ gateHint: "Remplissez le champ mis en évidence pour continuer",
639
+ gateReady: "Prêt. Appuyez sur Suivant ou sur Entrée pour continuer.",
640
+ awaitGateHint: "Enregistrez pour continuer",
641
+ awaitGateRefused: "Ce n’est pas encore enregistré. Vérifiez le formulaire et réessayez.",
642
+ backToStep: (title) => `Retour à «\u202f${title}\u202f»`,
643
+ },
644
+ // 0.50: the form's "Guide me" (TourGuideButton, docs/tours-harmonization.md §11).
645
+ tourGuide: {
646
+ label: "Me guider",
647
+ description: "Vous guide dans ce formulaire, champ par champ",
634
648
  },
635
649
  // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page. keksdose's reviewed words.
636
650
  tourCatalogue: {
@@ -1909,10 +1923,13 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
1909
1923
  rowActions: {
1910
1924
  actions: "Actions",
1911
1925
  actionsFor: (name) => `Actions pour ${name}`,
1926
+ confirmTitle: (action, name) => (name ? `${action} «\u00a0${name}\u00a0»\u00a0?` : `${action}\u00a0?`),
1927
+ confirmBody: "Cette action est irréversible.",
1912
1928
  },
1913
1929
  appShellMore: {
1914
1930
  more: "Plus",
1915
1931
  moreTitle: "Autres pages",
1932
+ overview: "Vue d’ensemble",
1916
1933
  },
1917
1934
  // 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words.
1918
1935
  mailTestCard: {
@@ -541,6 +541,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
541
541
  theme: "Téma váltása",
542
542
  palette: "Megjelenési séma",
543
543
  language: "Nyelv",
544
+ languageCurrent: (name) => `Nyelv kiválasztása, jelenlegi: ${name}`,
544
545
  switchRole: "Szerepkör váltása",
545
546
  role: (value) => `Szerepkör: ${value}`,
546
547
  },
@@ -618,6 +619,19 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
618
619
  awaitClickHint: "A folytatáshoz kattintson a kiemelt elemre",
619
620
  awaitInputHint: "A folytatáshoz adjon meg egy értéket a kiemelt mezőben", // 0.46: TourStep.awaitInput (keksdose live #436).
620
621
  step: (current, total) => `${n(current)} / ${n(total)}`,
622
+ // 0.50: guided form tours (docs/tours-harmonization.md §11).
623
+ leaveEmpty: "Hagyja üresen",
624
+ optional: "Nem kötelező",
625
+ gateHint: "A folytatáshoz töltse ki a kiemelt mezőt",
626
+ gateReady: "Kész. A folytatáshoz nyomja meg a Tovább gombot vagy az Entert.",
627
+ awaitGateHint: "A folytatáshoz mentsen",
628
+ awaitGateRefused: "Ez még nincs mentve. Ellenőrizze az űrlapot, és próbálja újra.",
629
+ backToStep: (title) => `Vissza ide: „${title}”`,
630
+ },
631
+ // 0.50: the form's "Guide me" (TourGuideButton, docs/tours-harmonization.md §11).
632
+ tourGuide: {
633
+ label: "Vezessen végig",
634
+ description: "Mezőről mezőre végigvezeti az űrlapon",
621
635
  },
622
636
  // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page.
623
637
  tourCatalogue: {
@@ -1875,10 +1889,13 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
1875
1889
  rowActions: {
1876
1890
  actions: "Műveletek",
1877
1891
  actionsFor: (name) => `${name} – műveletek`,
1892
+ confirmTitle: (action, name) => (name ? `${action}: „${name}”?` : `${action}?`),
1893
+ confirmBody: "Ez nem vonható vissza.",
1878
1894
  },
1879
1895
  appShellMore: {
1880
1896
  more: "Továbbiak",
1881
1897
  moreTitle: "További oldalak",
1898
+ overview: "Áttekintés",
1882
1899
  },
1883
1900
  // 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Ön", as
1884
1901
  // the rest of the catalogue.
@@ -546,6 +546,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
546
546
  theme: "Cambia tema",
547
547
  palette: "Preset di aspetto",
548
548
  language: "Lingua",
549
+ languageCurrent: (name) => `Scegli la lingua, attuale: ${name}`,
549
550
  switchRole: "Cambia ruolo",
550
551
  role: (value) => `Ruolo: ${value}`,
551
552
  },
@@ -620,6 +621,19 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
620
621
  awaitClickHint: "Faccia clic sull’elemento evidenziato per continuare",
621
622
  awaitInputHint: "Inserisca un valore nel campo evidenziato per continuare", // 0.46: TourStep.awaitInput (keksdose live #436).
622
623
  step: (current, total) => `${n(current)} / ${n(total)}`,
624
+ // 0.50: guided form tours (docs/tours-harmonization.md §11).
625
+ leaveEmpty: "Lascia vuoto",
626
+ optional: "Facoltativo",
627
+ gateHint: "Compili il campo evidenziato per continuare",
628
+ gateReady: "Pronto. Prema Avanti o Invio per continuare.",
629
+ awaitGateHint: "Salvi per continuare",
630
+ awaitGateRefused: "Non è ancora stato salvato. Controlli il modulo e riprovi.",
631
+ backToStep: (title) => `Torna a «${title}»`,
632
+ },
633
+ // 0.50: the form's "Guide me" (TourGuideButton, docs/tours-harmonization.md §11).
634
+ tourGuide: {
635
+ label: "Guidami",
636
+ description: "La guida in questo modulo, campo per campo",
623
637
  },
624
638
  // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page. keksdose's reviewed words.
625
639
  tourCatalogue: {
@@ -1888,10 +1902,13 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
1888
1902
  rowActions: {
1889
1903
  actions: "Azioni",
1890
1904
  actionsFor: (name) => `Azioni per ${name}`,
1905
+ confirmTitle: (action, name) => (name ? `${action} «${name}»?` : `${action}?`),
1906
+ confirmBody: "Questa azione non può essere annullata.",
1891
1907
  },
1892
1908
  appShellMore: {
1893
1909
  more: "Altro",
1894
1910
  moreTitle: "Altre pagine",
1911
+ overview: "Panoramica",
1895
1912
  },
1896
1913
  // 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Lei", as
1897
1914
  // the rest of the catalogue.
@@ -529,6 +529,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
529
529
  theme: "切换主题",
530
530
  palette: "外观预设",
531
531
  language: "语言",
532
+ languageCurrent: (name) => `选择语言,当前:${name}`,
532
533
  switchRole: "切换角色",
533
534
  role: (value) => `角色:${value}`,
534
535
  },
@@ -599,6 +600,19 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
599
600
  awaitClickHint: "点击高亮的元素以继续",
600
601
  awaitInputHint: "在高亮的字段中输入一个值以继续", // 0.46: TourStep.awaitInput (keksdose live #436).
601
602
  step: (current, total) => `${n(current)} / ${n(total)}`,
603
+ // 0.50: guided form tours (docs/tours-harmonization.md §11).
604
+ leaveEmpty: "留空",
605
+ optional: "可选",
606
+ gateHint: "填写高亮的字段以继续",
607
+ gateReady: "已就绪。按“下一步”或回车键继续。",
608
+ awaitGateHint: "保存以继续",
609
+ awaitGateRefused: "尚未保存。请检查表单后重试。",
610
+ backToStep: (title) => `返回“${title}”`,
611
+ },
612
+ // 0.50: the form's "Guide me" (TourGuideButton, docs/tours-harmonization.md §11).
613
+ tourGuide: {
614
+ label: "引导我",
615
+ description: "逐个字段引导您填写此表单",
602
616
  },
603
617
  // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page.
604
618
  tourCatalogue: {
@@ -1795,10 +1809,13 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
1795
1809
  rowActions: {
1796
1810
  actions: "操作",
1797
1811
  actionsFor: (name) => `${name} 的操作`,
1812
+ confirmTitle: (action, name) => (name ? `${action}“${name}”?` : `${action}?`),
1813
+ confirmBody: "此操作无法撤销。",
1798
1814
  },
1799
1815
  appShellMore: {
1800
1816
  more: "更多",
1801
1817
  moreTitle: "更多页面",
1818
+ overview: "概览",
1802
1819
  },
1803
1820
  // 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words.
1804
1821
  mailTestCard: {
@@ -153,9 +153,11 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
153
153
  "tabs.remove": [["{{tab}}"]],
154
154
  "appShell.toggleGroup": [["{{group}}"]],
155
155
  "topBar.role": [["{{role}}"]],
156
+ "topBar.languageCurrent": [["{{name}}"]],
156
157
  "file.size": [[2_500_000]],
157
158
  "wizard.step": [[2, 4]],
158
159
  "tour.step": [[2, 4]],
160
+ "tour.backToStep": [["{{title}}"]],
159
161
  "globalSearch.shortcut": [["{{keys}}"]],
160
162
  "pieChart.slice": [["{{label}}", "{{value}}", "{{percent}}"]],
161
163
  "sparkline.rising": [["{{first}}", "{{last}}"]],
@@ -279,6 +281,7 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
279
281
  "signChip.direction": [["{{current}}", "{{next}}"]],
280
282
  "signChip.switchTo": [["{{next}}"]],
281
283
  "rowActions.actionsFor": [["{{name}}"]],
284
+ "rowActions.confirmTitle": [["{{action}}", "{{name}}"], ["{{action}}"]],
282
285
  "columnMapper.readError": [["{{name}}"]],
283
286
  "columnMapper.summary": [
284
287
  [1, 1],
@@ -106,6 +106,7 @@ export const DEFAULT_TOP_BAR_LABELS: TopBarLabels = {
106
106
  theme: "Toggle theme",
107
107
  palette: "Appearance preset",
108
108
  language: "Language",
109
+ languageCurrent: (name) => `Choose language, current: ${name}`,
109
110
  switchRole: "Switch role",
110
111
  role: (value) => `Role: ${value}`,
111
112
  };
package/src/index.ts CHANGED
@@ -154,6 +154,12 @@ export { useCloseTransition, OVERLAY_EXIT_MS } from "./hooks/use-close-transitio
154
154
  export { useRowSwipe } from "./hooks/use-row-swipe";
155
155
  // 0.28: keksdose's noindex marker, for the legal and auth pages of every app.
156
156
  export { useNoIndex } from "./hooks/use-noindex";
157
+ // 0.49 KIT-9: the window's title from page, section and app, in the provider's format.
158
+ export { useDocumentTitle } from "./hooks/use-document-title";
159
+ // 0.49 KIT-1b: U+2212 before a number as "-", the copy handler's rule, for an app's text.
160
+ export { asciiMinusInNumbers } from "./lib/clipboard-minus";
161
+ export { DEFAULT_DOCUMENT_TITLE_FORMAT, formatDocumentTitle } from "./lib/document-title";
162
+ export type { DocumentTitleParts } from "./lib/document-title";
157
163
  export type { SwipeStage, RowSwipeOptions, RowSwipeReturn } from "./hooks/use-row-swipe";
158
164
  // Any element as a file drop target, screened like FileButton (kastlan).
159
165
  export { useFileDrop, dragHasFiles } from "./hooks/use-file-drop";
@@ -381,10 +387,15 @@ export * from "./components/alert-banner";
381
387
  export * from "./components/toggle-group";
382
388
  // A wrapping set of links/buttons marked `aria-current` — Tabs-like looks, not tabs.
383
389
  export * from "./components/nav-pills";
390
+ // 0.49.0 (KIT-6): a section's pages on the phone, one scrolling row of pills.
391
+ export * from "./components/section-nav";
384
392
  // A pill carrying one VALUE — inert, a link, or a toggle, depending on which prop it is
385
393
  // given — and the list field built from it. Distinct from Button on purpose: a row of
386
394
  // buttons reads as "choose an action", a row of chips as "here are the things".
387
395
  export * from "./components/chip";
396
+ // 0.49.0 (KIT-8): five domain-free status kinds and their tones — Chip/StatusDot `status`.
397
+ export { statusTone, STATUS_KINDS } from "./lib/status-tone";
398
+ export type { StatusKind, StatusTone } from "./lib/status-tone";
388
399
  // ChipInput's own module since MOD-9: it renders Chip, so chip.tsx re-exports only its types.
389
400
  export * from "./components/chip-input";
390
401
  export * from "./components/wizard-stepper";
@@ -589,6 +600,8 @@ export type {
589
600
  RowActionsColumnOptions,
590
601
  RowActionsHeadProps,
591
602
  RowActionTone,
603
+ // 0.49 KIT-12: a danger action's confirmation words.
604
+ RowActionConfirm,
592
605
  } from "./components/row-actions";
593
606
  // 0.22.0: the inputs round (kastlan's, keksdose's and Kurvenschmiede's audits).
594
607
  export * from "./components/checkbox-group";
@@ -77,12 +77,12 @@ export function LandingActions({
77
77
  className={cn("flex flex-col gap-3 sm:flex-row", align === "center" && "sm:justify-center", className)}
78
78
  >
79
79
  {session === "user" ? (
80
- <Button href={openAppHref} variant="brand" size={buttonSize} className={extra}>
80
+ <Button href={openAppHref} variant="accent" size={buttonSize} className={extra}>
81
81
  {labels.openApp}
82
82
  </Button>
83
83
  ) : (
84
84
  <>
85
- <Button href={accessLink.href} variant="brand" size={buttonSize} className={extra}>
85
+ <Button href={accessLink.href} variant="accent" size={buttonSize} className={extra}>
86
86
  {accessLink.label}
87
87
  </Button>
88
88
  {demoHref !== null && (
@@ -82,7 +82,7 @@ export function PublicHeader({
82
82
 
83
83
  const actions =
84
84
  session === "user" ? (
85
- <Button href={openAppHref} variant="brand" size={size}>
85
+ <Button href={openAppHref} variant="accent" size={size}>
86
86
  {labels.openApp}
87
87
  </Button>
88
88
  ) : (
@@ -96,7 +96,7 @@ export function PublicHeader({
96
96
  {labels.signIn}
97
97
  </Button>
98
98
  )}
99
- <Button href={accessLink.href} variant="brand" size={size}>
99
+ <Button href={accessLink.href} variant="accent" size={size}>
100
100
  {accessLink.label}
101
101
  </Button>
102
102
  </>
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The copy handler for the one minus sign (0.49 KIT-1b, Marcel's follow-up to KIT-1).
3
+ *
4
+ * The kit prints a negative with U+2212 (lib/minus.ts). A spreadsheet does not read that
5
+ * as a number: a figure copied out of a kit table and pasted into Excel landed as text.
6
+ * So while a selection inside the page is copied, every U+2212 standing directly before a
7
+ * number — optionally after a currency code or symbol and spaces: "−CHF 1,250.50",
8
+ * "−€2,000", "−12%" — becomes the ASCII "-" in the clipboard, in `text/plain` AND in
9
+ * `text/html` (Excel pastes the HTML flavour, as cells). The page keeps "−".
10
+ *
11
+ * A copy with no such minus is left to the browser untouched, and so is any copy out of an
12
+ * input, a textarea or an editable element: what the person typed is theirs.
13
+ *
14
+ * Installed once per document by `UiKitProvider` (`copyAsciiMinus`, default `true`) —
15
+ * the one part every app mounts, the signed-out pages included.
16
+ */
17
+
18
+ /** U+2212, then (optionally) a currency code or symbol, then spaces, then a digit. */
19
+ const MINUS_BEFORE_NUMBER = /\u2212(?=(?:[A-Z]{3}|\p{Sc})?[\s\u00a0\u202f]*\d)/gu;
20
+
21
+ /**
22
+ * `text` with each U+2212 that stands before a number (in the sense above) as the ASCII
23
+ * hyphen-minus, every other character as it was — for text an app hands a machine that
24
+ * did not come from a formatter's `minus: "ascii"`.
25
+ */
26
+ export function asciiMinusInNumbers(text: string): string {
27
+ return text.replace(MINUS_BEFORE_NUMBER, "-");
28
+ }
29
+
30
+ function hasMinusBeforeNumber(text: string): boolean {
31
+ MINUS_BEFORE_NUMBER.lastIndex = 0;
32
+ const found = MINUS_BEFORE_NUMBER.test(text);
33
+ MINUS_BEFORE_NUMBER.lastIndex = 0;
34
+ return found;
35
+ }
36
+
37
+ /** An input, a textarea or an editable element: a copy there is the person's own text. */
38
+ function isEditable(node: Node | null | undefined): boolean {
39
+ const el = node instanceof Element ? node : node?.parentElement;
40
+ if (!el) return false;
41
+ if (el.closest("input, textarea, select")) return true;
42
+ return (el as HTMLElement).isContentEditable === true || el.closest('[contenteditable=""], [contenteditable="true"]') !== null;
43
+ }
44
+
45
+ /** Table parts copied alone are wrapped back into a table, which Excel reads as cells. */
46
+ function wrapTableParts(container: HTMLElement): string {
47
+ const first = container.firstElementChild?.tagName;
48
+ const html = container.innerHTML;
49
+ if (first === "TD" || first === "TH") return `<table><tbody><tr>${html}</tr></tbody></table>`;
50
+ if (first === "TR") return `<table><tbody>${html}</tbody></table>`;
51
+ if (first === "TBODY" || first === "THEAD" || first === "TFOOT") return `<table>${html}</table>`;
52
+ return html;
53
+ }
54
+
55
+ function onCopy(event: ClipboardEvent): void {
56
+ if (event.defaultPrevented || !event.clipboardData) return;
57
+ const doc = (event.target as Node | null)?.ownerDocument ?? document;
58
+ if (isEditable(event.target as Node | null) || isEditable(doc.activeElement)) return;
59
+ const selection = doc.getSelection();
60
+ if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
61
+ if (isEditable(selection.anchorNode) || isEditable(selection.focusNode)) return;
62
+ const text = selection.toString();
63
+ if (!hasMinusBeforeNumber(text)) return;
64
+
65
+ const container = doc.createElement("div");
66
+ for (let i = 0; i < selection.rangeCount; i++) container.appendChild(selection.getRangeAt(i).cloneContents());
67
+ const walker = doc.createTreeWalker(container, NodeFilter.SHOW_TEXT);
68
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
69
+ node.nodeValue = asciiMinusInNumbers(node.nodeValue ?? "");
70
+ }
71
+ event.clipboardData.setData("text/plain", asciiMinusInNumbers(text));
72
+ event.clipboardData.setData("text/html", wrapTableParts(container));
73
+ event.preventDefault();
74
+ }
75
+
76
+ const installs = new WeakMap<Document, number>();
77
+
78
+ /**
79
+ * Listen for copies on `doc` until the returned function is called. Counted: nested
80
+ * providers install it once, and the last one to go removes it.
81
+ */
82
+ export function installClipboardMinus(doc: Document = document): () => void {
83
+ const count = installs.get(doc) ?? 0;
84
+ if (count === 0) doc.addEventListener("copy", onCopy);
85
+ installs.set(doc, count + 1);
86
+ let done = false;
87
+ return () => {
88
+ if (done) return;
89
+ done = true;
90
+ const left = (installs.get(doc) ?? 1) - 1;
91
+ installs.set(doc, left);
92
+ if (left === 0) doc.removeEventListener("copy", onCopy);
93
+ };
94
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * A component's `data-*` props, for its root element (0.50, keksdose): `data-tour`,
3
+ * `data-testid` and the like reach the DOM even when the props are not spread there
4
+ * whole. TypeScript does not check hyphenated JSX attributes, so without this a
5
+ * `data-tour` on such a component vanished without an error.
6
+ */
7
+ export type DataAttributes = { [attribute: `data-${string}`]: string | number | boolean | undefined };
8
+
9
+ /** Only the `data-*` entries of `props`. */
10
+ export function dataAttributes(props: object): DataAttributes {
11
+ const out: DataAttributes = {};
12
+ for (const [k, v] of Object.entries(props)) if (k.startsWith("data-")) out[k as `data-${string}`] = v;
13
+ return out;
14
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The window's title from its parts (0.49 KIT-9): `{page}`, `{section}` and `{app}` in a
3
+ * format, by default "{page} · {section} · {app}" — "Invoices · Finance · Kastlan".
4
+ *
5
+ * A part that is missing (undefined, null, empty or blank) is dropped WITH the separator
6
+ * before it, so "Invoices · Kastlan" never reads "Invoices · · Kastlan". The text between
7
+ * two parts is the separator; text before the first part and after the last is kept as
8
+ * written ("[{page} – {app}]" → "[KS]"). No part at all gives "".
9
+ */
10
+ export const DEFAULT_DOCUMENT_TITLE_FORMAT = "{page} · {section} · {app}";
11
+
12
+ /** The parts {@link formatDocumentTitle} fills in. */
13
+ export interface DocumentTitleParts {
14
+ page?: string | null;
15
+ section?: string | null;
16
+ app?: string | null;
17
+ }
18
+
19
+ const PLACEHOLDER = /\{(page|section|app)\}/g;
20
+
21
+ export function formatDocumentTitle(format: string, parts: DocumentTitleParts): string {
22
+ let prefix = "";
23
+ let out = "";
24
+ let pending = "";
25
+ let last = 0;
26
+ let seen = false;
27
+ for (const m of format.matchAll(PLACEHOLDER)) {
28
+ const literal = format.slice(last, m.index!);
29
+ last = m.index! + m[0].length;
30
+ if (!seen) prefix = literal;
31
+ else pending = out === "" ? "" : literal || pending;
32
+ seen = true;
33
+ const value = parts[m[1] as keyof DocumentTitleParts]?.trim();
34
+ if (!value) continue;
35
+ out += (out === "" ? "" : pending) + value;
36
+ pending = "";
37
+ }
38
+ if (!seen) return format;
39
+ if (out === "") return "";
40
+ return prefix + out + format.slice(last);
41
+ }
@@ -2,7 +2,8 @@ import { useEffect } from "react";
2
2
 
3
3
  /**
4
4
  * The room the floating launchers take above the bottom nav, published as
5
- * `--app-float-clearance` on `<html>` so `AppShell` can pad `<main>` past them.
5
+ * `--app-float-clearance` on `<html>` so `AppShell` can pad `<main>` past them, and with
6
+ * the nav included as `--kit-floating-inset` (0.49 KIT-11, below).
6
7
  *
7
8
  * keksdose dev#612 (UX-1.3, "the help button covers content"): the shell padded `<main>`
8
9
  * for the nav plus 1.5rem, so on a phone the last row of every page ended under the help
@@ -23,12 +24,27 @@ import { useEffect } from "react";
23
24
 
24
25
  const claims = new Map<symbol, string>();
25
26
 
27
+ /**
28
+ * 0.49 KIT-11: `--kit-floating-inset` is the same room measured from the screen's bottom
29
+ * edge — the phone's bottom nav (`--app-nav-h`, 0px from `md` up) or the home indicator,
30
+ * whichever is taller, plus `--app-float-clearance` — so a page's own fixed or sticky
31
+ * footer, or its last row, can clear the launchers with one length at every width.
32
+ * Unset while no launcher is mounted, like `--app-float-clearance`: write
33
+ * `var(--kit-floating-inset, var(--app-nav-h, 0px))`.
34
+ */
35
+ const NAV_OR_HOME_INDICATOR = "max(var(--app-nav-h, 0px), env(safe-area-inset-bottom, 0px))";
36
+
26
37
  function publish() {
27
38
  if (typeof document === "undefined") return;
28
39
  const root = document.documentElement;
29
40
  const values = [...claims.values()];
30
- if (values.length === 0) root.style.removeProperty("--app-float-clearance");
31
- else root.style.setProperty("--app-float-clearance", values.length === 1 ? values[0] : `max(${values.join(", ")})`);
41
+ if (values.length === 0) {
42
+ root.style.removeProperty("--app-float-clearance");
43
+ root.style.removeProperty("--kit-floating-inset");
44
+ return;
45
+ }
46
+ root.style.setProperty("--app-float-clearance", values.length === 1 ? values[0] : `max(${values.join(", ")})`);
47
+ root.style.setProperty("--kit-floating-inset", `calc(${NAV_OR_HOME_INDICATOR} + var(--app-float-clearance))`);
32
48
  }
33
49
 
34
50
  /** Claim `value` (a CSS length: the launcher's top edge above the nav, plus a gap) until