@poodle64/ui 2026.8.7

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 (387) hide show
  1. package/README.md +624 -0
  2. package/dist/components/ui/alert/alert-description.svelte +23 -0
  3. package/dist/components/ui/alert/alert-description.svelte.d.ts +9 -0
  4. package/dist/components/ui/alert/alert-title.svelte +23 -0
  5. package/dist/components/ui/alert/alert-title.svelte.d.ts +9 -0
  6. package/dist/components/ui/alert/alert.svelte +46 -0
  7. package/dist/components/ui/alert/alert.svelte.d.ts +28 -0
  8. package/dist/components/ui/alert/index.d.ts +4 -0
  9. package/dist/components/ui/alert/index.js +4 -0
  10. package/dist/components/ui/alert-dialog/alert-dialog-action.svelte +27 -0
  11. package/dist/components/ui/alert-dialog/alert-dialog-action.svelte.d.ts +9 -0
  12. package/dist/components/ui/alert-dialog/alert-dialog-cancel.svelte +27 -0
  13. package/dist/components/ui/alert-dialog/alert-dialog-cancel.svelte.d.ts +9 -0
  14. package/dist/components/ui/alert-dialog/alert-dialog-content.svelte +39 -0
  15. package/dist/components/ui/alert-dialog/alert-dialog-content.svelte.d.ts +11 -0
  16. package/dist/components/ui/alert-dialog/alert-dialog-description.svelte +20 -0
  17. package/dist/components/ui/alert-dialog/alert-dialog-description.svelte.d.ts +4 -0
  18. package/dist/components/ui/alert-dialog/alert-dialog-footer.svelte +23 -0
  19. package/dist/components/ui/alert-dialog/alert-dialog-footer.svelte.d.ts +5 -0
  20. package/dist/components/ui/alert-dialog/alert-dialog-header.svelte +23 -0
  21. package/dist/components/ui/alert-dialog/alert-dialog-header.svelte.d.ts +5 -0
  22. package/dist/components/ui/alert-dialog/alert-dialog-media.svelte +23 -0
  23. package/dist/components/ui/alert-dialog/alert-dialog-media.svelte.d.ts +5 -0
  24. package/dist/components/ui/alert-dialog/alert-dialog-overlay.svelte +20 -0
  25. package/dist/components/ui/alert-dialog/alert-dialog-overlay.svelte.d.ts +4 -0
  26. package/dist/components/ui/alert-dialog/alert-dialog-portal.svelte +7 -0
  27. package/dist/components/ui/alert-dialog/alert-dialog-portal.svelte.d.ts +3 -0
  28. package/dist/components/ui/alert-dialog/alert-dialog-title.svelte +20 -0
  29. package/dist/components/ui/alert-dialog/alert-dialog-title.svelte.d.ts +4 -0
  30. package/dist/components/ui/alert-dialog/alert-dialog-trigger.svelte +7 -0
  31. package/dist/components/ui/alert-dialog/alert-dialog-trigger.svelte.d.ts +4 -0
  32. package/dist/components/ui/alert-dialog/alert-dialog.svelte +7 -0
  33. package/dist/components/ui/alert-dialog/alert-dialog.svelte.d.ts +3 -0
  34. package/dist/components/ui/alert-dialog/index.d.ts +13 -0
  35. package/dist/components/ui/alert-dialog/index.js +15 -0
  36. package/dist/components/ui/app-dialog/app-dialog.svelte +111 -0
  37. package/dist/components/ui/app-dialog/app-dialog.svelte.d.ts +41 -0
  38. package/dist/components/ui/app-dialog/index.d.ts +2 -0
  39. package/dist/components/ui/app-dialog/index.js +2 -0
  40. package/dist/components/ui/app-shell/app-nav.svelte +299 -0
  41. package/dist/components/ui/app-shell/app-nav.svelte.d.ts +22 -0
  42. package/dist/components/ui/app-shell/app-shell.svelte +675 -0
  43. package/dist/components/ui/app-shell/app-shell.svelte.d.ts +157 -0
  44. package/dist/components/ui/app-shell/index.d.ts +6 -0
  45. package/dist/components/ui/app-shell/index.js +6 -0
  46. package/dist/components/ui/app-shell/measure.d.ts +28 -0
  47. package/dist/components/ui/app-shell/measure.js +21 -0
  48. package/dist/components/ui/app-shell/texture.d.ts +32 -0
  49. package/dist/components/ui/app-shell/texture.js +27 -0
  50. package/dist/components/ui/app-shell/types.d.ts +134 -0
  51. package/dist/components/ui/app-shell/types.js +88 -0
  52. package/dist/components/ui/arc-gauge/arc-gauge.svelte +112 -0
  53. package/dist/components/ui/arc-gauge/arc-gauge.svelte.d.ts +15 -0
  54. package/dist/components/ui/arc-gauge/index.d.ts +3 -0
  55. package/dist/components/ui/arc-gauge/index.js +2 -0
  56. package/dist/components/ui/avatar/avatar-fallback.svelte +26 -0
  57. package/dist/components/ui/avatar/avatar-fallback.svelte.d.ts +4 -0
  58. package/dist/components/ui/avatar/avatar-image.svelte +17 -0
  59. package/dist/components/ui/avatar/avatar-image.svelte.d.ts +4 -0
  60. package/dist/components/ui/avatar/avatar.svelte +31 -0
  61. package/dist/components/ui/avatar/avatar.svelte.d.ts +4 -0
  62. package/dist/components/ui/avatar/index.d.ts +4 -0
  63. package/dist/components/ui/avatar/index.js +6 -0
  64. package/dist/components/ui/badge/badge.svelte +50 -0
  65. package/dist/components/ui/badge/badge.svelte.d.ts +38 -0
  66. package/dist/components/ui/badge/index.d.ts +2 -0
  67. package/dist/components/ui/badge/index.js +2 -0
  68. package/dist/components/ui/bar-row/bar-row.svelte +42 -0
  69. package/dist/components/ui/bar-row/bar-row.svelte.d.ts +12 -0
  70. package/dist/components/ui/bar-row/index.d.ts +2 -0
  71. package/dist/components/ui/bar-row/index.js +2 -0
  72. package/dist/components/ui/button/button.svelte +89 -0
  73. package/dist/components/ui/button/button.svelte.d.ts +70 -0
  74. package/dist/components/ui/button/index.d.ts +2 -0
  75. package/dist/components/ui/button/index.js +4 -0
  76. package/dist/components/ui/card/card-action.svelte +23 -0
  77. package/dist/components/ui/card/card-action.svelte.d.ts +5 -0
  78. package/dist/components/ui/card/card-content.svelte +20 -0
  79. package/dist/components/ui/card/card-content.svelte.d.ts +5 -0
  80. package/dist/components/ui/card/card-description.svelte +20 -0
  81. package/dist/components/ui/card/card-description.svelte.d.ts +5 -0
  82. package/dist/components/ui/card/card-footer.svelte +23 -0
  83. package/dist/components/ui/card/card-footer.svelte.d.ts +5 -0
  84. package/dist/components/ui/card/card-header.svelte +23 -0
  85. package/dist/components/ui/card/card-header.svelte.d.ts +5 -0
  86. package/dist/components/ui/card/card-title.svelte +52 -0
  87. package/dist/components/ui/card/card-title.svelte.d.ts +8 -0
  88. package/dist/components/ui/card/card.svelte +25 -0
  89. package/dist/components/ui/card/card.svelte.d.ts +8 -0
  90. package/dist/components/ui/card/index.d.ts +8 -0
  91. package/dist/components/ui/card/index.js +10 -0
  92. package/dist/components/ui/checkbox/checkbox.svelte +36 -0
  93. package/dist/components/ui/checkbox/checkbox.svelte.d.ts +8 -0
  94. package/dist/components/ui/checkbox/index.d.ts +2 -0
  95. package/dist/components/ui/checkbox/index.js +2 -0
  96. package/dist/components/ui/command/command-dialog.svelte +42 -0
  97. package/dist/components/ui/command/command-dialog.svelte.d.ts +14 -0
  98. package/dist/components/ui/command/command-empty.svelte +17 -0
  99. package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
  100. package/dist/components/ui/command/command-group.svelte +33 -0
  101. package/dist/components/ui/command/command-group.svelte.d.ts +7 -0
  102. package/dist/components/ui/command/command-input.svelte +36 -0
  103. package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
  104. package/dist/components/ui/command/command-item.svelte +27 -0
  105. package/dist/components/ui/command/command-item.svelte.d.ts +4 -0
  106. package/dist/components/ui/command/command-link-item.svelte +20 -0
  107. package/dist/components/ui/command/command-link-item.svelte.d.ts +4 -0
  108. package/dist/components/ui/command/command-list.svelte +20 -0
  109. package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
  110. package/dist/components/ui/command/command-loading.svelte +7 -0
  111. package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
  112. package/dist/components/ui/command/command-separator.svelte +17 -0
  113. package/dist/components/ui/command/command-separator.svelte.d.ts +4 -0
  114. package/dist/components/ui/command/command-shortcut.svelte +23 -0
  115. package/dist/components/ui/command/command-shortcut.svelte.d.ts +5 -0
  116. package/dist/components/ui/command/command.svelte +28 -0
  117. package/dist/components/ui/command/command.svelte.d.ts +8 -0
  118. package/dist/components/ui/command/index.d.ts +12 -0
  119. package/dist/components/ui/command/index.js +14 -0
  120. package/dist/components/ui/command-palette/command-palette.svelte +94 -0
  121. package/dist/components/ui/command-palette/command-palette.svelte.d.ts +40 -0
  122. package/dist/components/ui/command-palette/index.d.ts +3 -0
  123. package/dist/components/ui/command-palette/index.js +2 -0
  124. package/dist/components/ui/context-column/context-column.svelte +56 -0
  125. package/dist/components/ui/context-column/context-column.svelte.d.ts +14 -0
  126. package/dist/components/ui/context-column/index.d.ts +2 -0
  127. package/dist/components/ui/context-column/index.js +2 -0
  128. package/dist/components/ui/data-table/data-table.svelte.d.ts +40 -0
  129. package/dist/components/ui/data-table/data-table.svelte.js +110 -0
  130. package/dist/components/ui/data-table/flex-render.svelte +40 -0
  131. package/dist/components/ui/data-table/flex-render.svelte.d.ts +33 -0
  132. package/dist/components/ui/data-table/index.d.ts +3 -0
  133. package/dist/components/ui/data-table/index.js +3 -0
  134. package/dist/components/ui/data-table/render-helpers.d.ts +90 -0
  135. package/dist/components/ui/data-table/render-helpers.js +99 -0
  136. package/dist/components/ui/data-table-tanstack/data-table-tanstack.svelte +247 -0
  137. package/dist/components/ui/data-table-tanstack/data-table-tanstack.svelte.d.ts +79 -0
  138. package/dist/components/ui/data-table-tanstack/index.d.ts +3 -0
  139. package/dist/components/ui/data-table-tanstack/index.js +2 -0
  140. package/dist/components/ui/data-table-toolbar/data-table-toolbar.svelte +102 -0
  141. package/dist/components/ui/data-table-toolbar/data-table-toolbar.svelte.d.ts +32 -0
  142. package/dist/components/ui/data-table-toolbar/index.d.ts +3 -0
  143. package/dist/components/ui/data-table-toolbar/index.js +2 -0
  144. package/dist/components/ui/detail-panel/detail-panel.svelte +95 -0
  145. package/dist/components/ui/detail-panel/detail-panel.svelte.d.ts +26 -0
  146. package/dist/components/ui/detail-panel/index.d.ts +2 -0
  147. package/dist/components/ui/detail-panel/index.js +2 -0
  148. package/dist/components/ui/dialog/dialog-close.svelte +11 -0
  149. package/dist/components/ui/dialog/dialog-close.svelte.d.ts +4 -0
  150. package/dist/components/ui/dialog/dialog-content.svelte +48 -0
  151. package/dist/components/ui/dialog/dialog-content.svelte.d.ts +13 -0
  152. package/dist/components/ui/dialog/dialog-description.svelte +20 -0
  153. package/dist/components/ui/dialog/dialog-description.svelte.d.ts +4 -0
  154. package/dist/components/ui/dialog/dialog-footer.svelte +35 -0
  155. package/dist/components/ui/dialog/dialog-footer.svelte.d.ts +8 -0
  156. package/dist/components/ui/dialog/dialog-header.svelte +20 -0
  157. package/dist/components/ui/dialog/dialog-header.svelte.d.ts +5 -0
  158. package/dist/components/ui/dialog/dialog-overlay.svelte +20 -0
  159. package/dist/components/ui/dialog/dialog-overlay.svelte.d.ts +4 -0
  160. package/dist/components/ui/dialog/dialog-portal.svelte +7 -0
  161. package/dist/components/ui/dialog/dialog-portal.svelte.d.ts +3 -0
  162. package/dist/components/ui/dialog/dialog-title.svelte +17 -0
  163. package/dist/components/ui/dialog/dialog-title.svelte.d.ts +4 -0
  164. package/dist/components/ui/dialog/dialog-trigger.svelte +11 -0
  165. package/dist/components/ui/dialog/dialog-trigger.svelte.d.ts +4 -0
  166. package/dist/components/ui/dialog/dialog.svelte +7 -0
  167. package/dist/components/ui/dialog/dialog.svelte.d.ts +3 -0
  168. package/dist/components/ui/dialog/index.d.ts +11 -0
  169. package/dist/components/ui/dialog/index.js +13 -0
  170. package/dist/components/ui/dialog-section/dialog-section.svelte +20 -0
  171. package/dist/components/ui/dialog-section/dialog-section.svelte.d.ts +8 -0
  172. package/dist/components/ui/dialog-section/index.d.ts +2 -0
  173. package/dist/components/ui/dialog-section/index.js +2 -0
  174. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  175. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +4 -0
  176. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +44 -0
  177. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +9 -0
  178. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +31 -0
  179. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte.d.ts +10 -0
  180. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  181. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte.d.ts +8 -0
  182. package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
  183. package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte.d.ts +4 -0
  184. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
  185. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte.d.ts +8 -0
  186. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +27 -0
  187. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte.d.ts +8 -0
  188. package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  189. package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte.d.ts +3 -0
  190. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  191. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +4 -0
  192. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
  193. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte.d.ts +4 -0
  194. package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  195. package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte.d.ts +4 -0
  196. package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
  197. package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte.d.ts +5 -0
  198. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
  199. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte.d.ts +4 -0
  200. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  201. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte.d.ts +7 -0
  202. package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  203. package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte.d.ts +3 -0
  204. package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  205. package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte.d.ts +4 -0
  206. package/dist/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
  207. package/dist/components/ui/dropdown-menu/dropdown-menu.svelte.d.ts +3 -0
  208. package/dist/components/ui/dropdown-menu/index.d.ts +18 -0
  209. package/dist/components/ui/dropdown-menu/index.js +18 -0
  210. package/dist/components/ui/empty-state/empty-state.svelte +44 -0
  211. package/dist/components/ui/empty-state/empty-state.svelte.d.ts +14 -0
  212. package/dist/components/ui/empty-state/index.d.ts +2 -0
  213. package/dist/components/ui/empty-state/index.js +2 -0
  214. package/dist/components/ui/error-state/error-state.svelte +52 -0
  215. package/dist/components/ui/error-state/error-state.svelte.d.ts +10 -0
  216. package/dist/components/ui/error-state/index.d.ts +2 -0
  217. package/dist/components/ui/error-state/index.js +2 -0
  218. package/dist/components/ui/info-tip/index.d.ts +2 -0
  219. package/dist/components/ui/info-tip/index.js +2 -0
  220. package/dist/components/ui/info-tip/info-tip.svelte +37 -0
  221. package/dist/components/ui/info-tip/info-tip.svelte.d.ts +10 -0
  222. package/dist/components/ui/input/index.d.ts +2 -0
  223. package/dist/components/ui/input/index.js +4 -0
  224. package/dist/components/ui/input/input.svelte +48 -0
  225. package/dist/components/ui/input/input.svelte.d.ts +13 -0
  226. package/dist/components/ui/input-group/index.d.ts +7 -0
  227. package/dist/components/ui/input-group/index.js +9 -0
  228. package/dist/components/ui/input-group/input-group-addon.svelte +53 -0
  229. package/dist/components/ui/input-group/input-group-addon.svelte.d.ts +32 -0
  230. package/dist/components/ui/input-group/input-group-button.svelte +49 -0
  231. package/dist/components/ui/input-group/input-group-button.svelte.d.ts +32 -0
  232. package/dist/components/ui/input-group/input-group-input.svelte +23 -0
  233. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +11 -0
  234. package/dist/components/ui/input-group/input-group-text.svelte +22 -0
  235. package/dist/components/ui/input-group/input-group-text.svelte.d.ts +5 -0
  236. package/dist/components/ui/input-group/input-group-textarea.svelte +23 -0
  237. package/dist/components/ui/input-group/input-group-textarea.svelte.d.ts +3 -0
  238. package/dist/components/ui/input-group/input-group.svelte +24 -0
  239. package/dist/components/ui/input-group/input-group.svelte.d.ts +5 -0
  240. package/dist/components/ui/label/index.d.ts +2 -0
  241. package/dist/components/ui/label/index.js +4 -0
  242. package/dist/components/ui/label/label.svelte +20 -0
  243. package/dist/components/ui/label/label.svelte.d.ts +4 -0
  244. package/dist/components/ui/loading-state/index.d.ts +2 -0
  245. package/dist/components/ui/loading-state/index.js +2 -0
  246. package/dist/components/ui/loading-state/loading-state.svelte +28 -0
  247. package/dist/components/ui/loading-state/loading-state.svelte.d.ts +8 -0
  248. package/dist/components/ui/page-header/index.d.ts +2 -0
  249. package/dist/components/ui/page-header/index.js +2 -0
  250. package/dist/components/ui/page-header/page-header.svelte +100 -0
  251. package/dist/components/ui/page-header/page-header.svelte.d.ts +17 -0
  252. package/dist/components/ui/panel/index.d.ts +2 -0
  253. package/dist/components/ui/panel/index.js +2 -0
  254. package/dist/components/ui/panel/panel.svelte +54 -0
  255. package/dist/components/ui/panel/panel.svelte.d.ts +16 -0
  256. package/dist/components/ui/password-input/index.d.ts +2 -0
  257. package/dist/components/ui/password-input/index.js +4 -0
  258. package/dist/components/ui/password-input/password-input.svelte +48 -0
  259. package/dist/components/ui/password-input/password-input.svelte.d.ts +8 -0
  260. package/dist/components/ui/popover/index.d.ts +4 -0
  261. package/dist/components/ui/popover/index.js +6 -0
  262. package/dist/components/ui/popover/popover-content.svelte +45 -0
  263. package/dist/components/ui/popover/popover-content.svelte.d.ts +7 -0
  264. package/dist/components/ui/popover/popover-trigger.svelte +7 -0
  265. package/dist/components/ui/popover/popover-trigger.svelte.d.ts +4 -0
  266. package/dist/components/ui/popover/popover.svelte +7 -0
  267. package/dist/components/ui/popover/popover.svelte.d.ts +3 -0
  268. package/dist/components/ui/progress/index.d.ts +2 -0
  269. package/dist/components/ui/progress/index.js +4 -0
  270. package/dist/components/ui/progress/progress.svelte +30 -0
  271. package/dist/components/ui/progress/progress.svelte.d.ts +4 -0
  272. package/dist/components/ui/scorecard/index.d.ts +2 -0
  273. package/dist/components/ui/scorecard/index.js +2 -0
  274. package/dist/components/ui/scorecard/scorecard.svelte +35 -0
  275. package/dist/components/ui/scorecard/scorecard.svelte.d.ts +8 -0
  276. package/dist/components/ui/select/index.d.ts +12 -0
  277. package/dist/components/ui/select/index.js +14 -0
  278. package/dist/components/ui/select/select-content.svelte +45 -0
  279. package/dist/components/ui/select/select-content.svelte.d.ts +11 -0
  280. package/dist/components/ui/select/select-group-heading.svelte +21 -0
  281. package/dist/components/ui/select/select-group-heading.svelte.d.ts +10 -0
  282. package/dist/components/ui/select/select-group.svelte +17 -0
  283. package/dist/components/ui/select/select-group.svelte.d.ts +4 -0
  284. package/dist/components/ui/select/select-item.svelte +38 -0
  285. package/dist/components/ui/select/select-item.svelte.d.ts +4 -0
  286. package/dist/components/ui/select/select-label.svelte +20 -0
  287. package/dist/components/ui/select/select-label.svelte.d.ts +6 -0
  288. package/dist/components/ui/select/select-portal.svelte +7 -0
  289. package/dist/components/ui/select/select-portal.svelte.d.ts +3 -0
  290. package/dist/components/ui/select/select-scroll-down-button.svelte +23 -0
  291. package/dist/components/ui/select/select-scroll-down-button.svelte.d.ts +4 -0
  292. package/dist/components/ui/select/select-scroll-up-button.svelte +23 -0
  293. package/dist/components/ui/select/select-scroll-up-button.svelte.d.ts +4 -0
  294. package/dist/components/ui/select/select-separator.svelte +18 -0
  295. package/dist/components/ui/select/select-separator.svelte.d.ts +4 -0
  296. package/dist/components/ui/select/select-trigger.svelte +29 -0
  297. package/dist/components/ui/select/select-trigger.svelte.d.ts +8 -0
  298. package/dist/components/ui/select/select.svelte +11 -0
  299. package/dist/components/ui/select/select.svelte.d.ts +3 -0
  300. package/dist/components/ui/separator/index.d.ts +2 -0
  301. package/dist/components/ui/separator/index.js +4 -0
  302. package/dist/components/ui/separator/separator.svelte +23 -0
  303. package/dist/components/ui/separator/separator.svelte.d.ts +4 -0
  304. package/dist/components/ui/skeleton/index.d.ts +2 -0
  305. package/dist/components/ui/skeleton/index.js +4 -0
  306. package/dist/components/ui/skeleton/skeleton.svelte +17 -0
  307. package/dist/components/ui/skeleton/skeleton.svelte.d.ts +5 -0
  308. package/dist/components/ui/sonner/index.d.ts +1 -0
  309. package/dist/components/ui/sonner/index.js +1 -0
  310. package/dist/components/ui/sonner/sonner.svelte +34 -0
  311. package/dist/components/ui/sonner/sonner.svelte.d.ts +4 -0
  312. package/dist/components/ui/sparkline/index.d.ts +3 -0
  313. package/dist/components/ui/sparkline/index.js +2 -0
  314. package/dist/components/ui/sparkline/sparkline.svelte +90 -0
  315. package/dist/components/ui/sparkline/sparkline.svelte.d.ts +16 -0
  316. package/dist/components/ui/stat-card/index.d.ts +2 -0
  317. package/dist/components/ui/stat-card/index.js +2 -0
  318. package/dist/components/ui/stat-card/stat-card.svelte +77 -0
  319. package/dist/components/ui/stat-card/stat-card.svelte.d.ts +34 -0
  320. package/dist/components/ui/stat-list/index.d.ts +3 -0
  321. package/dist/components/ui/stat-list/index.js +2 -0
  322. package/dist/components/ui/stat-list/stat-list.svelte +80 -0
  323. package/dist/components/ui/stat-list/stat-list.svelte.d.ts +23 -0
  324. package/dist/components/ui/status/index.d.ts +1 -0
  325. package/dist/components/ui/status/index.js +1 -0
  326. package/dist/components/ui/status-badge/index.d.ts +3 -0
  327. package/dist/components/ui/status-badge/index.js +2 -0
  328. package/dist/components/ui/status-badge/status-badge.svelte +53 -0
  329. package/dist/components/ui/status-badge/status-badge.svelte.d.ts +39 -0
  330. package/dist/components/ui/switch/index.d.ts +2 -0
  331. package/dist/components/ui/switch/index.js +4 -0
  332. package/dist/components/ui/switch/switch.svelte +39 -0
  333. package/dist/components/ui/switch/switch.svelte.d.ts +4 -0
  334. package/dist/components/ui/table/index.d.ts +10 -0
  335. package/dist/components/ui/table/index.js +12 -0
  336. package/dist/components/ui/table/table-body.svelte +20 -0
  337. package/dist/components/ui/table/table-body.svelte.d.ts +5 -0
  338. package/dist/components/ui/table/table-caption.svelte +20 -0
  339. package/dist/components/ui/table/table-caption.svelte.d.ts +5 -0
  340. package/dist/components/ui/table/table-cell.svelte +20 -0
  341. package/dist/components/ui/table/table-cell.svelte.d.ts +5 -0
  342. package/dist/components/ui/table/table-classes.d.ts +15 -0
  343. package/dist/components/ui/table/table-classes.js +15 -0
  344. package/dist/components/ui/table/table-footer.svelte +20 -0
  345. package/dist/components/ui/table/table-footer.svelte.d.ts +5 -0
  346. package/dist/components/ui/table/table-head.svelte +23 -0
  347. package/dist/components/ui/table/table-head.svelte.d.ts +5 -0
  348. package/dist/components/ui/table/table-header.svelte +20 -0
  349. package/dist/components/ui/table/table-header.svelte.d.ts +5 -0
  350. package/dist/components/ui/table/table-row.svelte +23 -0
  351. package/dist/components/ui/table/table-row.svelte.d.ts +5 -0
  352. package/dist/components/ui/table/table.svelte +37 -0
  353. package/dist/components/ui/table/table.svelte.d.ts +20 -0
  354. package/dist/components/ui/tabs/index.d.ts +5 -0
  355. package/dist/components/ui/tabs/index.js +7 -0
  356. package/dist/components/ui/tabs/tabs-content.svelte +13 -0
  357. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
  358. package/dist/components/ui/tabs/tabs-list.svelte +16 -0
  359. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
  360. package/dist/components/ui/tabs/tabs-trigger.svelte +16 -0
  361. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
  362. package/dist/components/ui/tabs/tabs.svelte +19 -0
  363. package/dist/components/ui/tabs/tabs.svelte.d.ts +4 -0
  364. package/dist/components/ui/textarea/index.d.ts +2 -0
  365. package/dist/components/ui/textarea/index.js +4 -0
  366. package/dist/components/ui/textarea/textarea.svelte +22 -0
  367. package/dist/components/ui/textarea/textarea.svelte.d.ts +5 -0
  368. package/dist/components/ui/tile-grid/index.d.ts +2 -0
  369. package/dist/components/ui/tile-grid/index.js +2 -0
  370. package/dist/components/ui/tile-grid/tile-grid.svelte +66 -0
  371. package/dist/components/ui/tile-grid/tile-grid.svelte.d.ts +43 -0
  372. package/dist/components/ui/tooltip/index.d.ts +6 -0
  373. package/dist/components/ui/tooltip/index.js +8 -0
  374. package/dist/components/ui/tooltip/tooltip-content.svelte +52 -0
  375. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +11 -0
  376. package/dist/components/ui/tooltip/tooltip-portal.svelte +7 -0
  377. package/dist/components/ui/tooltip/tooltip-portal.svelte.d.ts +4 -0
  378. package/dist/components/ui/tooltip/tooltip-provider.svelte +7 -0
  379. package/dist/components/ui/tooltip/tooltip-provider.svelte.d.ts +4 -0
  380. package/dist/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  381. package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +4 -0
  382. package/dist/components/ui/tooltip/tooltip.svelte +7 -0
  383. package/dist/components/ui/tooltip/tooltip.svelte.d.ts +4 -0
  384. package/dist/styles.css +961 -0
  385. package/dist/utils.d.ts +12 -0
  386. package/dist/utils.js +5 -0
  387. package/package.json +81 -0
@@ -0,0 +1,961 @@
1
+ /**
2
+ * @poodle64/ui — component stylesheet.
3
+ *
4
+ * The composed components (PageHeader, StatusBadge, Panel, …) need three things
5
+ * the shadcn variable layer does not supply: a small type/tracking scale, a card
6
+ * "milled edge" treatment, and the status chip/dot classes. Every app that
7
+ * hand-built its own page chrome also hand-built these, which is exactly the
8
+ * drift this package exists to end — so they ship here, with the components that
9
+ * depend on them, rather than being re-typed into each app's own app.css.
10
+ *
11
+ * Import ONCE in the consuming app's app.css, after the token imports:
12
+ *
13
+ * @import '@poodle64/design-tokens/tokens.tw.css';
14
+ * @import '@poodle64/design-tokens/tokens.css';
15
+ * @import '@poodle64/ui/styles.css';
16
+ *
17
+ * Everything here resolves through either a --ds-* token (owned by
18
+ * @poodle64/design-tokens) or a shadcn semantic variable (--border, --primary,
19
+ * --muted-foreground, …) supplied by the app's own alias layer. There is no
20
+ * palette in this file: change the app's palette and every class below follows.
21
+ */
22
+
23
+ /* ─────────────────────────────────────────────────────────────────────────────
24
+ The enter/exit animation utilities.
25
+
26
+ Every overlay this package ships is written against `animate-in`,
27
+ `animate-out`, `fade-in-0`, `zoom-in-95` and `slide-in-from-*`. None of those
28
+ are Tailwind utilities — they come from tw-animate-css — so in an app that
29
+ does not import it they compile to NOTHING, and the transition is dead for a
30
+ second, independent reason on top of the variant question below. Same
31
+ silence: the class is in the DOM, no rule exists, nothing fails.
32
+
33
+ It is a dependency rather than a peer, and imported here rather than
34
+ documented as a setup step, for the same reason the variants are declared
35
+ here: the package writes the utilities, so the package owns what makes them
36
+ real. An app that imports tw-animate-css itself as well loses nothing — the
37
+ definitions are identical.
38
+
39
+ It must stay the FIRST statement in this file: CSS requires @import before
40
+ any other at-rule, and the @custom-variant declarations below are at-rules.
41
+ ───────────────────────────────────────────────────────────────────────────── */
42
+ @import 'tw-animate-css';
43
+
44
+ /* ─────────────────────────────────────────────────────────────────────────────
45
+ The dark variant.
46
+
47
+ Tailwind v4 resolves a bare `dark:` against `prefers-color-scheme`, not
48
+ against a class. Every component in this package — the shell's theme-toggle
49
+ icons, the input surfaces, the destructive ring refinements, ~50 utilities in
50
+ all — is written against the CLASS strategy, so without this line every one
51
+ of them is dead in a consuming app and the app's theme switch moves the
52
+ palette while leaving the components' dark refinements behind. Nothing fails:
53
+ the class is in the DOM, the rule simply never matches.
54
+
55
+ It was unowned until now. All four adopting apps had independently typed the
56
+ identical declaration into their own app.css, which is precisely the drift
57
+ this package exists to end — and it meant the FIFTH app to adopt would get a
58
+ silently half-dark component set. It ships here, with the utilities that
59
+ depend on it. The selector is byte-identical to what those apps already
60
+ declare, so an app that keeps its own copy re-declares the same thing and
61
+ nothing changes; an app that deletes its copy loses nothing.
62
+ ───────────────────────────────────────────────────────────────────────────── */
63
+ @custom-variant dark (&:is(.dark *));
64
+
65
+ /* ─────────────────────────────────────────────────────────────────────────────
66
+ The bits-ui open/closed variants.
67
+
68
+ Same defect as `dark` above, one attribute along. This package writes ~47
69
+ `data-open:` / `data-closed:` utilities across the dialogue, alert-dialogue,
70
+ dropdown-menu, popover, tooltip, select and command surfaces — the whole
71
+ enter/exit animation of every overlay it ships. Tailwind v4 compiles a bare
72
+ `data-open:` to `&[data-open]`, and bits-ui does not emit `data-open`: it
73
+ emits `data-state="open"` / `data-state="closed"`. So the utilities exist in
74
+ the stylesheet, match nothing, and every overlay opens and closes with no
75
+ transition. Nothing fails — the class is in the DOM, the rule simply never
76
+ matches — which is why four of the five adopting apps shipped with dead
77
+ overlay transitions and none of them noticed.
78
+
79
+ Only these two need declaring. The other five shorthand data-variants this
80
+ package writes — `data-selected`, `data-highlighted`, `data-disabled`,
81
+ `data-placeholder` (bits-ui, all emitted as empty-string-or-undefined) and
82
+ `data-inset` (set by this package's own menu items) — are BARE attributes, so
83
+ Tailwind's default `&[data-x]` already matches them and a declaration here
84
+ would only restate it. `src/test/data-state-variants.test.ts` is what keeps
85
+ that distinction honest: it enumerates every shorthand data-variant the built
86
+ package ships and fails on any one whose compiled selector is not the
87
+ attribute the DOM actually carries, so a new unowned variant cannot ship.
88
+ ───────────────────────────────────────────────────────────────────────────── */
89
+ @custom-variant data-open (&[data-state='open']);
90
+ @custom-variant data-closed (&[data-state='closed']);
91
+
92
+ /* ─────────────────────────────────────────────────────────────────────────────
93
+ The shadcn semantic surface.
94
+
95
+ Every component in this package is written against the shadcn colour names
96
+ (bg-card, bg-popover, bg-muted, bg-accent, border-input, bg-secondary and
97
+ their -foreground pairs). Declaring those names as plain custom properties
98
+ makes the VARIABLE exist but never tells Tailwind they are theme colours, so
99
+ the utilities compile to nothing at all: no build error, no lint hit, no
100
+ failing test — just a class in the DOM with no CSS behind it. Registration
101
+ is therefore two steps, and both are needed.
102
+
103
+ Step one, below: the mapping itself, declared once. Each shadcn name aliases
104
+ a --ds-* token from @poodle64/design-tokens, so a consuming app that picks a
105
+ palette by overriding --ds-color-* moves this whole surface with it, and
106
+ .dark flows through untouched.
107
+
108
+ The values are not a fresh design: they are what every app in the estate had
109
+ already converged on independently, hoisted here verbatim. There was no
110
+ disagreement to arbitrate — the surface ladder mapping (card/accent on
111
+ surface-2, popover on surface-3, muted/secondary on surface-1, input on
112
+ border) is identical in every app that had one.
113
+
114
+ The override point is --ds-color-*, and it reaches everything below. The
115
+ bare names are the contract components and app CSS read directly (the chip
116
+ colour, the selected-row rule, the card edge); redeclaring one after this
117
+ import is a deliberate per-app deviation rather than the setup tax it is
118
+ today.
119
+
120
+ card/card-foreground/popover/popover-foreground/secondary/
121
+ secondary-foreground/muted/accent/accent-foreground/input carry NO default
122
+ here (design-system#8). A bare name declared unconditionally at :root
123
+ freezes there: its own var(--ds-color-*) reference resolves once, at
124
+ :root, and every descendant inherits that fixed result regardless of a
125
+ later --ds-color-* override or a scoped .dark wrapper below it. These ten
126
+ names are consumed ONLY through the @theme inline fallback chain below
127
+ (var(--card, var(--ds-color-surface-2)), …), so leaving them undeclared by
128
+ default means that fallback — not a frozen alias — is what every consumer
129
+ resolves against, and both a scoped --ds-color-* override and a scoped
130
+ .dark wrapper reach it. An app that still sets one of these by name (the
131
+ existing, undocumented but working escape hatch) is unaffected: var()'s
132
+ fallback triggers only when the name is undeclared anywhere in the chain.
133
+
134
+ background/foreground/primary/primary-foreground/destructive/
135
+ destructive-foreground/border/border-strong/ring/muted-foreground/radius
136
+ keep their default here because this package's OWN hand-written CSS below
137
+ (.ds-nav-indicator, .ds-card-edge, .ds-dialog-section, .ds-skip-link:focus,
138
+ .ds-ink-neutral, .ds-chip-neutral, …) reads them as
139
+ plain, unconditional var() — not through a fallback — and because the
140
+ Tailwind utilities for these are generated by @poodle64/design-tokens'
141
+ OWN @theme inline registration off --ds-color-* directly, never through
142
+ these bare names at all (they were never in that utility's chain to begin
143
+ with, so removing their default would only cost the internal CSS above,
144
+ for no scoping gain). */
145
+ :root {
146
+ --background: var(--ds-color-background);
147
+ --foreground: var(--ds-color-foreground);
148
+ --primary: var(--ds-color-primary);
149
+ --primary-foreground: var(--ds-color-primary-foreground);
150
+ --destructive: var(--ds-color-destructive);
151
+ --destructive-foreground: var(--ds-color-destructive-foreground);
152
+ --border: var(--ds-color-border);
153
+ --border-strong: var(--ds-color-border-strong);
154
+ --ring: var(--ds-color-ring);
155
+ --muted-foreground: var(--ds-color-muted-foreground);
156
+
157
+ /* Consumed by the input-group size ramp as calc(var(--radius) - n). The
158
+ button ramp uses --radius-md from @poodle64/design-tokens instead. */
159
+ --radius: var(--ds-radius-lg);
160
+ }
161
+
162
+ /* ─────────────────────────────────────────────────────────────────────────────
163
+ Step two: register those names with Tailwind so the utilities generate.
164
+
165
+ `inline` is what makes the override story work — Tailwind substitutes the
166
+ value into the utility, so .bg-card lands on var(--card) and follows any
167
+ redefinition of --card, at any scope, including .dark. Without it the
168
+ utility would freeze against whatever --card meant in :root.
169
+
170
+ Only the names @poodle64/design-tokens does NOT already register appear
171
+ here, and that exclusivity is load-bearing rather than tidiness. Registering
172
+ a key both packages claim makes the winner depend on which stylesheet was
173
+ imported last, so an app overriding it would get different results from the
174
+ same CSS depending on its import order, silently. One owner per key removes
175
+ the question; `background`, `foreground`, `primary`, `border`, `ring`,
176
+ `destructive` and their pairs stay the token package's, which is why they
177
+ are absent below despite being part of the same surface.
178
+
179
+ Sidebar and chart colours are deliberately NOT registered here either. This
180
+ package ships no sidebar or chart component, so nothing it exports could
181
+ reference them, and they are the one part of the shadcn surface the estate
182
+ genuinely disagrees on (chrome hue is an app's own decision, not a shared
183
+ default). An app using them registers them itself, as it does today.
184
+
185
+ Each value is a fallback chain, `var(--bare-name, var(--ds-color-x))`, not
186
+ a single reference (design-system#8). --card etc. carry no default (see
187
+ the :root block above), so by default every one of these resolves through
188
+ the --ds-color-* fallback — evaluated live at the consuming element, so a
189
+ scoped subtree override or a scoped .dark wrapper both reach it. An app
190
+ that still sets --card (etc.) by name keeps working unchanged: var()'s
191
+ fallback is used only when the name is undeclared anywhere in the chain.
192
+ ───────────────────────────────────────────────────────────────────────────── */
193
+ @theme inline {
194
+ --color-card: var(--card, var(--ds-color-surface-2));
195
+ --color-card-foreground: var(--card-foreground, var(--ds-color-foreground));
196
+ --color-popover: var(--popover, var(--ds-color-surface-3));
197
+ --color-popover-foreground: var(--popover-foreground, var(--ds-color-foreground));
198
+ --color-secondary: var(--secondary, var(--ds-color-surface-1));
199
+ --color-secondary-foreground: var(--secondary-foreground, var(--ds-color-foreground));
200
+ --color-muted: var(--muted, var(--ds-color-surface-1));
201
+ --color-accent: var(--accent, var(--ds-color-surface-2));
202
+ --color-accent-foreground: var(--accent-foreground, var(--ds-color-foreground));
203
+ --color-input: var(--input, var(--ds-color-border));
204
+
205
+ /* The shell's chrome surface — the nav rail and the top bar.
206
+ Deliberately NOT `sidebar`. `20-sveltekit-frontend.md` makes the
207
+ `sidebar-*` set the app's own to define, and this package registers
208
+ neither `sidebar-*` nor `chart-*`; painting the shell off a `sidebar`
209
+ colour utility would have meant claiming a key the estate has not agreed
210
+ on and that no app can then override without a stylesheet-order coin
211
+ toss. (Naming the class literally here would itself trip the dead-utility
212
+ gate, which reads every string this package ships.) `shell` is a
213
+ name neither package claimed, so the one-owner-per-key rule holds, and it
214
+ defaults to a token this package already resolves. An app that already
215
+ has a chrome hue points its own token at it in one line:
216
+
217
+ :root { --ds-shell-chrome: var(--sidebar); }
218
+
219
+ The two foreground keys are the chrome's INK, and they are consumed —
220
+ by the rail, the bar, every chrome control and every nav rule below.
221
+ `--color-shell-foreground` was registered and read by nothing until now,
222
+ which made it a trap rather than an affordance: an app pointing
223
+ `--ds-shell-chrome-foreground` at a contrasting value got no effect and
224
+ had to override a package style to get chrome ink that inverts against
225
+ the palette. Both default to the same tokens the chrome used to hard-code
226
+ (`--foreground` / `--muted-foreground`), so nothing moves until an app
227
+ asks it to; two declarations then invert the whole chrome:
228
+
229
+ :root {
230
+ --ds-shell-chrome: var(--ds-color-primary);
231
+ --ds-shell-chrome-foreground: var(--ds-color-primary-foreground);
232
+ --ds-shell-chrome-muted-foreground: color-mix(
233
+ in oklch, var(--ds-color-primary-foreground) 70%, transparent);
234
+ }
235
+ */
236
+ --color-shell: var(--ds-shell-chrome);
237
+ --color-shell-foreground: var(--ds-shell-chrome-foreground);
238
+ --color-shell-muted-foreground: var(--ds-shell-chrome-muted-foreground);
239
+ }
240
+
241
+ /* ─────────────────────────────────────────────────────────────────────────────
242
+ Theme registration — the scale keys the composed components reference as
243
+ Tailwind utilities (text-display, text-body, text-stat, tracking-eyebrow).
244
+ An app may override any of these by re-declaring the key after this import.
245
+ ───────────────────────────────────────────────────────────────────────────── */
246
+ @theme {
247
+ /* Eyebrow / column-head tracking — pairs with text-2xs + uppercase. */
248
+ --tracking-eyebrow: 0.06em;
249
+
250
+ /* Page-title and large-readout size (PageHeader, StatCard). */
251
+ --text-display: 1.75rem;
252
+ --text-display--line-height: 2.125rem;
253
+ --text-display--letter-spacing: -0.02em;
254
+
255
+ /* Panel/card heading size — one step above the sm body default. */
256
+ --text-body: 0.9375rem;
257
+
258
+ /* StatList readout value size. */
259
+ --text-stat: 1.0625rem;
260
+ }
261
+
262
+ /* ─────────────────────────────────────────────────────────────────────────────
263
+ Atmosphere variables consumed by .ds-edge. Derived from the app's own
264
+ semantic --foreground so the edge highlight inverts correctly between light
265
+ and dark with no per-mode override. The drop shadow is a neutral translucent
266
+ black in both modes (a shadow is an absence of light, not a palette colour);
267
+ an app wanting a tinted shadow overrides --ds-shadow-sm.
268
+ ───────────────────────────────────────────────────────────────────────────── */
269
+ :root {
270
+ --ds-card-edge: color-mix(in oklch, var(--foreground) 6%, transparent);
271
+ --ds-shadow-sm:
272
+ 0 1px 2px oklch(0 0 0 / 0.1),
273
+ 0 1px 1px oklch(0 0 0 / 0.06);
274
+
275
+ /* ── Shell geometry ──────────────────────────────────────────────────────
276
+ Named custom properties rather than Tailwind theme keys, deliberately:
277
+ a `--spacing-*` or `--container-*` entry with a name like `rail` would
278
+ sit inside a namespace Tailwind resolves the sizing utilities against
279
+ (`22-file-extensions`-adjacent trap documented in
280
+ tailwind-namespace.test.ts). Outside those namespaces there is nothing
281
+ to shadow, and an app still retunes the rail in one declaration.
282
+ ──────────────────────────────────────────────────────────────────────── */
283
+ --ds-shell-chrome: var(--ds-color-surface-1);
284
+ --ds-shell-chrome-foreground: var(--ds-color-foreground);
285
+ --ds-shell-chrome-muted-foreground: var(--ds-color-muted-foreground);
286
+ --ds-shell-rail-width: 15.5rem;
287
+ --ds-shell-rail-width-collapsed: 3.5rem;
288
+
289
+ /* ── Content measure ──────────────────────────────────────────────────────
290
+ The scale AppShell's `measure` prop selects between, as custom properties
291
+ for the same reason the rail width is one: an app retunes a tier in one
292
+ declaration instead of waiting on a release of this package.
293
+
294
+ `prose` is in `ch` deliberately, where the other two are in `rem`. A
295
+ reading measure is a count of CHARACTERS, not a length — the whole claim
296
+ is "a line the eye can track back from", and that is a function of the
297
+ face and size the app actually set, not of a number someone typed once.
298
+ `page` and `wide` are page frames rather than reading measures, so they
299
+ are lengths and stay in rem.
300
+ ──────────────────────────────────────────────────────────────────────── */
301
+ --ds-shell-measure-prose: 72ch;
302
+ --ds-shell-measure-page: 80rem;
303
+ --ds-shell-measure-wide: 120rem;
304
+ }
305
+
306
+ @layer base {
307
+ /**
308
+ * The milled edge. Cards read as having depth from a hairline border plus a
309
+ * 1px inner highlight, not from a heavy drop shadow (shared-design-language
310
+ * §2 and §6). Applied by DetailPanel, AppDialog, Panel, StatCard, StatList,
311
+ * EmptyState, ErrorState and DataTableTanstack.
312
+ */
313
+ .ds-edge {
314
+ box-shadow:
315
+ inset 0 1px 0 0 var(--ds-card-edge),
316
+ var(--ds-shadow-sm);
317
+ }
318
+
319
+ /** Tabular figures, so a column of numbers aligns. */
320
+ .ds-tabular {
321
+ font-variant-numeric: tabular-nums;
322
+ font-feature-settings: 'tnum';
323
+ }
324
+
325
+ /**
326
+ * Dialogue sections: a divider with even spacing is drawn between adjacent
327
+ * DialogSections, so every dialogue separates its information the same way.
328
+ * The first section in a body carries no divider. This adjacent-sibling rule
329
+ * is the whole behaviour of DialogSection — the component is inert without it.
330
+ */
331
+ .ds-dialog-section + .ds-dialog-section {
332
+ margin-top: 1.25rem;
333
+ border-top: 1px solid color-mix(in oklch, var(--border) 60%, transparent);
334
+ padding-top: 1.25rem;
335
+ }
336
+
337
+ /**
338
+ * Status chips and dots: one source for every state pill in every app.
339
+ * Vocabulary: success | warning | error | info | neutral (see the Status type
340
+ * exported from @poodle64/ui/status). Consumed by StatusBadge, StatCard,
341
+ * StatList and DataTableToolbar.
342
+ *
343
+ * `-primary` below is the one exception: a StatusBadge-only extension for
344
+ * brand-emphasis chips (not a health state), keyed off --ds-color-primary
345
+ * rather than a --ds-color-status-* token. It is NOT part of the shared
346
+ * five-state vocabulary above — StatCard, StatList and DataTableToolbar
347
+ * never see it, and the Status type itself stays closed at five states.
348
+ */
349
+ .ds-chip {
350
+ display: inline-flex;
351
+ align-items: center;
352
+ gap: 0.375rem;
353
+ border-radius: var(--ds-radius-sm);
354
+ padding: 0.1rem 0.5rem 0.1rem 0.4rem;
355
+ font-size: 0.75rem;
356
+ line-height: 1rem;
357
+ font-weight: 500;
358
+ white-space: nowrap;
359
+ }
360
+
361
+ .ds-dot {
362
+ width: 0.5rem;
363
+ height: 0.5rem;
364
+ border-radius: 9999px;
365
+ flex: none;
366
+ }
367
+
368
+ .ds-chip-success {
369
+ color: var(--ds-color-status-success);
370
+ background: color-mix(in oklch, var(--ds-color-status-success) 14%, transparent);
371
+ }
372
+ .ds-dot-success {
373
+ background: var(--ds-color-status-success);
374
+ }
375
+
376
+ .ds-chip-warning {
377
+ color: var(--ds-color-status-warning);
378
+ background: color-mix(in oklch, var(--ds-color-status-warning) 16%, transparent);
379
+ }
380
+ .ds-dot-warning {
381
+ background: var(--ds-color-status-warning);
382
+ }
383
+
384
+ .ds-chip-error {
385
+ color: var(--ds-color-status-error);
386
+ background: color-mix(in oklch, var(--ds-color-status-error) 15%, transparent);
387
+ }
388
+ .ds-dot-error {
389
+ background: var(--ds-color-status-error);
390
+ }
391
+
392
+ .ds-chip-info {
393
+ color: var(--ds-color-status-info);
394
+ background: color-mix(in oklch, var(--ds-color-status-info) 16%, transparent);
395
+ }
396
+ .ds-dot-info {
397
+ background: var(--ds-color-status-info);
398
+ }
399
+
400
+ .ds-chip-neutral {
401
+ color: var(--muted-foreground);
402
+ background: color-mix(in oklch, var(--ds-color-status-neutral) 14%, transparent);
403
+ }
404
+ .ds-dot-neutral {
405
+ background: var(--ds-color-status-neutral);
406
+ }
407
+
408
+ .ds-chip-primary {
409
+ color: var(--ds-color-primary);
410
+ background: color-mix(in oklch, var(--ds-color-primary) 14%, transparent);
411
+ }
412
+ .ds-dot-primary {
413
+ background: var(--ds-color-primary);
414
+ }
415
+
416
+ /**
417
+ * Ink in the status vocabulary, for text that IS the state rather than text
418
+ * that sits beside a chip. StatCard's `valueTone` is the first consumer: a
419
+ * negative figure should read as negative on the figure itself, not on a dot
420
+ * two lines above it.
421
+ *
422
+ * Same five names and the same tokens as the chips, so a palette change moves
423
+ * both together and an app cannot end up with a red chip beside an orange
424
+ * number.
425
+ */
426
+ .ds-ink-success {
427
+ color: var(--ds-color-status-success);
428
+ }
429
+ .ds-ink-warning {
430
+ color: var(--ds-color-status-warning);
431
+ }
432
+ .ds-ink-error {
433
+ color: var(--ds-color-status-error);
434
+ }
435
+ .ds-ink-info {
436
+ color: var(--ds-color-status-info);
437
+ }
438
+ .ds-ink-neutral {
439
+ color: var(--muted-foreground);
440
+ }
441
+
442
+ /**
443
+ * A dot for a state that is still moving (StatusBadge's `pulse`). Opacity
444
+ * only — no scale — so the pulse cannot shift the row it sits in, and the dot
445
+ * never fades below the contrast at which it is still legible as a colour.
446
+ */
447
+ .ds-dot-pulse {
448
+ animation: ds-dot-pulse 1.6s ease-in-out infinite;
449
+ }
450
+
451
+ @keyframes ds-dot-pulse {
452
+ 0%,
453
+ 100% {
454
+ opacity: 1;
455
+ }
456
+ 50% {
457
+ opacity: 0.35;
458
+ }
459
+ }
460
+
461
+ /* ─────────────────────────────────────────────────────────────────────────
462
+ The application shell (AppShell, AppNav, CommandPalette).
463
+
464
+ These are real CSS rather than utilities on the elements because the shell
465
+ renders the SAME nav link in two places — desktop rail, mobile drawer —
466
+ and every surveyed app that had two nav call sites had let the two drift
467
+ apart. One rule set, two call sites, no drift. The tints are color-mix over the app's own --primary and
468
+ --foreground, so a palette change moves them and this file holds no colour.
469
+ ───────────────────────────────────────────────────────────────────────── */
470
+ /* Nav ink is a pair of locals rather than the page's --foreground directly,
471
+ because AppNav is rendered in two different kinds of surface: inside the
472
+ shell's chrome (rail, drawer), and — exported in its own
473
+ right — as a route-scoped secondary column sitting on the ordinary page
474
+ background. The default is the page's own ink, and only the chrome
475
+ surfaces below re-point it, so an app that inverts its chrome moves the
476
+ rail's text without dragging a secondary nav on `bg-background` with it.
477
+
478
+ The indirection through --ds-nav-chrome-ink is what makes that actually
479
+ work, and it is not decoration. A custom property declared on an element
480
+ beats one inherited into it, and `.ds-nav` is a DESCENDANT of every chrome
481
+ surface below — so re-pointing --ds-nav-ink on the rail was overridden by
482
+ `.ds-nav`'s own declaration a moment later, on the very element that
483
+ consumes it. Both declarations were correct in isolation and the winner
484
+ was decided by which element carried it, which is why no compiled-CSS gate
485
+ could see it: it is a cascade fact, and it needs an engine. It was
486
+ invisible in use too, because both sides default to the same token, so the
487
+ only symptom was an app inverting its chrome and getting nothing —
488
+ precisely the dead affordance `theme-coverage.test.ts` calls "worse than a
489
+ missing key". `harness/drive.mjs` now drives an inverted chrome and reads
490
+ the resolved ink back.
491
+
492
+ The chrome sets a variable `.ds-nav` READS rather than one it re-declares,
493
+ so the fallback chain resolves in the right order at the point of use. */
494
+ .ds-nav {
495
+ --ds-nav-ink: var(--ds-nav-chrome-ink, var(--foreground));
496
+ --ds-nav-ink-muted: var(--ds-nav-chrome-ink-muted, var(--muted-foreground));
497
+ }
498
+
499
+ .ds-shell-rail,
500
+ .ds-shell-bar {
501
+ --ds-nav-chrome-ink: var(--ds-shell-chrome-foreground);
502
+ --ds-nav-chrome-ink-muted: var(--ds-shell-chrome-muted-foreground);
503
+ }
504
+
505
+ .ds-nav-item {
506
+ position: relative;
507
+ display: flex;
508
+ align-items: center;
509
+ gap: 0.75rem;
510
+ border-radius: var(--ds-radius-md);
511
+ padding: 0.5rem 0.625rem;
512
+ font-size: 0.875rem;
513
+ line-height: 1.25rem;
514
+ color: var(--ds-nav-ink-muted);
515
+ white-space: nowrap;
516
+ transition:
517
+ color 150ms,
518
+ background-color 150ms;
519
+ }
520
+
521
+ .ds-nav-item:hover {
522
+ color: var(--ds-nav-ink);
523
+ background: color-mix(in oklch, var(--ds-nav-ink) 5%, transparent);
524
+ }
525
+
526
+ /* The active row's INK is the chrome's own foreground, never --primary.
527
+ (#11.)
528
+
529
+ --ds-color-primary is the one token the package invites every app to
530
+ override, and the constraint stated where an app picks it is that it clear
531
+ AA against its own -foreground pair — the FILL case, primary as a button
532
+ background. Consuming it as ink on the chrome is a second, stricter and
533
+ entirely undocumented requirement, and an app can satisfy the stated one by
534
+ a mile and fail this one badly: a warm amber that is 7.69:1 as a fill
535
+ measured 1.90:1 here, and a saturated blue that is 5:1 as a fill measured
536
+ 2.87:1. Two real app palettes, both sanctioned, both illegible. The package
537
+ default only ever cleared it by 0.05.
538
+
539
+ So the shell stops asking a colour it does not control to be legible text.
540
+ The ink is the chrome's foreground, which the app's palette is ALREADY
541
+ obliged to keep AA against the chrome, and the guarantee is inherited from
542
+ that existing contract rather than invented as a new one — 15.09:1 on the
543
+ package default, and no app is told to repaint its brand.
544
+
545
+ Two alternatives were measured and lost. Deriving an accessible ink by
546
+ clamping lightness (`oklch(from var(--primary) min(l, X) c h)`) is not
547
+ sound across the gamut — a saturated green clamped to L 0.55 still lands at
548
+ 3.44:1 — so it trades a visible failure for a subtler one. Mixing primary
549
+ into the chrome ink IS provable (35% clears 4.5:1 for every in-gamut
550
+ primary in both modes) but at that ratio the hue is so diluted it reads as
551
+ warm or cool grey, which degrades every palette that was already fine to
552
+ buy safety for palettes no app has.
553
+
554
+ The brand is not lost, it moves to where it is not text: the 12% tint
555
+ below, the edge bar, the underline. An app that wants its brand back ON the
556
+ label sets --ds-nav-ink-active and owns the contrast knowingly:
557
+
558
+ :root { --ds-nav-ink-active: var(--ds-color-primary); }
559
+
560
+ It is read through a var() fallback rather than declared with a default,
561
+ deliberately: declaring `--ds-nav-ink-active: var(--ds-nav-ink)` at :root
562
+ would resolve where --ds-nav-ink does not exist and poison the property. */
563
+ .ds-nav-item[data-active='true'] {
564
+ color: var(--ds-nav-ink-active, var(--ds-nav-ink));
565
+ background: color-mix(in oklch, var(--primary) 12%, transparent);
566
+ font-weight: 500;
567
+ }
568
+
569
+ /* Colour alone never carries the active state (WCAG 1.4.1): the rail marks it
570
+ with a flush edge bar.
571
+
572
+ The bar keeps --primary at full strength, and that is a decision rather
573
+ than an oversight. WCAG 1.4.11 holds a state indicator to 3:1 only when the
574
+ state is not conveyed some other way; here it is conveyed three other ways
575
+ — the ink lifts from muted to full, the weight goes to 500, and the row
576
+ carries aria-current="page" — so the bar is free to be the one place the
577
+ app's brand survives undiluted. `harness/drive.mjs` asserts that redundancy
578
+ on every palette rather than trusting this paragraph, and prints the bar's
579
+ own ratio beside it. */
580
+ .ds-nav-indicator {
581
+ position: absolute;
582
+ left: 0;
583
+ top: 0.375rem;
584
+ bottom: 0.375rem;
585
+ width: 0.125rem;
586
+ border-radius: 9999px;
587
+ background: var(--primary);
588
+ }
589
+
590
+ .ds-nav-heading {
591
+ padding: 0 0.625rem 0.25rem;
592
+ font-size: var(--ds-text-2xs);
593
+ font-weight: 500;
594
+ text-transform: uppercase;
595
+ /* Fallback is load-bearing, not belt-and-braces: Tailwind tree-shakes an
596
+ @theme key whose utility nothing generates, so reading --tracking-eyebrow
597
+ from plain CSS works only while some component still uses the utility
598
+ form. The literal keeps this rule correct on its own; the var keeps an
599
+ app's override flowing through. */
600
+ letter-spacing: var(--tracking-eyebrow, 0.06em);
601
+ color: color-mix(in oklch, var(--ds-nav-ink-muted) 70%, transparent);
602
+ }
603
+
604
+ /* A count badge is small text on a tint, so it carries the 4.5:1 text floor
605
+ with the least margin of anything in the nav — and it was the worst of the
606
+ three (#11): 1.73:1 under a warm amber, and 3.72:1 even on the package
607
+ default, because it sits on the active row's tint AND its own. Same fix,
608
+ same reason: the ink is the chrome's, the brand stays in the chip. */
609
+ .ds-nav-badge {
610
+ margin-left: auto;
611
+ border-radius: 9999px;
612
+ padding: 0 0.375rem;
613
+ font-size: var(--ds-text-2xs);
614
+ font-weight: 500;
615
+ font-variant-numeric: tabular-nums;
616
+ color: var(--ds-nav-ink);
617
+ background: color-mix(in oklch, var(--primary) 15%, transparent);
618
+ }
619
+
620
+ /* A parent whose SECTION you are in, but whose own href does not match. Flat
621
+ prefix matching already covers the ordinary case — children under the
622
+ parent's own path light it up — so this only fires where the children live
623
+ at their own top-level routes and the parent would otherwise go dark while
624
+ you are inside it.
625
+
626
+ It lifts the ink and nothing else, on purpose. The tint, the weight and the
627
+ edge bar are what "this row IS the page" means in this rail, and spending
628
+ any of them on "the page is somewhere below this row" would leave two rows
629
+ claiming to be the current one. */
630
+ .ds-nav-item[data-within='true'] {
631
+ color: var(--ds-nav-ink);
632
+ }
633
+
634
+ /* The disclosed rows. The indent is measured against the parent's own
635
+ geometry rather than picked: .ds-nav-item pads 0.625rem and the icon is
636
+ 1.125rem, so the icon's centre line sits at 1.1875rem — where the guide
637
+ border goes — and 0.625rem of padding here plus the child's own 0.625rem
638
+ lands its label at 2.5rem, exactly under the parent's. The border is a real
639
+ affordance at fifteen rows, not decoration: it is what makes the run
640
+ scannable as one section. */
641
+ .ds-nav-children {
642
+ display: flex;
643
+ flex-direction: column;
644
+ gap: 0.125rem;
645
+ margin-top: 0.125rem;
646
+ margin-left: 1.1875rem;
647
+ padding-left: 0.625rem;
648
+ border-left: 1px solid var(--border);
649
+ }
650
+
651
+ /* `hidden` is an attribute the UA stylesheet honours at the weakest possible
652
+ specificity, so the `display: flex` above beats it and a closed group would
653
+ render wide open. This is not belt-and-braces; without it the attribute is
654
+ inert for layout while still hiding the rows from assistive technology,
655
+ which is the worst of both. */
656
+ .ds-nav-children[hidden] {
657
+ display: none;
658
+ }
659
+
660
+ /* A child sits on the chrome like any other row, one step quieter at rest.
661
+ The active child keeps the full active treatment from .ds-nav-item — it is
662
+ the current page, and depth does not make it less so. */
663
+ .ds-nav-item[data-child='true'] {
664
+ font-size: 0.8125rem;
665
+ }
666
+
667
+ .ds-nav-row {
668
+ display: flex;
669
+ align-items: center;
670
+ gap: 0.125rem;
671
+ }
672
+
673
+ /* The link takes the room and the chevron takes what is left, so a long label
674
+ truncates instead of pushing the control out of the rail. min-width: 0 is
675
+ what actually permits the shrink — a flex item's automatic minimum size is
676
+ its content, and .ds-nav-item is nowrap. */
677
+ .ds-nav-row > .ds-nav-item {
678
+ flex: 1 1 auto;
679
+ min-width: 0;
680
+ }
681
+
682
+ .ds-nav-disclosure {
683
+ display: grid;
684
+ flex: none;
685
+ place-items: center;
686
+ width: 1.75rem;
687
+ height: 1.75rem;
688
+ border-radius: var(--ds-radius-md);
689
+ color: var(--ds-nav-ink-muted);
690
+ transition:
691
+ color 150ms,
692
+ background-color 150ms;
693
+ }
694
+
695
+ .ds-nav-disclosure:hover {
696
+ color: var(--ds-nav-ink);
697
+ background: color-mix(in oklch, var(--ds-nav-ink) 5%, transparent);
698
+ }
699
+
700
+ /* The rotation IS the open/closed state for a sighted user, paired with the
701
+ aria-expanded that carries it for everyone else. Transform on the icon
702
+ rather than a swapped glyph, so the two states are the same object moving
703
+ rather than two pictures. */
704
+ .ds-nav-disclosure > svg {
705
+ transition: transform 150ms ease;
706
+ }
707
+
708
+ .ds-nav-disclosure[aria-expanded='true'] > svg {
709
+ transform: rotate(90deg);
710
+ }
711
+
712
+ .ds-nav[data-collapsed='true'] {
713
+ padding-inline: 0.375rem;
714
+ }
715
+ .ds-nav[data-collapsed='true'] .ds-nav-item {
716
+ justify-content: center;
717
+ padding-inline: 0;
718
+ }
719
+
720
+ /* The rail is ONE element in two states, not two elements (see the comment in
721
+ app-shell.svelte): below md it is hidden until `data-drawer` turns it into
722
+ an overlay; at md and up it is the permanent column and `data-drawer` means
723
+ nothing. Expressing the swap here rather than as competing utility classes
724
+ is what makes it deterministic — `fixed` and `sticky` set the same property,
725
+ so which won would otherwise depend on Tailwind's emission order, not on the
726
+ order they appear in the class attribute.
727
+
728
+ 48rem is Tailwind's own --breakpoint-md. It is written literally because a
729
+ media query cannot read a custom property; an app that retunes the md
730
+ breakpoint must retune this rule with it. */
731
+ .ds-shell-rail {
732
+ display: none;
733
+ position: sticky;
734
+ top: 0;
735
+ height: 100dvh;
736
+ width: var(--ds-shell-rail-width);
737
+ transition: width 200ms ease;
738
+ }
739
+
740
+ .ds-shell-rail[data-drawer='true'] {
741
+ display: flex;
742
+ position: fixed;
743
+ inset-block: 0;
744
+ left: 0;
745
+ z-index: 50;
746
+ max-width: 82vw;
747
+ box-shadow:
748
+ 0 10px 15px -3px oklch(0 0 0 / 0.1),
749
+ 0 4px 6px -4px oklch(0 0 0 / 0.1);
750
+ animation: ds-drawer-in 200ms ease-out;
751
+ }
752
+
753
+ @media (min-width: 48rem) {
754
+ .ds-shell-rail {
755
+ display: flex;
756
+ }
757
+ /* At md the drawer state is inert: the rail is already on screen, so an
758
+ overlay opened on a phone and then widened must not stay an overlay. */
759
+ .ds-shell-rail[data-drawer='true'] {
760
+ position: sticky;
761
+ z-index: auto;
762
+ max-width: none;
763
+ box-shadow: none;
764
+ animation: none;
765
+ }
766
+ }
767
+
768
+ .ds-shell-rail[data-collapsed='true'] {
769
+ width: var(--ds-shell-rail-width-collapsed);
770
+ }
771
+
772
+ /* The content measure: the shell's content box is ONE element in four
773
+ states, the same shape as the rail above it.
774
+
775
+ Real declarations here rather than a `max-w-[var(--ds-shell-measure-page)]`
776
+ utility on the component, for the reason the rail's width is here too: an
777
+ arbitrary utility only exists if the CONSUMER's Tailwind build happened to
778
+ scan the file that spells it, whereas this stylesheet is imported by every
779
+ consumer as a condition of using the package at all. Whoever writes the
780
+ utility owns whatever makes it real.
781
+
782
+ `full` writes no attribute and matches nothing here, so a shell that never
783
+ asked for a measure carries no class, no attribute and no declaration —
784
+ which is what makes the prop additive rather than a default anyone has to
785
+ opt out of.
786
+
787
+ The cap is the BORDER box, so `padded` spends its inline padding inside
788
+ the measure rather than outside it. That is the same arithmetic as the
789
+ `mx-auto max-w-4xl px-6` idiom this replaces, and keeping it means one
790
+ element rather than a second wrapper nobody asked for. */
791
+ .ds-shell-measure {
792
+ margin-inline: auto;
793
+ }
794
+
795
+ .ds-shell-measure[data-measure='prose'] {
796
+ max-width: var(--ds-shell-measure-prose);
797
+ }
798
+
799
+ .ds-shell-measure[data-measure='page'] {
800
+ max-width: var(--ds-shell-measure-page);
801
+ }
802
+
803
+ .ds-shell-measure[data-measure='wide'] {
804
+ max-width: var(--ds-shell-measure-wide);
805
+ }
806
+
807
+ /* The content texture: the house atmosphere, painted once by the shell on its
808
+ own scrolling content region.
809
+
810
+ Two apps had built this picture separately — a faint dot-grid floor plus a
811
+ soft accent vignette in the top-right — under two names, in two app.css
812
+ files, and one applied it as a helper class per route so it reached four
813
+ routes out of about fifteen. It is the same drift the nav rules above were
814
+ hoisted to end, one layer down.
815
+
816
+ A BACKGROUND, not a layer, and that is the design. Both apps first reached
817
+ for an absolutely positioned `::before` inside the scroller, which is wrong
818
+ three ways at once: at `z-index: auto` a positioned box paints above
819
+ non-positioned content, so the atmosphere tints the page rather than sitting
820
+ under it (invisible only because it is faint); `z-index: -1` trades that for
821
+ sinking behind an ancestor's background, since neither `<main>` nor
822
+ `.ds-shell` opens a stacking context; and it has to be excused from
823
+ hit-testing by hand. A background layer cannot be hit-tested, always paints
824
+ beneath every descendant, adds no box to the flex column, and opens no
825
+ stacking context — every one of those properties for free.
826
+
827
+ `background-attachment: local` is what makes it the page's FLOOR rather than
828
+ a backdrop behind a moving page. A scroll container's background defaults to
829
+ `scroll`, which pins it to the border box: the dots would hang motionless
830
+ while the content slid over them. `local` resolves the painting and
831
+ positioning areas against the scrollable area instead, so the texture
832
+ travels with the content that sits on it. The vignette's height is therefore
833
+ a length rather than a percentage — under `local` a percentage would resolve
834
+ against the whole scroll height and stretch one corner glow down a 5000px
835
+ page.
836
+
837
+ That length defaults to `calc(100dvh - 3.5rem)` rather than `100dvh`, and
838
+ the 3.5rem is the top bar's own `h-14`: `.ds-shell` is `h-dvh` with the bar
839
+ above `<main>` in the same column, so the region this paints is one bar
840
+ shorter than the viewport and a plain `100dvh` overshoots it by about 6%.
841
+ The literal is written out for the same reason `48rem` is in the rail's
842
+ media query below — nothing here can read the bar's Tailwind utility — so
843
+ an app that retunes the bar height retunes this alongside it, in the knob
844
+ already provided.
845
+
846
+ The five knobs are read through var() fallbacks at the point of use rather
847
+ than declared at :root, for the reason the @theme inline block above states
848
+ (design-system#8): a :root alias holding a var() reference resolves once, at
849
+ :root, and a scoped override or a scoped .dark wrapper below it can never
850
+ reach the result. Read here, they resolve live on the element that paints
851
+ them. It also means this feature adds nothing at all to a consumer's :root —
852
+ a shell that never asks for a texture receives one rule set whose selector
853
+ matches nothing.
854
+
855
+ An app retunes any of them in one declaration:
856
+
857
+ :root {
858
+ --ds-shell-texture-grid-ink: oklch(0.68 0.02 250 / 0.5);
859
+ --ds-shell-texture-vignette-ink: color-mix(in oklch, var(--primary) 8%, transparent);
860
+ --ds-shell-texture-grid-pitch: 24px;
861
+ --ds-shell-texture-vignette-at: 15% -10%;
862
+ }
863
+
864
+ The corner is a knob rather than a fixed 85% because it is the one value an
865
+ app cannot otherwise reach without redeclaring this whole rule, and because
866
+ a radial-gradient position is PHYSICAL — there is no logical form of `at
867
+ 85%`, so an RTL app wanting the glow at the reading-start corner has to be
868
+ able to say so. Driven at `dir=rtl` alongside a 24px root size and
869
+ `forced-colors: active`, none of which move the texture on their own.
870
+
871
+ A plain wash rather than a grid is that same declaration with the grid ink
872
+ set to `transparent`, which is why `grid` is the only named texture: the
873
+ apps differed on values, not on pictures. */
874
+ .ds-shell-texture[data-texture='grid'] {
875
+ background-image:
876
+ radial-gradient(
877
+ 120% 70% at var(--ds-shell-texture-vignette-at, 85% -10%),
878
+ var(--ds-shell-texture-vignette-ink, color-mix(in oklch, var(--primary) 6%, transparent)),
879
+ transparent 60%
880
+ ),
881
+ radial-gradient(
882
+ circle at 1px 1px,
883
+ var(--ds-shell-texture-grid-ink, color-mix(in oklch, var(--foreground) 14%, transparent))
884
+ 1px,
885
+ transparent 0
886
+ );
887
+ background-repeat: no-repeat, repeat;
888
+ background-position: 0 0;
889
+ background-size:
890
+ 100% var(--ds-shell-texture-vignette-height, calc(100dvh - 3.5rem)),
891
+ var(--ds-shell-texture-grid-pitch, 30px) var(--ds-shell-texture-grid-pitch, 30px);
892
+ background-attachment: local, local;
893
+ }
894
+
895
+ /* Not on paper. A 30px dot grid prints as banding on any real printer, and a
896
+ vignette prints as a smudge in one corner — both apps that built this had
897
+ independently learned that and written the same suppression into their own
898
+ print rules, which is the copy this package is here to take over.
899
+
900
+ Later in the file than the rule above and in the same layer, so it wins
901
+ without `!important`; an app's own print rules are unlayered and still beat
902
+ it, which is the right way round. The white is the content region's, not the
903
+ document's — `html`/`body` stay the app's to decide.
904
+
905
+ `#fff` is the one literal colour in an otherwise entirely token-driven
906
+ feature, on the same grounds `--ds-shadow-sm` gives for its neutral black:
907
+ paper is white, which is a fact about the medium rather than a palette
908
+ choice, and a brand-tinted "white" here would only spend the reader's
909
+ toner. An app that genuinely wants the region printed in a colour paints it
910
+ from its own unlayered print rules and wins. */
911
+ @media print {
912
+ .ds-shell-texture[data-texture='grid'] {
913
+ background-image: none;
914
+ background-color: #fff;
915
+ }
916
+ }
917
+
918
+ /* The bypass link (WCAG 2.4.1): off-screen until focused, then a real target
919
+ above the sticky bar. */
920
+ .ds-skip-link {
921
+ position: absolute;
922
+ left: 0.5rem;
923
+ top: -3rem;
924
+ z-index: 60;
925
+ border-radius: var(--ds-radius-md);
926
+ background: var(--popover, var(--ds-color-surface-3));
927
+ color: var(--popover-foreground, var(--ds-color-foreground));
928
+ padding: 0.5rem 1rem;
929
+ font-size: 0.875rem;
930
+ font-weight: 500;
931
+ box-shadow: var(--ds-shadow-sm);
932
+ transition: top 150ms ease;
933
+ }
934
+ .ds-skip-link:focus {
935
+ top: 0.5rem;
936
+ outline: 2px solid var(--ring);
937
+ outline-offset: 2px;
938
+ }
939
+
940
+ @keyframes ds-drawer-in {
941
+ from {
942
+ transform: translateX(-100%);
943
+ }
944
+ to {
945
+ transform: translateX(0);
946
+ }
947
+ }
948
+
949
+ @media (prefers-reduced-motion: reduce) {
950
+ .ds-shell-rail[data-drawer='true'],
951
+ .ds-dot-pulse {
952
+ animation: none;
953
+ }
954
+ .ds-shell-rail {
955
+ transition: none;
956
+ }
957
+ .ds-nav-disclosure > svg {
958
+ transition: none;
959
+ }
960
+ }
961
+ }