@nqmcreative/ui 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/README.md +89 -34
  2. package/cli/index.mjs +67 -30
  3. package/dist/core/calendar.d.ts +3 -0
  4. package/dist/core/calendar.js +36 -0
  5. package/dist/core/dialog.svelte.d.ts +14 -0
  6. package/dist/core/dialog.svelte.js +23 -0
  7. package/dist/core/files.d.ts +35 -0
  8. package/dist/core/files.js +62 -0
  9. package/dist/core/index.d.ts +37 -0
  10. package/dist/core/index.js +35 -0
  11. package/dist/core/list.svelte.d.ts +41 -0
  12. package/dist/core/list.svelte.js +77 -0
  13. package/dist/core/number.d.ts +7 -0
  14. package/dist/core/number.js +17 -0
  15. package/dist/core/pagination.d.ts +6 -0
  16. package/dist/core/pagination.js +21 -0
  17. package/dist/core/password.d.ts +8 -0
  18. package/dist/core/password.js +23 -0
  19. package/dist/core/table.d.ts +22 -0
  20. package/dist/core/table.js +41 -0
  21. package/dist/core/theme.svelte.d.ts +16 -0
  22. package/dist/core/theme.svelte.js +34 -0
  23. package/dist/core/trigger.d.ts +35 -0
  24. package/dist/core/trigger.js +77 -0
  25. package/dist/index.d.ts +11 -103
  26. package/dist/index.js +11 -75
  27. package/dist/{components → styles/matte}/AccordionItem.svelte +1 -1
  28. package/dist/{components → styles/matte}/AccordionItem.svelte.d.ts +1 -1
  29. package/dist/{components → styles/matte}/Alert.svelte +8 -2
  30. package/dist/{components → styles/matte}/Alert.svelte.d.ts +1 -1
  31. package/dist/{components → styles/matte}/Avatar.svelte +1 -1
  32. package/dist/{components → styles/matte}/Avatar.svelte.d.ts +1 -1
  33. package/dist/{components → styles/matte}/Badge.svelte +8 -3
  34. package/dist/{components → styles/matte}/Badge.svelte.d.ts +1 -3
  35. package/dist/{components → styles/matte}/Breadcrumb.svelte +2 -2
  36. package/dist/{components → styles/matte}/Breadcrumb.svelte.d.ts +1 -1
  37. package/dist/{components → styles/matte}/Button.svelte +1 -1
  38. package/dist/{components → styles/matte}/Button.svelte.d.ts +1 -1
  39. package/dist/styles/matte/CTASection.svelte +76 -0
  40. package/dist/styles/matte/CTASection.svelte.d.ts +18 -0
  41. package/dist/{components → styles/matte}/Calendar.svelte +7 -17
  42. package/dist/{components → styles/matte}/Calendar.svelte.d.ts +1 -1
  43. package/dist/{components → styles/matte}/Card.svelte +1 -1
  44. package/dist/{components → styles/matte}/Card.svelte.d.ts +1 -1
  45. package/dist/{components → styles/matte}/Checkbox.svelte +1 -1
  46. package/dist/{components → styles/matte}/Checkbox.svelte.d.ts +1 -1
  47. package/dist/{components → styles/matte}/CheckboxGroup.svelte +1 -1
  48. package/dist/{components → styles/matte}/CheckboxGroup.svelte.d.ts +1 -1
  49. package/dist/{components → styles/matte}/Combobox.svelte +31 -42
  50. package/dist/{components → styles/matte}/Combobox.svelte.d.ts +1 -1
  51. package/dist/{components → styles/matte}/CommandPalette.svelte +26 -41
  52. package/dist/{components → styles/matte}/CommandPalette.svelte.d.ts +1 -1
  53. package/dist/{components → styles/matte}/ConfirmDialog.svelte +2 -2
  54. package/dist/{components → styles/matte}/ConfirmDialog.svelte.d.ts +2 -2
  55. package/dist/{components → styles/matte}/ContextMenu.svelte +5 -7
  56. package/dist/{components → styles/matte}/DatePicker.svelte +7 -11
  57. package/dist/{components → styles/matte}/DatePicker.svelte.d.ts +1 -1
  58. package/dist/{components → styles/matte}/Drawer.svelte +4 -7
  59. package/dist/{components → styles/matte}/Dropdown.svelte +18 -36
  60. package/dist/{components → styles/matte}/Dropdown.svelte.d.ts +1 -1
  61. package/dist/{components → styles/matte}/Dropzone.svelte +11 -43
  62. package/dist/{components → styles/matte}/Dropzone.svelte.d.ts +2 -5
  63. package/dist/{components → styles/matte}/EmptyState.svelte +1 -1
  64. package/dist/{components → styles/matte}/EmptyState.svelte.d.ts +1 -1
  65. package/dist/styles/matte/FeatureCard.svelte +58 -0
  66. package/dist/styles/matte/FeatureCard.svelte.d.ts +16 -0
  67. package/dist/styles/matte/FeatureGrid.svelte +62 -0
  68. package/dist/styles/matte/FeatureGrid.svelte.d.ts +16 -0
  69. package/dist/{components → styles/matte}/Footer.svelte +1 -1
  70. package/dist/{components → styles/matte}/Footer.svelte.d.ts +1 -1
  71. package/dist/styles/matte/HeroSection.svelte +80 -0
  72. package/dist/styles/matte/HeroSection.svelte.d.ts +21 -0
  73. package/dist/{components → styles/matte}/Input.svelte +1 -1
  74. package/dist/{components → styles/matte}/Input.svelte.d.ts +1 -1
  75. package/dist/{components → styles/matte}/Link.svelte +1 -1
  76. package/dist/{components → styles/matte}/Link.svelte.d.ts +1 -1
  77. package/dist/{components → styles/matte}/LocaleProvider.svelte +1 -1
  78. package/dist/{components → styles/matte}/LocaleProvider.svelte.d.ts +1 -1
  79. package/dist/styles/matte/LogoCloud.svelte +61 -0
  80. package/dist/styles/matte/LogoCloud.svelte.d.ts +19 -0
  81. package/dist/{components → styles/matte}/MenuItem.svelte +1 -1
  82. package/dist/{components → styles/matte}/MenuItem.svelte.d.ts +1 -1
  83. package/dist/{components → styles/matte}/Modal.svelte +4 -7
  84. package/dist/{components → styles/matte}/MultiSelect.svelte +18 -37
  85. package/dist/{components → styles/matte}/MultiSelect.svelte.d.ts +1 -1
  86. package/dist/{components → styles/matte}/Navbar.svelte +2 -2
  87. package/dist/{components → styles/matte}/Navbar.svelte.d.ts +1 -1
  88. package/dist/{components → styles/matte}/NumberInput.svelte +4 -8
  89. package/dist/{components → styles/matte}/NumberInput.svelte.d.ts +1 -1
  90. package/dist/{components → styles/matte}/Pagination.svelte +4 -15
  91. package/dist/{components → styles/matte}/Pagination.svelte.d.ts +1 -1
  92. package/dist/{components → styles/matte}/PasswordInput.svelte +4 -13
  93. package/dist/{components → styles/matte}/PasswordInput.svelte.d.ts +1 -1
  94. package/dist/{components → styles/matte}/Popover.svelte +16 -41
  95. package/dist/{components → styles/matte}/Popover.svelte.d.ts +1 -1
  96. package/dist/styles/matte/PricingCard.svelte +107 -0
  97. package/dist/styles/matte/PricingCard.svelte.d.ts +25 -0
  98. package/dist/{components → styles/matte}/Progress.svelte +3 -2
  99. package/dist/{components → styles/matte}/Progress.svelte.d.ts +1 -1
  100. package/dist/{components → styles/matte}/Radio.svelte +1 -1
  101. package/dist/{components → styles/matte}/Radio.svelte.d.ts +1 -1
  102. package/dist/{components → styles/matte}/RadioGroup.svelte +1 -1
  103. package/dist/{components → styles/matte}/RadioGroup.svelte.d.ts +1 -1
  104. package/dist/{components → styles/matte}/SegmentedControl.svelte +1 -1
  105. package/dist/{components → styles/matte}/SegmentedControl.svelte.d.ts +1 -1
  106. package/dist/{components → styles/matte}/Select.svelte +1 -1
  107. package/dist/{components → styles/matte}/Select.svelte.d.ts +1 -1
  108. package/dist/{components → styles/matte}/Sidebar.svelte +2 -2
  109. package/dist/{components → styles/matte}/Sidebar.svelte.d.ts +1 -1
  110. package/dist/{components → styles/matte}/Slider.svelte +3 -2
  111. package/dist/{components → styles/matte}/Slider.svelte.d.ts +1 -1
  112. package/dist/{components → styles/matte}/Spinner.svelte +2 -2
  113. package/dist/{components → styles/matte}/Spinner.svelte.d.ts +1 -1
  114. package/dist/{components → styles/matte}/Stat.svelte +1 -1
  115. package/dist/{components → styles/matte}/Stat.svelte.d.ts +1 -1
  116. package/dist/styles/matte/StatsBand.svelte +57 -0
  117. package/dist/styles/matte/StatsBand.svelte.d.ts +20 -0
  118. package/dist/{components → styles/matte}/Steps.svelte +1 -1
  119. package/dist/{components → styles/matte}/Steps.svelte.d.ts +1 -1
  120. package/dist/{components → styles/matte}/Switch.svelte +1 -1
  121. package/dist/{components → styles/matte}/Switch.svelte.d.ts +1 -1
  122. package/dist/{components → styles/matte}/Table.svelte +6 -28
  123. package/dist/{components → styles/matte}/Table.svelte.d.ts +2 -6
  124. package/dist/{components → styles/matte}/Tabs.svelte +8 -1
  125. package/dist/{components → styles/matte}/Tabs.svelte.d.ts +1 -1
  126. package/dist/styles/matte/Testimonial.svelte +67 -0
  127. package/dist/styles/matte/Testimonial.svelte.d.ts +19 -0
  128. package/dist/{components → styles/matte}/Textarea.svelte +1 -1
  129. package/dist/{components → styles/matte}/Textarea.svelte.d.ts +1 -1
  130. package/dist/{components → styles/matte}/ThemeToggle.svelte +14 -27
  131. package/dist/{components → styles/matte}/ThemeToggle.svelte.d.ts +2 -6
  132. package/dist/{components → styles/matte}/Toaster.svelte +4 -4
  133. package/dist/styles/matte/index.d.ts +109 -0
  134. package/dist/styles/matte/index.js +83 -0
  135. package/dist/styles/paper/Accordion.svelte +17 -0
  136. package/dist/styles/paper/Accordion.svelte.d.ts +8 -0
  137. package/dist/styles/paper/AccordionItem.svelte +46 -0
  138. package/dist/styles/paper/AccordionItem.svelte.d.ts +14 -0
  139. package/dist/styles/paper/Alert.svelte +99 -0
  140. package/dist/styles/paper/Alert.svelte.d.ts +18 -0
  141. package/dist/styles/paper/Avatar.svelte +60 -0
  142. package/dist/styles/paper/Avatar.svelte.d.ts +16 -0
  143. package/dist/styles/paper/AvatarGroup.svelte +17 -0
  144. package/dist/styles/paper/AvatarGroup.svelte.d.ts +8 -0
  145. package/dist/styles/paper/Badge.svelte +58 -0
  146. package/dist/styles/paper/Badge.svelte.d.ts +16 -0
  147. package/dist/styles/paper/Breadcrumb.svelte +49 -0
  148. package/dist/styles/paper/Breadcrumb.svelte.d.ts +14 -0
  149. package/dist/styles/paper/Button.svelte +96 -0
  150. package/dist/styles/paper/Button.svelte.d.ts +21 -0
  151. package/dist/styles/paper/CTASection.svelte +76 -0
  152. package/dist/styles/paper/CTASection.svelte.d.ts +18 -0
  153. package/dist/styles/paper/Calendar.svelte +202 -0
  154. package/dist/styles/paper/Calendar.svelte.d.ts +22 -0
  155. package/dist/styles/paper/Card.svelte +78 -0
  156. package/dist/styles/paper/Card.svelte.d.ts +21 -0
  157. package/dist/styles/paper/Checkbox.svelte +75 -0
  158. package/dist/styles/paper/Checkbox.svelte.d.ts +15 -0
  159. package/dist/styles/paper/CheckboxGroup.svelte +111 -0
  160. package/dist/styles/paper/CheckboxGroup.svelte.d.ts +30 -0
  161. package/dist/styles/paper/Combobox.svelte +266 -0
  162. package/dist/styles/paper/Combobox.svelte.d.ts +29 -0
  163. package/dist/styles/paper/CommandPalette.svelte +209 -0
  164. package/dist/styles/paper/CommandPalette.svelte.d.ts +32 -0
  165. package/dist/styles/paper/ConfirmDialog.svelte +97 -0
  166. package/dist/styles/paper/ConfirmDialog.svelte.d.ts +22 -0
  167. package/dist/styles/paper/ContextMenu.svelte +88 -0
  168. package/dist/styles/paper/ContextMenu.svelte.d.ts +15 -0
  169. package/dist/styles/paper/DatePicker.svelte +192 -0
  170. package/dist/styles/paper/DatePicker.svelte.d.ts +23 -0
  171. package/dist/styles/paper/Divider.svelte +30 -0
  172. package/dist/styles/paper/Divider.svelte.d.ts +10 -0
  173. package/dist/styles/paper/Drawer.svelte +165 -0
  174. package/dist/styles/paper/Drawer.svelte.d.ts +20 -0
  175. package/dist/styles/paper/Dropdown.svelte +94 -0
  176. package/dist/styles/paper/Dropdown.svelte.d.ts +23 -0
  177. package/dist/styles/paper/Dropzone.svelte +210 -0
  178. package/dist/styles/paper/Dropzone.svelte.d.ts +29 -0
  179. package/dist/styles/paper/EmptyState.svelte +71 -0
  180. package/dist/styles/paper/EmptyState.svelte.d.ts +18 -0
  181. package/dist/styles/paper/FeatureCard.svelte +58 -0
  182. package/dist/styles/paper/FeatureCard.svelte.d.ts +16 -0
  183. package/dist/styles/paper/FeatureGrid.svelte +62 -0
  184. package/dist/styles/paper/FeatureGrid.svelte.d.ts +16 -0
  185. package/dist/styles/paper/Field.svelte +42 -0
  186. package/dist/styles/paper/Field.svelte.d.ts +16 -0
  187. package/dist/styles/paper/Footer.svelte +91 -0
  188. package/dist/styles/paper/Footer.svelte.d.ts +25 -0
  189. package/dist/styles/paper/HeroSection.svelte +86 -0
  190. package/dist/styles/paper/HeroSection.svelte.d.ts +21 -0
  191. package/dist/styles/paper/Input.svelte +60 -0
  192. package/dist/styles/paper/Input.svelte.d.ts +18 -0
  193. package/dist/styles/paper/InputGroup.svelte +20 -0
  194. package/dist/styles/paper/InputGroup.svelte.d.ts +11 -0
  195. package/dist/styles/paper/Kbd.svelte +17 -0
  196. package/dist/styles/paper/Kbd.svelte.d.ts +8 -0
  197. package/dist/styles/paper/Label.svelte +16 -0
  198. package/dist/styles/paper/Label.svelte.d.ts +9 -0
  199. package/dist/styles/paper/Link.svelte +44 -0
  200. package/dist/styles/paper/Link.svelte.d.ts +16 -0
  201. package/dist/styles/paper/LocaleProvider.svelte +24 -0
  202. package/dist/styles/paper/LocaleProvider.svelte.d.ts +10 -0
  203. package/dist/styles/paper/LogoCloud.svelte +61 -0
  204. package/dist/styles/paper/LogoCloud.svelte.d.ts +19 -0
  205. package/dist/styles/paper/MenuItem.svelte +64 -0
  206. package/dist/styles/paper/MenuItem.svelte.d.ts +18 -0
  207. package/dist/styles/paper/MenuSeparator.svelte +22 -0
  208. package/dist/styles/paper/MenuSeparator.svelte.d.ts +8 -0
  209. package/dist/styles/paper/Modal.svelte +118 -0
  210. package/dist/styles/paper/Modal.svelte.d.ts +21 -0
  211. package/dist/styles/paper/MultiSelect.svelte +287 -0
  212. package/dist/styles/paper/MultiSelect.svelte.d.ts +31 -0
  213. package/dist/styles/paper/Navbar.svelte +201 -0
  214. package/dist/styles/paper/Navbar.svelte.d.ts +33 -0
  215. package/dist/styles/paper/NumberInput.svelte +96 -0
  216. package/dist/styles/paper/NumberInput.svelte.d.ts +15 -0
  217. package/dist/styles/paper/Pagination.svelte +83 -0
  218. package/dist/styles/paper/Pagination.svelte.d.ts +15 -0
  219. package/dist/styles/paper/PasswordInput.svelte +100 -0
  220. package/dist/styles/paper/PasswordInput.svelte.d.ts +12 -0
  221. package/dist/styles/paper/Popover.svelte +89 -0
  222. package/dist/styles/paper/Popover.svelte.d.ts +19 -0
  223. package/dist/styles/paper/PricingCard.svelte +108 -0
  224. package/dist/styles/paper/PricingCard.svelte.d.ts +25 -0
  225. package/dist/styles/paper/Progress.svelte +59 -0
  226. package/dist/styles/paper/Progress.svelte.d.ts +17 -0
  227. package/dist/styles/paper/Radio.svelte +55 -0
  228. package/dist/styles/paper/Radio.svelte.d.ts +15 -0
  229. package/dist/styles/paper/RadioGroup.svelte +95 -0
  230. package/dist/styles/paper/RadioGroup.svelte.d.ts +27 -0
  231. package/dist/styles/paper/SegmentedControl.svelte +88 -0
  232. package/dist/styles/paper/SegmentedControl.svelte.d.ts +25 -0
  233. package/dist/styles/paper/Select.svelte +61 -0
  234. package/dist/styles/paper/Select.svelte.d.ts +20 -0
  235. package/dist/styles/paper/Sidebar.svelte +206 -0
  236. package/dist/styles/paper/Sidebar.svelte.d.ts +36 -0
  237. package/dist/styles/paper/Skeleton.svelte +46 -0
  238. package/dist/styles/paper/Skeleton.svelte.d.ts +13 -0
  239. package/dist/styles/paper/Slider.svelte +80 -0
  240. package/dist/styles/paper/Slider.svelte.d.ts +19 -0
  241. package/dist/styles/paper/Spinner.svelte +34 -0
  242. package/dist/styles/paper/Spinner.svelte.d.ts +13 -0
  243. package/dist/styles/paper/Stat.svelte +51 -0
  244. package/dist/styles/paper/Stat.svelte.d.ts +16 -0
  245. package/dist/styles/paper/StatsBand.svelte +63 -0
  246. package/dist/styles/paper/StatsBand.svelte.d.ts +20 -0
  247. package/dist/styles/paper/Steps.svelte +141 -0
  248. package/dist/styles/paper/Steps.svelte.d.ts +24 -0
  249. package/dist/styles/paper/Switch.svelte +64 -0
  250. package/dist/styles/paper/Switch.svelte.d.ts +14 -0
  251. package/dist/styles/paper/Table.svelte +217 -0
  252. package/dist/styles/paper/Table.svelte.d.ts +66 -0
  253. package/dist/styles/paper/Tabs.svelte +102 -0
  254. package/dist/styles/paper/Tabs.svelte.d.ts +23 -0
  255. package/dist/styles/paper/Testimonial.svelte +64 -0
  256. package/dist/styles/paper/Testimonial.svelte.d.ts +19 -0
  257. package/dist/styles/paper/Textarea.svelte +54 -0
  258. package/dist/styles/paper/Textarea.svelte.d.ts +12 -0
  259. package/dist/styles/paper/ThemeToggle.svelte +130 -0
  260. package/dist/styles/paper/ThemeToggle.svelte.d.ts +16 -0
  261. package/dist/styles/paper/Toaster.svelte +132 -0
  262. package/dist/styles/paper/Toaster.svelte.d.ts +8 -0
  263. package/dist/styles/paper/Tooltip.svelte +33 -0
  264. package/dist/styles/paper/Tooltip.svelte.d.ts +12 -0
  265. package/dist/styles/paper/fonts.css +46 -0
  266. package/dist/styles/paper/index.d.ts +109 -0
  267. package/dist/styles/paper/index.js +83 -0
  268. package/dist/styles/paper/theme.css +222 -0
  269. package/package.json +531 -201
  270. package/registry.json +658 -339
  271. /package/dist/{actions → core/actions}/anchor.d.ts +0 -0
  272. /package/dist/{actions → core/actions}/anchor.js +0 -0
  273. /package/dist/{actions → core/actions}/dismissable.d.ts +0 -0
  274. /package/dist/{actions → core/actions}/dismissable.js +0 -0
  275. /package/dist/{date.d.ts → core/date.d.ts} +0 -0
  276. /package/dist/{date.js → core/date.js} +0 -0
  277. /package/dist/{locale.svelte.d.ts → core/locale.svelte.d.ts} +0 -0
  278. /package/dist/{locale.svelte.js → core/locale.svelte.js} +0 -0
  279. /package/dist/{toast.svelte.d.ts → core/toast.svelte.d.ts} +0 -0
  280. /package/dist/{toast.svelte.js → core/toast.svelte.js} +0 -0
  281. /package/dist/{tones.d.ts → core/tones.d.ts} +0 -0
  282. /package/dist/{tones.js → core/tones.js} +0 -0
  283. /package/dist/{components → styles/matte}/Accordion.svelte +0 -0
  284. /package/dist/{components → styles/matte}/Accordion.svelte.d.ts +0 -0
  285. /package/dist/{components → styles/matte}/AvatarGroup.svelte +0 -0
  286. /package/dist/{components → styles/matte}/AvatarGroup.svelte.d.ts +0 -0
  287. /package/dist/{components → styles/matte}/ContextMenu.svelte.d.ts +0 -0
  288. /package/dist/{components → styles/matte}/Divider.svelte +0 -0
  289. /package/dist/{components → styles/matte}/Divider.svelte.d.ts +0 -0
  290. /package/dist/{components → styles/matte}/Drawer.svelte.d.ts +0 -0
  291. /package/dist/{components → styles/matte}/Field.svelte +0 -0
  292. /package/dist/{components → styles/matte}/Field.svelte.d.ts +0 -0
  293. /package/dist/{components → styles/matte}/InputGroup.svelte +0 -0
  294. /package/dist/{components → styles/matte}/InputGroup.svelte.d.ts +0 -0
  295. /package/dist/{components → styles/matte}/Kbd.svelte +0 -0
  296. /package/dist/{components → styles/matte}/Kbd.svelte.d.ts +0 -0
  297. /package/dist/{components → styles/matte}/Label.svelte +0 -0
  298. /package/dist/{components → styles/matte}/Label.svelte.d.ts +0 -0
  299. /package/dist/{components → styles/matte}/MenuSeparator.svelte +0 -0
  300. /package/dist/{components → styles/matte}/MenuSeparator.svelte.d.ts +0 -0
  301. /package/dist/{components → styles/matte}/Modal.svelte.d.ts +0 -0
  302. /package/dist/{components → styles/matte}/Skeleton.svelte +0 -0
  303. /package/dist/{components → styles/matte}/Skeleton.svelte.d.ts +0 -0
  304. /package/dist/{components → styles/matte}/Toaster.svelte.d.ts +0 -0
  305. /package/dist/{components → styles/matte}/Tooltip.svelte +0 -0
  306. /package/dist/{components → styles/matte}/Tooltip.svelte.d.ts +0 -0
  307. /package/dist/{fonts.css → styles/matte/fonts.css} +0 -0
  308. /package/dist/{theme.css → styles/matte/theme.css} +0 -0
package/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # @nqmcreative/ui
2
2
 
3
- Svelte 5 (runes) + Tailwind CSS v4 component library. Flat surfaces, no
4
- shadows, no border-radius except pills. 57 components, eight tones, light and
5
- dark, no runtime dependencies.
3
+ Svelte 5 (runes) + Tailwind CSS v4 component library. One package, **two
4
+ styles**, 65 components each the same props, the same behaviour, a different
5
+ look. Eight tones, light and dark, no runtime dependencies.
6
+
7
+ | style | import | character |
8
+ | ------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
9
+ | `matte` | `@nqmcreative/ui/matte` | Flat and sharp-cornered — no radius, no shadow, no gloss. Hairline rules and mono micro-labels do the work borders usually do. |
10
+ | `paper` | `@nqmcreative/ui/paper` | The neutral one. Start here if unsure: soft corners, light shadows, generous spacing, and nothing that fights the content. |
6
11
 
7
12
  The docs site lives in this repo — every component with a live demo, plus
8
13
  theming, locale and contributing guides. `bun run dev` to read it.
@@ -14,7 +19,7 @@ bun add @nqmcreative/ui
14
19
  ```
15
20
 
16
21
  ```bash
17
- bunx nqm-ui init
22
+ bunx nqm-ui init --style matte
18
23
  ```
19
24
 
20
25
  `init` writes `src/app.css`, patches `src/app.html`, and adds the CSS import to
@@ -33,14 +38,18 @@ Your project brings its own `svelte` ^5 and Tailwind CSS v4.
33
38
  ```css
34
39
  /* src/app.css */
35
40
  @import 'tailwindcss';
36
- @import '@nqmcreative/ui/theme.css';
37
- @import '@nqmcreative/ui/fonts.css';
41
+ @import '@nqmcreative/ui/matte/theme.css';
42
+ @import '@nqmcreative/ui/matte/fonts.css';
38
43
 
39
44
  /* Tailwind v4 skips node_modules. Without this every component renders
40
- unstyled, with no error anywhere. */
41
- @source '../node_modules/@nqmcreative/ui/dist';
45
+ unstyled, with no error anywhere. Point it at the style you import. */
46
+ @source '../node_modules/@nqmcreative/ui/dist/styles/matte';
42
47
  ```
43
48
 
49
+ Import exactly one style's `theme.css` — they all define the same token names,
50
+ so two would fight over every colour. Each style ships its own `fonts.css`
51
+ alongside it; skip it and the type falls back to the system stack.
52
+
44
53
  Import that CSS once in `src/routes/+layout.svelte`, and put the font
45
54
  preconnect plus the no-flash theme script in `src/app.html`, above
46
55
  `%sveltekit.head%`:
@@ -57,13 +66,11 @@ preconnect plus the no-flash theme script in `src/app.html`, above
57
66
 
58
67
  ## Use
59
68
 
60
- Each component has its own subpath — the file name in kebab-case:
69
+ The import names the style:
61
70
 
62
71
  ```svelte
63
72
  <script lang="ts">
64
- import Button from '@nqmcreative/ui/button';
65
- import Field from '@nqmcreative/ui/field';
66
- import Input from '@nqmcreative/ui/input';
73
+ import { Button, Field, Input } from '@nqmcreative/ui/matte';
67
74
 
68
75
  let email = $state('');
69
76
  </script>
@@ -75,7 +82,13 @@ Each component has its own subpath — the file name in kebab-case:
75
82
  <Button tone="accent">Get started</Button>
76
83
  ```
77
84
 
78
- The barrel works too: `import { Button } from '@nqmcreative/ui'`.
85
+ Per-component subpaths work too the style, then the file name in kebab-case:
86
+ `@nqmcreative/ui/matte/avatar-group`.
87
+
88
+ **There is no root export.** `import { Button } from '@nqmcreative/ui'` resolves
89
+ to nothing, on purpose: a style is a choice, and picking one by accident is the
90
+ mistake this layout exists to prevent. Switching later is a find-and-replace of
91
+ the word after `@nqmcreative/ui/`.
79
92
 
80
93
  ### Tones
81
94
 
@@ -89,22 +102,23 @@ Eight of them — `brand`, `accent`, `violet`, `info`, `success`, `warning`,
89
102
  ```
90
103
 
91
104
  Each ships four steps: `bg-accent`, `hover:bg-accent-hover`, `bg-accent-light`,
92
- `border-accent-border`. Redeclare any of them in your own `@theme` block to
93
- rebrand everything at once.
105
+ `border-accent-border`. The token _names_ are a contract every style fills in,
106
+ so only the values differ between them. Redeclare any of them in your own
107
+ `@theme` block to rebrand everything at once.
94
108
 
95
109
  ### Dark mode
96
110
 
97
- `theme.css` ships both palettes; nothing in a component says `dark:`.
98
- `<html class="dark">` or `class="light"` forces one, bare `<html>` follows the
99
- OS. `ThemeToggle` handles all three.
111
+ Each style's `theme.css` ships both palettes; nothing in a component says
112
+ `dark:`. `<html class="dark">` or `class="light"` forces one, bare `<html>`
113
+ follows the OS. `ThemeToggle` handles all three.
100
114
 
101
115
  ### Locale
102
116
 
103
117
  Every built-in string — ARIA names, `No data`, the calendar's month names —
104
- comes from one object:
118
+ comes from one object, shared by every style:
105
119
 
106
120
  ```js
107
- import { setLocale, idID } from '@nqmcreative/ui/locale';
121
+ import { setLocale, idID } from '@nqmcreative/ui/core';
108
122
 
109
123
  setLocale(idID);
110
124
  ```
@@ -112,6 +126,26 @@ setLocale(idID);
112
126
  `enUS` and `idID` ship with the package; pass a partial to change a few keys.
113
127
  Under SSR use `<LocaleProvider>` instead, so the locale is scoped to a request.
114
128
 
129
+ ## Layout
130
+
131
+ ```
132
+ src/lib/core/ behaviour, a11y, types — no markup, no classes
133
+ src/lib/styles/matte/ 65 components
134
+ src/lib/styles/paper/ 65 components
135
+ scripts/catalogue.mjs the list every style must implement
136
+ ```
137
+
138
+ `core` holds what has no opinion about looks: focus traps, roving keyboard
139
+ navigation, viewport-aware anchoring, date maths, file validation, table
140
+ sorting, the toast queue, the locale strings. There is no `matte` version of a
141
+ focus trap, so no style writes one.
142
+
143
+ A style holds only markup and classes. It may differ from another completely —
144
+ different DOM, different animation — as long as the props and the behaviour
145
+ match. `bun run lint` fails if a style is missing a component from the
146
+ catalogue, and the parity suite mounts every style to assert they behave the
147
+ same.
148
+
115
149
  ## Components
116
150
 
117
151
  | group | components |
@@ -123,6 +157,7 @@ Under SSR use `<LocaleProvider>` instead, so the locale is scoped to a request.
123
157
  | Layout & nav | `Divider`, `Tabs`, `Accordion`, `AccordionItem`, `Breadcrumb`, `Pagination`, `Steps` |
124
158
  | App shell | `Navbar`, `Sidebar`, `Footer` |
125
159
  | Overlay | `Modal`, `ConfirmDialog`, `Drawer`, `Dropdown` + `MenuItem` + `MenuSeparator`, `ContextMenu`, `CommandPalette`, `Popover`, `Tooltip` |
160
+ | Marketing | `HeroSection`, `FeatureGrid`, `FeatureCard`, `PricingCard`, `Testimonial`, `LogoCloud`, `CTASection`, `StatsBand` |
126
161
  | System | `ThemeToggle`, `LocaleProvider` |
127
162
 
128
163
  Overlays are built on the native top layer and three in-house actions
@@ -131,33 +166,53 @@ Overlays are built on the native top layer and three in-house actions
131
166
  ## CLI
132
167
 
133
168
  ```bash
134
- bunx nqm-ui list forms # components in a category
135
- bunx nqm-ui info date-picker # subpath, and what it pulls in
136
- bunx nqm-ui add button badge # print the import lines
137
- bunx nqm-ui add button --to src/routes/+page.svelte
169
+ bunx nqm-ui list forms # components in a category
170
+ bunx nqm-ui info date-picker # its import line in every style
171
+ bunx nqm-ui add button badge --style matte # print the import lines
172
+ bunx nqm-ui add button --style paper --to src/routes/+page.svelte
138
173
  ```
139
174
 
175
+ `--style` has no default: the CLI lists the styles and asks, rather than
176
+ guessing.
177
+
140
178
  ## Developing
141
179
 
142
180
  ```bash
143
181
  bun install
144
182
  bun run dev # the docs site at localhost:5173
145
183
  bun run check # type-check
146
- bun run test # vitest
184
+ bun run test # vitest, including the cross-style parity suite
147
185
  bun run build # site + package
148
186
  ```
149
187
 
150
188
  `src/lib` is the package, `src/site` and `src/routes` are the docs site.
151
189
 
152
- **`dist/` is committed rebuild and commit it with your change.** Consumers
153
- read from `dist/`, and installing from GitHub cannot build it: bun blocks a
154
- dependency's `prepare` script.
155
-
156
- Adding a component: write it in `src/lib/components`, export it from
157
- `src/lib/index.ts` under a category comment, add a demo in `src/site/demos`,
158
- then `bun run build`. `bun run lint` fails if the generated exports map or
159
- `registry.json` is stale. The full conventions are on the site under
160
- `/docs/adding-components`.
190
+ **The style is the first segment of the URL**, not a toggle:
191
+ `/matte/components/button` and `/paper/components/button` are two prerendered
192
+ pages. Each renders entirely in its own style — navbar, sidebar, footer and
193
+ search box included — because every page takes its components from
194
+ `UI[data.style]` rather than importing one style directly. The static HTML for
195
+ the two differs: `h-11 px-7` in one, `h-10 px-4 rounded-md` in the other.
196
+
197
+ Two pieces make that work. `src/hooks.server.ts` stamps `data-style` onto
198
+ `<html>` before the first paint — on `<html>` rather than a wrapper, so
199
+ portalled overlays reach the tokens too and dark mode's class lands on the same
200
+ element. `src/routes/styles.css` re-declares each style's tokens under
201
+ `[data-style]`, generated by `bun run scoped`.
202
+
203
+ Both are docs-only. The package still ships one global `theme.css` per style,
204
+ because `--color-brand` cannot hold two values on one element.
205
+
206
+ **`dist/` is committed — rebuild and commit it with your change**, so what is
207
+ in the repo always matches the source beside it.
208
+
209
+ Adding a component: add a line to `scripts/catalogue.mjs`, put any behaviour in
210
+ `src/lib/core`, write the file in **every** style folder, add **one** demo in
211
+ `src/site/demos`, then `bun run build`. Barrels, exports map, registry, scoped
212
+ tokens and the per-style demo copies are all generated — a demo is authored once
213
+ against `matte` and copied into every other style with its import path
214
+ rewritten. `bun run lint` fails if any is stale or a style is missing
215
+ the component. Full conventions are on the site under `/docs/adding-components`.
161
216
 
162
217
  ## License
163
218
 
package/cli/index.mjs CHANGED
@@ -23,7 +23,7 @@ const argv = process.argv.slice(2);
23
23
  const flags = new Set(argv.filter((a) => a.startsWith('--')));
24
24
 
25
25
  /** Flags that take a value, so the value is not mistaken for a positional. */
26
- const valueFlags = new Set(['--to']);
26
+ const valueFlags = new Set(['--to', '--style']);
27
27
  const args = [];
28
28
  for (let i = 0; i < argv.length; i++) {
29
29
  if (argv[i].startsWith('--')) {
@@ -66,15 +66,37 @@ async function registry() {
66
66
  fail('registry.json not found — is the package installed correctly?');
67
67
  }
68
68
 
69
- /** Accepts `Button`, `button`, `date-picker` or the full subpath. */
69
+ /** Accepts `Button`, `button`, `date-picker` or any style's full subpath. */
70
70
  function findComponent(list, query) {
71
- const needle = query.toLowerCase().replace(/^@nqmcreative\/ui\//, '');
72
- return list.find(
73
- (item) =>
74
- item.name.toLowerCase() === needle ||
75
- item.subpath.endsWith(`/${needle}`) ||
76
- item.subpath === query
77
- );
71
+ const needle = query.toLowerCase().replace(/^@nqmcreative\/ui\/[a-z-]+\//, '');
72
+ return list.find((item) => item.name.toLowerCase() === needle || item.slug === needle);
73
+ }
74
+
75
+ /**
76
+ * The style to work in. There is no default on purpose: a component belongs to
77
+ * a style, and picking one by accident is the mistake this package exists to
78
+ * prevent. Asking once beats shipping the wrong look.
79
+ */
80
+ function pickStyle(styles) {
81
+ const index = argv.indexOf('--style');
82
+ const wanted = index !== -1 ? argv[index + 1] : null;
83
+
84
+ if (!wanted) {
85
+ log();
86
+ log(' which style?');
87
+ log();
88
+ for (const style of styles) {
89
+ log(` ${c.cyan(style.name.padEnd(8))} ${c.dim(style.description)}`);
90
+ }
91
+ log();
92
+ log(` ${c.dim(`e.g. ${command} --style ${styles[0].name}`)}`);
93
+ log();
94
+ process.exit(1);
95
+ }
96
+
97
+ const style = styles.find((s) => s.name === wanted);
98
+ if (!style) fail(`unknown style "${wanted}" — try ${styles.map((s) => s.name).join(' or ')}`);
99
+ return style;
78
100
  }
79
101
 
80
102
  /* ------------------------------------------------------------ file edits -- */
@@ -128,10 +150,12 @@ function sourcePath(cssRel) {
128
150
  /* ------------------------------------------------------------------ init -- */
129
151
 
130
152
  async function init() {
153
+ const { styles } = await registry();
154
+ const style = pickStyle(styles);
131
155
  const project = await detectProject();
132
156
 
133
157
  log();
134
- log(c.bold(' @nqmcreative/ui'));
158
+ log(c.bold(` @nqmcreative/ui — ${style.title}`));
135
159
  log(c.dim(` ${project.isKit ? 'SvelteKit' : 'Svelte + Vite'} project detected`));
136
160
  log();
137
161
 
@@ -151,16 +175,20 @@ async function init() {
151
175
  const source = sourcePath(project.cssRel);
152
176
  const lines = [
153
177
  `@import 'tailwindcss';`,
154
- `@import '@nqmcreative/ui/theme.css';`,
155
- `@import '@nqmcreative/ui/fonts.css';`,
178
+ `@import '@nqmcreative/ui/${style.name}/theme.css';`,
179
+ ...(style.fonts ? [`@import '@nqmcreative/ui/${style.name}/fonts.css';`] : []),
156
180
  ``,
157
- `/* Tailwind v4 skips node_modules — point it at the package's dist so`,
158
- ` the class names used inside the components are generated. */`,
159
- `@source '${source}';`
181
+ `/* Tailwind v4 skips node_modules — point it at this style's folder so`,
182
+ ` the class names used inside its components are generated. */`,
183
+ `@source '${source}/styles/${style.name}';`
160
184
  ];
161
185
 
162
186
  const css = await readOr(project.css);
163
- if (css.includes('@nqmcreative/ui/theme.css')) {
187
+
188
+ // Matched against *this* style's theme, not any of them. A file left over
189
+ // from an older layout — or wired to a different style — has to be reported,
190
+ // not skipped as already done.
191
+ if (css.includes(`@nqmcreative/ui/${style.name}/theme.css`)) {
164
192
  skip(`${project.cssRel} already imports the theme`);
165
193
  } else {
166
194
  const body = css.trim()
@@ -197,7 +225,7 @@ async function init() {
197
225
  // Unindented — `%sveltekit.head%` already sits at its own indentation,
198
226
  // so the join below supplies it and the first line does not double up.
199
227
  const additions = [];
200
- if (!html.includes('fonts.gstatic.com')) {
228
+ if (style.fonts && !html.includes('fonts.gstatic.com')) {
201
229
  additions.push(`<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />`);
202
230
  }
203
231
  if (!html.includes('nqm-theme')) {
@@ -272,14 +300,15 @@ async function init() {
272
300
  }
273
301
 
274
302
  log();
275
- log(` Next: ${c.cyan('bunx @nqmcreative/ui add button')}`);
303
+ log(` Next: ${c.cyan(`bunx @nqmcreative/ui add button --style ${style.name}`)}`);
276
304
  log();
277
305
  }
278
306
 
279
307
  /* ------------------------------------------------------------------- add -- */
280
308
 
281
309
  async function add() {
282
- const { components } = await registry();
310
+ const { components, styles } = await registry();
311
+ const style = pickStyle(styles);
283
312
  const names = args.slice(1);
284
313
  if (names.length === 0) fail('which component? e.g. `nqm-ui add button dialog`');
285
314
 
@@ -290,7 +319,7 @@ async function add() {
290
319
  const close = components
291
320
  .filter((c) => c.name.toLowerCase().startsWith(name.slice(0, 3).toLowerCase()))
292
321
  .slice(0, 3)
293
- .map((c) => c.subpath.split('/').pop());
322
+ .map((c) => c.slug);
294
323
  fail(
295
324
  `unknown component "${name}"${close.length ? ` — did you mean ${close.join(', ')}?` : ''}`
296
325
  );
@@ -298,7 +327,7 @@ async function add() {
298
327
  found.push(item);
299
328
  }
300
329
 
301
- const imports = found.map((item) => `import ${item.name} from '${item.subpath}';`);
330
+ const imports = found.map((item) => `import ${item.name} from '${item.subpaths[style.name]}';`);
302
331
 
303
332
  // `--to <file>` inserts the imports into an existing Svelte file.
304
333
  const toIndex = argv.indexOf('--to');
@@ -339,7 +368,7 @@ async function add() {
339
368
  /* ------------------------------------------------------------------ list -- */
340
369
 
341
370
  async function list() {
342
- const { components, count } = await registry();
371
+ const { components, count, styles } = await registry();
343
372
  const filter = args[1]?.toLowerCase();
344
373
 
345
374
  const groups = {};
@@ -355,11 +384,15 @@ async function list() {
355
384
  for (const [category, items] of Object.entries(groups)) {
356
385
  log(` ${c.bold(category)}`);
357
386
  for (const item of items) {
358
- log(` ${item.subpath.split('/').pop().padEnd(20)} ${c.dim(item.subpath)}`);
387
+ log(` ${item.slug.padEnd(20)} ${c.dim(item.description)}`);
359
388
  }
360
389
  log();
361
390
  }
362
- log(c.dim(` ${shown} of ${count} components`));
391
+ log(
392
+ c.dim(
393
+ ` ${shown} of ${count} components, in every style: ${styles.map((s) => s.name).join(', ')}`
394
+ )
395
+ );
363
396
  log();
364
397
  }
365
398
 
@@ -374,11 +407,12 @@ async function info() {
374
407
  log(` ${c.bold(item.name)} ${c.dim(item.category)}`);
375
408
  if (item.description) log(` ${item.description}`);
376
409
  log();
377
- log(` ${c.cyan(`import ${item.name} from '${item.subpath}';`)}`);
410
+ for (const [style, subpath] of Object.entries(item.subpaths)) {
411
+ log(` ${c.dim(style.padEnd(8))} ${c.cyan(`import ${item.name} from '${subpath}';`)}`);
412
+ }
378
413
  log();
379
414
  if (item.uses.length) log(` renders ${item.uses.join(', ')}`);
380
- if (item.modules.length) log(` uses ${item.modules.join(', ')}`);
381
- log(` source ${item.file}`);
415
+ if (item.modules.length) log(` core ${item.modules.join(', ')}`);
382
416
  log();
383
417
  }
384
418
 
@@ -388,13 +422,16 @@ function help() {
388
422
  log(`
389
423
  ${c.bold('@nqmcreative/ui')}
390
424
 
391
- ${c.cyan('init')} wire the design system into this project
425
+ ${c.cyan('init --style <s>')} wire one style into this project
392
426
  ${c.cyan('add <name...>')} print the import for a component
427
+ ${c.dim('--style <s>')} which style to import from (required)
393
428
  ${c.dim('--to <file>')} …and insert it into that file instead
394
- ${c.cyan('list [category]')} every component and its subpath
395
- ${c.cyan('info <name>')} what one component pulls in
429
+ ${c.cyan('list [category]')} every component in the catalogue
430
+ ${c.cyan('info <name>')} what one component pulls in, in every style
396
431
 
397
432
  ${c.dim('--dry-run')} show the writes without making them
433
+
434
+ ${c.dim('Every style implements every component. Run init once per project.')}
398
435
  `);
399
436
  }
400
437
 
@@ -0,0 +1,3 @@
1
+ export declare function calendarKeyMove(key: string, focused: Date, weekStart: 0 | 1, shiftKey?: boolean): Date | null;
2
+ /** The placeholder a text date field shows for a locale's part order. */
3
+ export declare function dateHint(format: 'dmy' | 'mdy' | 'ymd'): string;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Keyboard movement inside a month grid.
3
+ *
4
+ * The arrow keys are obvious; Home/End are not — they go to the start and end
5
+ * of the *displayed* week, which depends on whether the grid starts on Monday
6
+ * or Sunday. Returns the day focus should move to, or `null` for a key the
7
+ * grid does not handle.
8
+ */
9
+ import { addDays, addMonths } from './date.js';
10
+ export function calendarKeyMove(key, focused, weekStart, shiftKey = false) {
11
+ const intoWeek = (focused.getDay() - weekStart + 7) % 7;
12
+ switch (key) {
13
+ case 'ArrowLeft':
14
+ return addDays(focused, -1);
15
+ case 'ArrowRight':
16
+ return addDays(focused, 1);
17
+ case 'ArrowUp':
18
+ return addDays(focused, -7);
19
+ case 'ArrowDown':
20
+ return addDays(focused, 7);
21
+ case 'Home':
22
+ return addDays(focused, -intoWeek);
23
+ case 'End':
24
+ return addDays(focused, 6 - intoWeek);
25
+ case 'PageUp':
26
+ return addMonths(focused, shiftKey ? -12 : -1);
27
+ case 'PageDown':
28
+ return addMonths(focused, shiftKey ? 12 : 1);
29
+ default:
30
+ return null;
31
+ }
32
+ }
33
+ /** The placeholder a text date field shows for a locale's part order. */
34
+ export function dateHint(format) {
35
+ return { dmy: 'DD/MM/YYYY', mdy: 'MM/DD/YYYY', ymd: 'YYYY/MM/DD' }[format];
36
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Keeps a bound `open` prop and a native `<dialog>` in step.
3
+ *
4
+ * `showModal()` throws if the dialog is already open and `close()` fires an
5
+ * event of its own, so both directions need the guard below. Every overlay
6
+ * built on `<dialog>` — Modal, Drawer, CommandPalette — calls this from an
7
+ * effect rather than repeating the two `if`s.
8
+ */
9
+ export declare function syncDialog(dialog: HTMLDialogElement | null, open: boolean): void;
10
+ /**
11
+ * True when a click landed on the backdrop rather than inside the panel.
12
+ * The backdrop is not a separate element — it is the dialog's own box.
13
+ */
14
+ export declare function isBackdropClick(event: MouseEvent, dialog: HTMLDialogElement | null): boolean;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Keeps a bound `open` prop and a native `<dialog>` in step.
3
+ *
4
+ * `showModal()` throws if the dialog is already open and `close()` fires an
5
+ * event of its own, so both directions need the guard below. Every overlay
6
+ * built on `<dialog>` — Modal, Drawer, CommandPalette — calls this from an
7
+ * effect rather than repeating the two `if`s.
8
+ */
9
+ export function syncDialog(dialog, open) {
10
+ if (!dialog)
11
+ return;
12
+ if (open && !dialog.open)
13
+ dialog.showModal();
14
+ else if (!open && dialog.open)
15
+ dialog.close();
16
+ }
17
+ /**
18
+ * True when a click landed on the backdrop rather than inside the panel.
19
+ * The backdrop is not a separate element — it is the dialog's own box.
20
+ */
21
+ export function isBackdropClick(event, dialog) {
22
+ return !!dialog && event.target === dialog;
23
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * File validation for the drop zone: which files a set of rules lets through,
3
+ * why the rest were turned away, and how to print a byte count.
4
+ */
5
+ export interface RejectedFile {
6
+ file: File;
7
+ reason: 'type' | 'size' | 'count';
8
+ }
9
+ export interface FileRules {
10
+ /** Same syntax as the input's `accept` — `'image/*,.pdf'`. */
11
+ accept?: string;
12
+ /** Per-file ceiling in bytes. `0` means no limit. */
13
+ maxSize?: number;
14
+ /** Cap on how many files may be held at once. `0` means no limit. */
15
+ maxFiles?: number;
16
+ multiple?: boolean;
17
+ /** How many are already held, counted against `maxFiles`. */
18
+ held?: number;
19
+ }
20
+ /** `image/*`, `.pdf` and `application/pdf` all mean what you would expect. */
21
+ export declare function matchesAccept(file: File, accept?: string): boolean;
22
+ /**
23
+ * Sorts one drop into keepers and rejects. Order matters: a file is judged on
24
+ * type first, then size, then whether there is room left — so the reason a
25
+ * user sees is the first rule it actually broke.
26
+ */
27
+ export declare function sortFiles(incoming: FileList | File[] | null, rules?: FileRules): {
28
+ accepted: File[];
29
+ rejected: RejectedFile[];
30
+ };
31
+ /**
32
+ * `900 B`, `2 MB`, `1.5 GB`. Units come from the locale so the number and its
33
+ * suffix can be translated together.
34
+ */
35
+ export declare function formatSize(bytes: number, units: readonly string[]): string;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * File validation for the drop zone: which files a set of rules lets through,
3
+ * why the rest were turned away, and how to print a byte count.
4
+ */
5
+ /** `image/*`, `.pdf` and `application/pdf` all mean what you would expect. */
6
+ export function matchesAccept(file, accept) {
7
+ if (!accept)
8
+ return true;
9
+ return accept.split(',').some((raw) => {
10
+ const rule = raw.trim().toLowerCase();
11
+ if (!rule)
12
+ return true;
13
+ if (rule.startsWith('.'))
14
+ return file.name.toLowerCase().endsWith(rule);
15
+ if (rule.endsWith('/*'))
16
+ return file.type.startsWith(rule.slice(0, -1));
17
+ return file.type.toLowerCase() === rule;
18
+ });
19
+ }
20
+ /**
21
+ * Sorts one drop into keepers and rejects. Order matters: a file is judged on
22
+ * type first, then size, then whether there is room left — so the reason a
23
+ * user sees is the first rule it actually broke.
24
+ */
25
+ export function sortFiles(incoming, rules = {}) {
26
+ const accepted = [];
27
+ const rejected = [];
28
+ if (!incoming)
29
+ return { accepted, rejected };
30
+ const { accept, maxSize = 0, maxFiles = 0, multiple = true, held = 0 } = rules;
31
+ let room = maxFiles ? maxFiles - (multiple ? held : 0) : Infinity;
32
+ for (const file of Array.from(incoming)) {
33
+ if (!matchesAccept(file, accept))
34
+ rejected.push({ file, reason: 'type' });
35
+ else if (maxSize && file.size > maxSize)
36
+ rejected.push({ file, reason: 'size' });
37
+ else if (room <= 0)
38
+ rejected.push({ file, reason: 'count' });
39
+ else {
40
+ accepted.push(file);
41
+ room--;
42
+ }
43
+ if (!multiple && accepted.length === 1)
44
+ break;
45
+ }
46
+ return { accepted, rejected };
47
+ }
48
+ /**
49
+ * `900 B`, `2 MB`, `1.5 GB`. Units come from the locale so the number and its
50
+ * suffix can be translated together.
51
+ */
52
+ export function formatSize(bytes, units) {
53
+ if (bytes < 1024)
54
+ return `${bytes} ${units[0]}`;
55
+ let value = bytes / 1024;
56
+ let i = 1;
57
+ while (value >= 1024 && i < units.length - 1) {
58
+ value /= 1024;
59
+ i++;
60
+ }
61
+ return `${value.toFixed(Number.isInteger(value) || value >= 10 ? 0 : 1)} ${units[i]}`;
62
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @nqmcreative/ui/core — everything the styles share.
3
+ *
4
+ * Behaviour, not looks: focus containment, keyboard navigation, date maths,
5
+ * file validation, the toast queue, the locale strings. Nothing in here
6
+ * renders anything, and nothing in here knows which style is being used.
7
+ *
8
+ * Styles are free to ignore any of it — but there is no `matte` version of a
9
+ * focus trap, so none of them should have to write one.
10
+ */
11
+ export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText } from './tones.js';
12
+ export type { Tone } from './tones.js';
13
+ export { anchored } from './actions/anchor.js';
14
+ export type { Placement, AnchorOptions } from './actions/anchor.js';
15
+ export { clickOutside, focusTrap, focusable, navigateList, portal } from './actions/dismissable.js';
16
+ export type { ClickOutsideOptions, FocusTrapOptions } from './actions/dismissable.js';
17
+ export { syncDialog, isBackdropClick } from './dialog.svelte.js';
18
+ export { popupTrigger, describeTrigger, menuItems, HOVER_GRACE } from './trigger.js';
19
+ export type { PopupTriggerOptions } from './trigger.js';
20
+ export { ListCursor, groupItems, matchQuery, revealIndex } from './list.svelte.js';
21
+ export type { ListItem } from './list.svelte.js';
22
+ export { nextSort, sortRows, toggleKey } from './table.js';
23
+ export type { TableSort, SortDirection } from './table.js';
24
+ export { pageRange } from './pagination.js';
25
+ export { sortFiles, matchesAccept, formatSize } from './files.js';
26
+ export type { RejectedFile, FileRules } from './files.js';
27
+ export { passwordScore } from './password.js';
28
+ export { clampToStep, percentOf, stepDecimals } from './number.js';
29
+ export { toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from './date.js';
30
+ export type { DateFormat } from './date.js';
31
+ export { calendarKeyMove, dateHint } from './calendar.js';
32
+ export { toast } from './toast.svelte.js';
33
+ export type { Toast, ToastOptions, ToastAction } from './toast.svelte.js';
34
+ export { applyTheme, storedTheme, persistTheme, nextTheme } from './theme.svelte.js';
35
+ export type { Theme } from './theme.svelte.js';
36
+ export { locale, setLocale, provideLocale, useLocale, enUS, idID, LocaleStore } from './locale.svelte.js';
37
+ export type { Locale } from './locale.svelte.js';