@dillingerstaffing/strand-ui 0.1.0 → 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 (261) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/Alert/Alert.d.ts +16 -0
  3. package/dist/components/Alert/Alert.d.ts.map +1 -0
  4. package/dist/components/Alert/index.d.ts +3 -0
  5. package/dist/components/Alert/index.d.ts.map +1 -0
  6. package/dist/components/Avatar/Avatar.d.ts +16 -0
  7. package/dist/components/Avatar/Avatar.d.ts.map +1 -0
  8. package/dist/components/Avatar/index.d.ts +3 -0
  9. package/dist/components/Avatar/index.d.ts.map +1 -0
  10. package/dist/components/Badge/Badge.d.ts +18 -0
  11. package/dist/components/Badge/Badge.d.ts.map +1 -0
  12. package/dist/components/Badge/index.d.ts +3 -0
  13. package/dist/components/Badge/index.d.ts.map +1 -0
  14. package/dist/components/Breadcrumb/Breadcrumb.d.ts +16 -0
  15. package/dist/components/Breadcrumb/Breadcrumb.d.ts.map +1 -0
  16. package/dist/components/Breadcrumb/index.d.ts +3 -0
  17. package/dist/components/Breadcrumb/index.d.ts.map +1 -0
  18. package/dist/components/Button/Button.d.ts +22 -0
  19. package/dist/components/Button/Button.d.ts.map +1 -0
  20. package/dist/components/Button/index.d.ts +3 -0
  21. package/dist/components/Button/index.d.ts.map +1 -0
  22. package/dist/components/Card/Card.d.ts +12 -0
  23. package/dist/components/Card/Card.d.ts.map +1 -0
  24. package/dist/components/Card/index.d.ts +3 -0
  25. package/dist/components/Card/index.d.ts.map +1 -0
  26. package/dist/components/Checkbox/Checkbox.d.ts +20 -0
  27. package/dist/components/Checkbox/Checkbox.d.ts.map +1 -0
  28. package/dist/components/Checkbox/index.d.ts +3 -0
  29. package/dist/components/Checkbox/index.d.ts.map +1 -0
  30. package/dist/components/Container/Container.d.ts +10 -0
  31. package/dist/components/Container/Container.d.ts.map +1 -0
  32. package/dist/components/Container/index.d.ts +3 -0
  33. package/dist/components/Container/index.d.ts.map +1 -0
  34. package/dist/components/DataReadout/DataReadout.d.ts +12 -0
  35. package/dist/components/DataReadout/DataReadout.d.ts.map +1 -0
  36. package/dist/components/DataReadout/index.d.ts +3 -0
  37. package/dist/components/DataReadout/index.d.ts.map +1 -0
  38. package/dist/components/Dialog/Dialog.d.ts +20 -0
  39. package/dist/components/Dialog/Dialog.d.ts.map +1 -0
  40. package/dist/components/Dialog/index.d.ts +3 -0
  41. package/dist/components/Dialog/index.d.ts.map +1 -0
  42. package/dist/components/Divider/Divider.d.ts +13 -0
  43. package/dist/components/Divider/Divider.d.ts.map +1 -0
  44. package/dist/components/Divider/index.d.ts +3 -0
  45. package/dist/components/Divider/index.d.ts.map +1 -0
  46. package/dist/components/FormField/FormField.d.ts +22 -0
  47. package/dist/components/FormField/FormField.d.ts.map +1 -0
  48. package/dist/components/FormField/index.d.ts +3 -0
  49. package/dist/components/FormField/index.d.ts.map +1 -0
  50. package/dist/components/Grid/Grid.d.ts +12 -0
  51. package/dist/components/Grid/Grid.d.ts.map +1 -0
  52. package/dist/components/Grid/index.d.ts +3 -0
  53. package/dist/components/Grid/index.d.ts.map +1 -0
  54. package/dist/components/Input/Input.d.ts +18 -0
  55. package/dist/components/Input/Input.d.ts.map +1 -0
  56. package/dist/components/Input/index.d.ts +3 -0
  57. package/dist/components/Input/index.d.ts.map +1 -0
  58. package/dist/components/Link/Link.d.ts +12 -0
  59. package/dist/components/Link/Link.d.ts.map +1 -0
  60. package/dist/components/Link/index.d.ts +3 -0
  61. package/dist/components/Link/index.d.ts.map +1 -0
  62. package/dist/components/Nav/Nav.d.ts +19 -0
  63. package/dist/components/Nav/Nav.d.ts.map +1 -0
  64. package/dist/components/Nav/index.d.ts +3 -0
  65. package/dist/components/Nav/index.d.ts.map +1 -0
  66. package/dist/components/Progress/Progress.d.ts +14 -0
  67. package/dist/components/Progress/Progress.d.ts.map +1 -0
  68. package/dist/components/Progress/index.d.ts +3 -0
  69. package/dist/components/Progress/index.d.ts.map +1 -0
  70. package/dist/components/Radio/Radio.d.ts +22 -0
  71. package/dist/components/Radio/Radio.d.ts.map +1 -0
  72. package/dist/components/Radio/index.d.ts +3 -0
  73. package/dist/components/Radio/index.d.ts.map +1 -0
  74. package/dist/components/Section/Section.d.ts +12 -0
  75. package/dist/components/Section/Section.d.ts.map +1 -0
  76. package/dist/components/Section/index.d.ts +3 -0
  77. package/dist/components/Section/index.d.ts.map +1 -0
  78. package/dist/components/Select/Select.d.ts +24 -0
  79. package/dist/components/Select/Select.d.ts.map +1 -0
  80. package/dist/components/Select/index.d.ts +3 -0
  81. package/dist/components/Select/index.d.ts.map +1 -0
  82. package/dist/components/Skeleton/Skeleton.d.ts +14 -0
  83. package/dist/components/Skeleton/Skeleton.d.ts.map +1 -0
  84. package/dist/components/Skeleton/index.d.ts +3 -0
  85. package/dist/components/Skeleton/index.d.ts.map +1 -0
  86. package/dist/components/Slider/Slider.d.ts +20 -0
  87. package/dist/components/Slider/Slider.d.ts.map +1 -0
  88. package/dist/components/Slider/index.d.ts +3 -0
  89. package/dist/components/Slider/index.d.ts.map +1 -0
  90. package/dist/components/Spinner/Spinner.d.ts +10 -0
  91. package/dist/components/Spinner/Spinner.d.ts.map +1 -0
  92. package/dist/components/Spinner/index.d.ts +3 -0
  93. package/dist/components/Spinner/index.d.ts.map +1 -0
  94. package/dist/components/Stack/Stack.d.ts +18 -0
  95. package/dist/components/Stack/Stack.d.ts.map +1 -0
  96. package/dist/components/Stack/index.d.ts +3 -0
  97. package/dist/components/Stack/index.d.ts.map +1 -0
  98. package/dist/components/Switch/Switch.d.ts +18 -0
  99. package/dist/components/Switch/Switch.d.ts.map +1 -0
  100. package/dist/components/Switch/index.d.ts +3 -0
  101. package/dist/components/Switch/index.d.ts.map +1 -0
  102. package/dist/components/Table/Table.d.ts +24 -0
  103. package/dist/components/Table/Table.d.ts.map +1 -0
  104. package/dist/components/Table/index.d.ts +3 -0
  105. package/dist/components/Table/index.d.ts.map +1 -0
  106. package/dist/components/Tabs/Tabs.d.ts +19 -0
  107. package/dist/components/Tabs/Tabs.d.ts.map +1 -0
  108. package/dist/components/Tabs/index.d.ts +3 -0
  109. package/dist/components/Tabs/index.d.ts.map +1 -0
  110. package/dist/components/Tag/Tag.d.ts +18 -0
  111. package/dist/components/Tag/Tag.d.ts.map +1 -0
  112. package/dist/components/Tag/index.d.ts +3 -0
  113. package/dist/components/Tag/index.d.ts.map +1 -0
  114. package/dist/components/Textarea/Textarea.d.ts +22 -0
  115. package/dist/components/Textarea/Textarea.d.ts.map +1 -0
  116. package/dist/components/Textarea/index.d.ts +3 -0
  117. package/dist/components/Textarea/index.d.ts.map +1 -0
  118. package/dist/components/Toast/Toast.d.ts +33 -0
  119. package/dist/components/Toast/Toast.d.ts.map +1 -0
  120. package/dist/components/Toast/index.d.ts +3 -0
  121. package/dist/components/Toast/index.d.ts.map +1 -0
  122. package/dist/components/Tooltip/Tooltip.d.ts +16 -0
  123. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -0
  124. package/dist/components/Tooltip/index.d.ts +3 -0
  125. package/dist/components/Tooltip/index.d.ts.map +1 -0
  126. package/dist/css/strand-ui.css +2464 -0
  127. package/dist/index.d.ts +64 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/test-setup.d.ts +2 -0
  130. package/dist/test-setup.d.ts.map +1 -0
  131. package/package.json +25 -11
  132. package/src/__tests__/build-output.test.ts +200 -0
  133. package/src/__tests__/design-language.test.ts +137 -0
  134. package/src/__tests__/static.test.tsx +60 -0
  135. package/src/components/Alert/Alert.css +75 -0
  136. package/src/components/Alert/Alert.test.tsx +92 -0
  137. package/src/components/Alert/Alert.tsx +59 -0
  138. package/src/components/Alert/index.ts +2 -0
  139. package/src/components/Avatar/Avatar.css +55 -0
  140. package/src/components/Avatar/Avatar.test.tsx +123 -0
  141. package/src/components/Avatar/Avatar.tsx +67 -0
  142. package/src/components/Avatar/index.ts +2 -0
  143. package/src/components/Badge/Badge.css +72 -0
  144. package/src/components/Badge/Badge.test.tsx +121 -0
  145. package/src/components/Badge/Badge.tsx +92 -0
  146. package/src/components/Badge/index.ts +2 -0
  147. package/src/components/Breadcrumb/Breadcrumb.css +50 -0
  148. package/src/components/Breadcrumb/Breadcrumb.test.tsx +107 -0
  149. package/src/components/Breadcrumb/Breadcrumb.tsx +59 -0
  150. package/src/components/Breadcrumb/index.ts +2 -0
  151. package/src/components/Button/Button.css +195 -0
  152. package/src/components/Button/Button.test.tsx +171 -0
  153. package/src/components/Button/Button.tsx +78 -0
  154. package/src/components/Button/index.ts +2 -0
  155. package/src/components/Card/Card.css +68 -0
  156. package/src/components/Card/Card.test.tsx +90 -0
  157. package/src/components/Card/Card.tsx +41 -0
  158. package/src/components/Card/index.ts +2 -0
  159. package/src/components/Checkbox/Checkbox.css +97 -0
  160. package/src/components/Checkbox/Checkbox.test.tsx +92 -0
  161. package/src/components/Checkbox/Checkbox.tsx +137 -0
  162. package/src/components/Checkbox/index.ts +2 -0
  163. package/src/components/Container/Container.css +25 -0
  164. package/src/components/Container/Container.test.tsx +82 -0
  165. package/src/components/Container/Container.tsx +37 -0
  166. package/src/components/Container/index.ts +2 -0
  167. package/src/components/DataReadout/DataReadout.css +30 -0
  168. package/src/components/DataReadout/DataReadout.test.tsx +105 -0
  169. package/src/components/DataReadout/DataReadout.tsx +29 -0
  170. package/src/components/DataReadout/index.ts +2 -0
  171. package/src/components/Dialog/Dialog.css +81 -0
  172. package/src/components/Dialog/Dialog.test.tsx +203 -0
  173. package/src/components/Dialog/Dialog.tsx +179 -0
  174. package/src/components/Dialog/index.ts +2 -0
  175. package/src/components/Divider/Divider.css +44 -0
  176. package/src/components/Divider/Divider.test.tsx +86 -0
  177. package/src/components/Divider/Divider.tsx +81 -0
  178. package/src/components/Divider/index.ts +2 -0
  179. package/src/components/FormField/FormField.css +47 -0
  180. package/src/components/FormField/FormField.test.tsx +99 -0
  181. package/src/components/FormField/FormField.tsx +79 -0
  182. package/src/components/FormField/index.ts +2 -0
  183. package/src/components/Grid/Grid.css +27 -0
  184. package/src/components/Grid/Grid.test.tsx +86 -0
  185. package/src/components/Grid/Grid.tsx +45 -0
  186. package/src/components/Grid/index.ts +2 -0
  187. package/src/components/Input/Input.css +87 -0
  188. package/src/components/Input/Input.test.tsx +95 -0
  189. package/src/components/Input/Input.tsx +69 -0
  190. package/src/components/Input/index.ts +2 -0
  191. package/src/components/Link/Link.css +30 -0
  192. package/src/components/Link/Link.test.tsx +88 -0
  193. package/src/components/Link/Link.tsx +31 -0
  194. package/src/components/Link/index.ts +2 -0
  195. package/src/components/Nav/Nav.css +179 -0
  196. package/src/components/Nav/Nav.test.tsx +174 -0
  197. package/src/components/Nav/Nav.tsx +101 -0
  198. package/src/components/Nav/index.ts +2 -0
  199. package/src/components/Progress/Progress.css +93 -0
  200. package/src/components/Progress/Progress.test.tsx +93 -0
  201. package/src/components/Progress/Progress.tsx +104 -0
  202. package/src/components/Progress/index.ts +2 -0
  203. package/src/components/Radio/Radio.css +98 -0
  204. package/src/components/Radio/Radio.test.tsx +80 -0
  205. package/src/components/Radio/Radio.tsx +72 -0
  206. package/src/components/Radio/index.ts +2 -0
  207. package/src/components/Section/Section.css +28 -0
  208. package/src/components/Section/Section.test.tsx +100 -0
  209. package/src/components/Section/Section.tsx +41 -0
  210. package/src/components/Section/index.ts +2 -0
  211. package/src/components/Select/Select.css +75 -0
  212. package/src/components/Select/Select.test.tsx +99 -0
  213. package/src/components/Select/Select.tsx +78 -0
  214. package/src/components/Select/index.ts +2 -0
  215. package/src/components/Skeleton/Skeleton.css +52 -0
  216. package/src/components/Skeleton/Skeleton.test.tsx +96 -0
  217. package/src/components/Skeleton/Skeleton.tsx +55 -0
  218. package/src/components/Skeleton/index.ts +2 -0
  219. package/src/components/Slider/Slider.css +107 -0
  220. package/src/components/Slider/Slider.test.tsx +85 -0
  221. package/src/components/Slider/Slider.tsx +66 -0
  222. package/src/components/Slider/index.ts +2 -0
  223. package/src/components/Spinner/Spinner.css +61 -0
  224. package/src/components/Spinner/Spinner.test.tsx +56 -0
  225. package/src/components/Spinner/Spinner.tsx +38 -0
  226. package/src/components/Spinner/index.ts +2 -0
  227. package/src/components/Stack/Stack.css +71 -0
  228. package/src/components/Stack/Stack.test.tsx +130 -0
  229. package/src/components/Stack/Stack.tsx +77 -0
  230. package/src/components/Stack/index.ts +2 -0
  231. package/src/components/Switch/Switch.css +94 -0
  232. package/src/components/Switch/Switch.test.tsx +98 -0
  233. package/src/components/Switch/Switch.tsx +80 -0
  234. package/src/components/Switch/index.ts +2 -0
  235. package/src/components/Table/Table.css +83 -0
  236. package/src/components/Table/Table.test.tsx +134 -0
  237. package/src/components/Table/Table.tsx +102 -0
  238. package/src/components/Table/index.ts +2 -0
  239. package/src/components/Tabs/Tabs.css +51 -0
  240. package/src/components/Tabs/Tabs.test.tsx +164 -0
  241. package/src/components/Tabs/Tabs.tsx +126 -0
  242. package/src/components/Tabs/index.ts +2 -0
  243. package/src/components/Tag/Tag.css +98 -0
  244. package/src/components/Tag/Tag.test.tsx +112 -0
  245. package/src/components/Tag/Tag.tsx +73 -0
  246. package/src/components/Tag/index.ts +2 -0
  247. package/src/components/Textarea/Textarea.css +80 -0
  248. package/src/components/Textarea/Textarea.test.tsx +89 -0
  249. package/src/components/Textarea/Textarea.tsx +102 -0
  250. package/src/components/Textarea/index.ts +2 -0
  251. package/src/components/Toast/Toast.css +103 -0
  252. package/src/components/Toast/Toast.test.tsx +219 -0
  253. package/src/components/Toast/Toast.tsx +177 -0
  254. package/src/components/Toast/index.ts +2 -0
  255. package/src/components/Tooltip/Tooltip.css +63 -0
  256. package/src/components/Tooltip/Tooltip.test.tsx +196 -0
  257. package/src/components/Tooltip/Tooltip.tsx +89 -0
  258. package/src/components/Tooltip/index.ts +2 -0
  259. package/src/index.ts +99 -0
  260. package/src/static.css +47 -0
  261. package/src/test-setup.ts +7 -0
@@ -0,0 +1,64 @@
1
+ /*! Strand UI v0.2.0 | MIT License | dillingerstaffing.com */
2
+ export { Button } from "./components/Button/index.js";
3
+ export type { ButtonProps } from "./components/Button/index.js";
4
+ export { Input } from "./components/Input/index.js";
5
+ export type { InputProps } from "./components/Input/index.js";
6
+ export { Textarea } from "./components/Textarea/index.js";
7
+ export type { TextareaProps } from "./components/Textarea/index.js";
8
+ export { Select } from "./components/Select/index.js";
9
+ export type { SelectProps, SelectOption } from "./components/Select/index.js";
10
+ export { Checkbox } from "./components/Checkbox/index.js";
11
+ export type { CheckboxProps } from "./components/Checkbox/index.js";
12
+ export { Radio } from "./components/Radio/index.js";
13
+ export type { RadioProps } from "./components/Radio/index.js";
14
+ export { Switch } from "./components/Switch/index.js";
15
+ export type { SwitchProps } from "./components/Switch/index.js";
16
+ export { Slider } from "./components/Slider/index.js";
17
+ export type { SliderProps } from "./components/Slider/index.js";
18
+ export { FormField } from "./components/FormField/index.js";
19
+ export type { FormFieldProps } from "./components/FormField/index.js";
20
+ export { Card } from "./components/Card/index.js";
21
+ export type { CardProps } from "./components/Card/index.js";
22
+ export { Badge } from "./components/Badge/index.js";
23
+ export type { BadgeProps } from "./components/Badge/index.js";
24
+ export { Avatar } from "./components/Avatar/index.js";
25
+ export type { AvatarProps } from "./components/Avatar/index.js";
26
+ export { Tag } from "./components/Tag/index.js";
27
+ export type { TagProps } from "./components/Tag/index.js";
28
+ export { Table } from "./components/Table/index.js";
29
+ export type { TableProps, TableColumn } from "./components/Table/index.js";
30
+ export { DataReadout } from "./components/DataReadout/index.js";
31
+ export type { DataReadoutProps } from "./components/DataReadout/index.js";
32
+ export { Stack } from "./components/Stack/index.js";
33
+ export type { StackProps } from "./components/Stack/index.js";
34
+ export { Grid } from "./components/Grid/index.js";
35
+ export type { GridProps } from "./components/Grid/index.js";
36
+ export { Container } from "./components/Container/index.js";
37
+ export type { ContainerProps } from "./components/Container/index.js";
38
+ export { Divider } from "./components/Divider/index.js";
39
+ export type { DividerProps } from "./components/Divider/index.js";
40
+ export { Section } from "./components/Section/index.js";
41
+ export type { SectionProps } from "./components/Section/index.js";
42
+ export { Link } from "./components/Link/index.js";
43
+ export type { LinkProps } from "./components/Link/index.js";
44
+ export { Tabs } from "./components/Tabs/index.js";
45
+ export type { TabsProps, TabItem } from "./components/Tabs/index.js";
46
+ export { Breadcrumb } from "./components/Breadcrumb/index.js";
47
+ export type { BreadcrumbProps, BreadcrumbItem } from "./components/Breadcrumb/index.js";
48
+ export { Nav } from "./components/Nav/index.js";
49
+ export type { NavProps } from "./components/Nav/index.js";
50
+ export { Toast, ToastProvider, useToast } from "./components/Toast/index.js";
51
+ export type { ToastProps } from "./components/Toast/index.js";
52
+ export { Alert } from "./components/Alert/index.js";
53
+ export type { AlertProps } from "./components/Alert/index.js";
54
+ export { Dialog } from "./components/Dialog/index.js";
55
+ export type { DialogProps } from "./components/Dialog/index.js";
56
+ export { Tooltip } from "./components/Tooltip/index.js";
57
+ export type { TooltipProps } from "./components/Tooltip/index.js";
58
+ export { Progress } from "./components/Progress/index.js";
59
+ export type { ProgressProps } from "./components/Progress/index.js";
60
+ export { Spinner } from "./components/Spinner/index.js";
61
+ export type { SpinnerProps } from "./components/Spinner/index.js";
62
+ export { Skeleton } from "./components/Skeleton/index.js";
63
+ export type { SkeletonProps } from "./components/Skeleton/index.js";
64
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAG7D,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE9E,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAC5D,YAAY,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE3E,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAC5D,YAAY,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGlE,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAExF,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAG1D,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAC7E,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,21 @@
1
1
  {
2
2
  "name": "@dillingerstaffing/strand-ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Strand UI - Preact/React component library built on the Strand Design Language",
5
5
  "author": "Dillinger Staffing <engineering@dillingerstaffing.com> (https://dillingerstaffing.com)",
6
6
  "license": "MIT",
7
+ "keywords": [
8
+ "design-system",
9
+ "ui-components",
10
+ "preact",
11
+ "react",
12
+ "css-custom-properties",
13
+ "design-tokens",
14
+ "accessibility",
15
+ "wcag",
16
+ "aria",
17
+ "component-library"
18
+ ],
7
19
  "homepage": "https://dillingerstaffing.com/labs/strand",
8
20
  "repository": {
9
21
  "type": "git",
@@ -22,20 +34,16 @@
22
34
  "types": "./dist/index.d.ts",
23
35
  "import": "./dist/index.js"
24
36
  },
25
- "./css/*": "./dist/css/*"
37
+ "./css/strand-ui.css": "./dist/css/strand-ui.css"
26
38
  },
39
+ "style": "./dist/css/strand-ui.css",
27
40
  "files": [
28
- "dist/"
41
+ "dist/",
42
+ "src/"
29
43
  ],
30
44
  "sideEffects": [
31
45
  "dist/css/*.css"
32
46
  ],
33
- "scripts": {
34
- "build": "vite build",
35
- "test": "vitest run",
36
- "test:watch": "vitest",
37
- "test:coverage": "vitest run --coverage"
38
- },
39
47
  "peerDependencies": {
40
48
  "preact": "^10.0.0"
41
49
  },
@@ -45,7 +53,7 @@
45
53
  }
46
54
  },
47
55
  "dependencies": {
48
- "@dillingerstaffing/strand": "workspace:*"
56
+ "@dillingerstaffing/strand": "0.2.0"
49
57
  },
50
58
  "devDependencies": {
51
59
  "@testing-library/preact": "^3.2.0",
@@ -54,5 +62,11 @@
54
62
  "preact": "^10.25.0",
55
63
  "vite": "^6.0.0",
56
64
  "vitest": "^3.0.0"
65
+ },
66
+ "scripts": {
67
+ "build": "vite build && tsc --emitDeclarationOnly",
68
+ "test": "vitest run",
69
+ "test:watch": "vitest",
70
+ "test:coverage": "vitest run --coverage"
57
71
  }
58
- }
72
+ }
@@ -0,0 +1,200 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+
5
+ const distDir = resolve(__dirname, "../../dist");
6
+
7
+ describe("Build output", () => {
8
+ it("produces index.js bundle", () => {
9
+ expect(existsSync(resolve(distDir, "index.js"))).toBe(true);
10
+ });
11
+
12
+ it("produces index.d.ts declarations", () => {
13
+ expect(existsSync(resolve(distDir, "index.d.ts"))).toBe(true);
14
+ });
15
+
16
+ it("declarations export all component types", () => {
17
+ const dts = readFileSync(resolve(distDir, "index.d.ts"), "utf-8");
18
+ const expectedExports = [
19
+ "Button", "Input", "Textarea", "Select", "Checkbox", "Radio",
20
+ "Switch", "Slider", "FormField", "Card", "Badge", "Avatar",
21
+ "Tag", "Table", "DataReadout", "Stack", "Grid", "Container",
22
+ "Divider", "Section", "Link", "Tabs", "Breadcrumb", "Nav",
23
+ "Toast", "Alert", "Dialog", "Tooltip", "Progress", "Spinner", "Skeleton",
24
+ ];
25
+ for (const name of expectedExports) {
26
+ expect(dts, `Missing export: ${name}`).toContain(name);
27
+ }
28
+ });
29
+
30
+ it("produces combined CSS file", () => {
31
+ expect(existsSync(resolve(distDir, "css/strand-ui.css"))).toBe(true);
32
+ });
33
+
34
+ it("CSS file contains styles for all components", () => {
35
+ const css = readFileSync(resolve(distDir, "css/strand-ui.css"), "utf-8");
36
+ const expectedClasses = [
37
+ ".strand-btn", ".strand-input", ".strand-select",
38
+ ".strand-checkbox", ".strand-radio", ".strand-switch",
39
+ ".strand-slider", ".strand-card", ".strand-badge",
40
+ ".strand-avatar", ".strand-tag", ".strand-table",
41
+ ".strand-data-readout", ".strand-stack", ".strand-grid",
42
+ ".strand-container", ".strand-divider", ".strand-section",
43
+ ".strand-link", ".strand-tabs", ".strand-breadcrumb",
44
+ ".strand-nav", ".strand-toast", ".strand-alert",
45
+ ".strand-dialog", ".strand-tooltip", ".strand-progress",
46
+ ".strand-spinner", ".strand-skeleton",
47
+ ];
48
+ for (const cls of expectedClasses) {
49
+ expect(css, `Missing CSS class: ${cls}`).toContain(cls);
50
+ }
51
+ });
52
+
53
+ it("CSS file uses only strand tokens (no hardcoded colors)", () => {
54
+ const css = readFileSync(resolve(distDir, "css/strand-ui.css"), "utf-8");
55
+ // Remove comments and check for hardcoded hex colors that aren't in rgba()
56
+ const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, "");
57
+ const hexMatches = withoutComments.match(/#[0-9a-fA-F]{6}\b/g) || [];
58
+ // Filter out known exceptions (danger hover/active states that extend the palette)
59
+ const knownExceptions = ["#DC2626", "#B91C1C", "#0D7377", "#92400E", "#991B1B"];
60
+ const unexpected = hexMatches.filter(
61
+ (h) => !knownExceptions.includes(h.toUpperCase())
62
+ );
63
+ expect(unexpected, `Hardcoded hex colors found: ${unexpected.join(", ")}`).toEqual([]);
64
+ });
65
+
66
+ it("JS bundle is under 50KB gzipped budget", () => {
67
+ const js = readFileSync(resolve(distDir, "index.js"), "utf-8");
68
+ // Rough check: uncompressed should be well under 200KB (gzip ~4:1 ratio)
69
+ expect(js.length).toBeLessThan(200_000);
70
+ });
71
+
72
+ it("src/ directory exists for CLI copy-paste", () => {
73
+ expect(existsSync(resolve(__dirname, "../components/Button/Button.tsx"))).toBe(true);
74
+ expect(existsSync(resolve(__dirname, "../components/Button/Button.css"))).toBe(true);
75
+ expect(existsSync(resolve(__dirname, "../components/Dialog/Dialog.tsx"))).toBe(true);
76
+ });
77
+
78
+ it("CSS has no hardcoded duration values", () => {
79
+ const css = readFileSync(resolve(distDir, "css/strand-ui.css"), "utf-8");
80
+ const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, "");
81
+ // Strip @keyframes blocks entirely (durations inside keyframes are acceptable)
82
+ const withoutKeyframes = withoutComments.replace(/@keyframes\s+[\w-]+\s*\{[^}]*(?:\{[^}]*\}[^}]*)*\}/g, "");
83
+ // Strip animation shorthand lines (duration is part of the shorthand spec, not standalone)
84
+ const withoutAnimations = withoutKeyframes.replace(/animation:.*$/gm, "");
85
+ // Strip var() references (tokenized durations are fine)
86
+ const withoutVars = withoutAnimations.replace(/var\([^)]+\)/g, "VAR_REF");
87
+
88
+ const durationPattern = /(?<!\w)(150ms|250ms|400ms|700ms|1\.8s|1\.5s|1\.2s|0\.8s)\b/g;
89
+ const matches: string[] = [];
90
+ let match: RegExpExecArray | null;
91
+ while ((match = durationPattern.exec(withoutVars)) !== null) {
92
+ matches.push(match[0]);
93
+ }
94
+
95
+ // Exception: 75ms in transition-duration for active/pressed states (Part XII spec)
96
+ const nonExemptPattern = /(?<!\w)75ms\b/g;
97
+ const lines = withoutVars.split("\n");
98
+ for (const line of lines) {
99
+ if (nonExemptPattern.test(line) && !line.includes("transition-duration")) {
100
+ matches.push("75ms (outside transition-duration)");
101
+ }
102
+ nonExemptPattern.lastIndex = 0;
103
+ }
104
+
105
+ expect(matches, `Hardcoded duration values found: ${matches.join(", ")}`).toEqual([]);
106
+ });
107
+
108
+ it("CSS has no hardcoded easing values", () => {
109
+ const css = readFileSync(resolve(distDir, "css/strand-ui.css"), "utf-8");
110
+ const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, "");
111
+ // Strip var() references
112
+ const withoutVars = withoutComments.replace(/var\([^)]+\)/g, "VAR_REF");
113
+
114
+ const violations: string[] = [];
115
+
116
+ // Check for raw cubic-bezier()
117
+ const cubicBezierPattern = /cubic-bezier\([^)]+\)/g;
118
+ let match: RegExpExecArray | null;
119
+ while ((match = cubicBezierPattern.exec(withoutVars)) !== null) {
120
+ violations.push(match[0]);
121
+ }
122
+
123
+ // Check for bare easing keywords (not inside var() and not "linear")
124
+ // linear is acceptable for spinners/continuous rotation
125
+ const easingKeywordPattern = /\b(ease-in-out|ease-in|ease-out)\b/g;
126
+ while ((match = easingKeywordPattern.exec(withoutVars)) !== null) {
127
+ violations.push(match[0]);
128
+ }
129
+
130
+ expect(violations, `Hardcoded easing values found: ${violations.join(", ")}`).toEqual([]);
131
+ });
132
+
133
+ it("CSS has no hardcoded border-radius pixel values", () => {
134
+ const css = readFileSync(resolve(distDir, "css/strand-ui.css"), "utf-8");
135
+ const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, "");
136
+ // Strip var() references
137
+ const withoutVars = withoutComments.replace(/var\([^)]+\)/g, "VAR_REF");
138
+
139
+ // Match border-radius with raw pixel values
140
+ const borderRadiusPattern = /border-radius:\s*\d+px/g;
141
+ const matches: string[] = [];
142
+ let match: RegExpExecArray | null;
143
+ while ((match = borderRadiusPattern.exec(withoutVars)) !== null) {
144
+ matches.push(match[0]);
145
+ }
146
+
147
+ expect(matches, `Hardcoded border-radius values found: ${matches.join(", ")}`).toEqual([]);
148
+ });
149
+
150
+ it("All interactive component CSS files include :focus-visible", () => {
151
+ const interactiveComponents = [
152
+ "Button", "Link", "Card", "Checkbox", "Input", "Radio",
153
+ "Select", "Slider", "Switch", "Tabs", "Breadcrumb", "Nav", "Table",
154
+ ];
155
+
156
+ for (const name of interactiveComponents) {
157
+ const cssPath = resolve(__dirname, `../components/${name}/${name}.css`);
158
+ const css = readFileSync(cssPath, "utf-8");
159
+ // Compound inputs (Input, Select, Textarea) use :focus-within on the wrapper,
160
+ // which is equivalent for components with a visually hidden native input
161
+ const hasFocusHandling = css.includes(":focus-visible") || css.includes(":focus-within");
162
+ expect(hasFocusHandling, `${name}.css missing :focus-visible or :focus-within`).toBe(true);
163
+ }
164
+ });
165
+
166
+ it("All animated component CSS files include prefers-reduced-motion", () => {
167
+ const allComponents = [
168
+ "Alert", "Avatar", "Badge", "Breadcrumb", "Button", "Card",
169
+ "Checkbox", "Container", "DataReadout", "Dialog", "Divider",
170
+ "FormField", "Grid", "Input", "Link", "Nav", "Progress", "Radio",
171
+ "Section", "Select", "Skeleton", "Slider", "Spinner", "Stack",
172
+ "Switch", "Table", "Tabs", "Tag", "Textarea", "Toast", "Tooltip",
173
+ ];
174
+
175
+ for (const name of allComponents) {
176
+ const cssPath = resolve(__dirname, `../components/${name}/${name}.css`);
177
+ const css = readFileSync(cssPath, "utf-8");
178
+ const usesAnimation = /\banimation\b/.test(css) || /\btransition\b/.test(css);
179
+ if (usesAnimation) {
180
+ expect(css, `${name}.css uses animation/transition but missing prefers-reduced-motion`).toContain("prefers-reduced-motion");
181
+ }
182
+ }
183
+ });
184
+
185
+ it("All component CSS files start with MIT license banner", () => {
186
+ const allComponents = [
187
+ "Alert", "Avatar", "Badge", "Breadcrumb", "Button", "Card",
188
+ "Checkbox", "Container", "DataReadout", "Dialog", "Divider",
189
+ "FormField", "Grid", "Input", "Link", "Nav", "Progress", "Radio",
190
+ "Section", "Select", "Skeleton", "Slider", "Spinner", "Stack",
191
+ "Switch", "Table", "Tabs", "Tag", "Textarea", "Toast", "Tooltip",
192
+ ];
193
+
194
+ for (const name of allComponents) {
195
+ const cssPath = resolve(__dirname, `../components/${name}/${name}.css`);
196
+ const css = readFileSync(cssPath, "utf-8");
197
+ expect(css, `${name}.css missing MIT license banner`).toMatch(/^\/\*! Strand/);
198
+ }
199
+ });
200
+ });
@@ -0,0 +1,137 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+
5
+ const componentDir = resolve(__dirname, "../components");
6
+
7
+ const allComponents: { name: string; cssPath: string }[] = [
8
+ { name: "Alert", cssPath: resolve(componentDir, "Alert/Alert.css") },
9
+ { name: "Avatar", cssPath: resolve(componentDir, "Avatar/Avatar.css") },
10
+ { name: "Badge", cssPath: resolve(componentDir, "Badge/Badge.css") },
11
+ { name: "Breadcrumb", cssPath: resolve(componentDir, "Breadcrumb/Breadcrumb.css") },
12
+ { name: "Button", cssPath: resolve(componentDir, "Button/Button.css") },
13
+ { name: "Card", cssPath: resolve(componentDir, "Card/Card.css") },
14
+ { name: "Checkbox", cssPath: resolve(componentDir, "Checkbox/Checkbox.css") },
15
+ { name: "Container", cssPath: resolve(componentDir, "Container/Container.css") },
16
+ { name: "DataReadout", cssPath: resolve(componentDir, "DataReadout/DataReadout.css") },
17
+ { name: "Dialog", cssPath: resolve(componentDir, "Dialog/Dialog.css") },
18
+ { name: "Divider", cssPath: resolve(componentDir, "Divider/Divider.css") },
19
+ { name: "FormField", cssPath: resolve(componentDir, "FormField/FormField.css") },
20
+ { name: "Grid", cssPath: resolve(componentDir, "Grid/Grid.css") },
21
+ { name: "Input", cssPath: resolve(componentDir, "Input/Input.css") },
22
+ { name: "Link", cssPath: resolve(componentDir, "Link/Link.css") },
23
+ { name: "Nav", cssPath: resolve(componentDir, "Nav/Nav.css") },
24
+ { name: "Progress", cssPath: resolve(componentDir, "Progress/Progress.css") },
25
+ { name: "Radio", cssPath: resolve(componentDir, "Radio/Radio.css") },
26
+ { name: "Section", cssPath: resolve(componentDir, "Section/Section.css") },
27
+ { name: "Select", cssPath: resolve(componentDir, "Select/Select.css") },
28
+ { name: "Skeleton", cssPath: resolve(componentDir, "Skeleton/Skeleton.css") },
29
+ { name: "Slider", cssPath: resolve(componentDir, "Slider/Slider.css") },
30
+ { name: "Spinner", cssPath: resolve(componentDir, "Spinner/Spinner.css") },
31
+ { name: "Stack", cssPath: resolve(componentDir, "Stack/Stack.css") },
32
+ { name: "Switch", cssPath: resolve(componentDir, "Switch/Switch.css") },
33
+ { name: "Table", cssPath: resolve(componentDir, "Table/Table.css") },
34
+ { name: "Tabs", cssPath: resolve(componentDir, "Tabs/Tabs.css") },
35
+ { name: "Tag", cssPath: resolve(componentDir, "Tag/Tag.css") },
36
+ { name: "Textarea", cssPath: resolve(componentDir, "Textarea/Textarea.css") },
37
+ { name: "Toast", cssPath: resolve(componentDir, "Toast/Toast.css") },
38
+ { name: "Tooltip", cssPath: resolve(componentDir, "Tooltip/Tooltip.css") },
39
+ ];
40
+
41
+ describe("CSS design language compliance", () => {
42
+ it("No component CSS uses raw hex colors except known exceptions", () => {
43
+ // Known exceptions: rgba() for Tag tint backgrounds and Alert status backgrounds
44
+ // (opacity variants without tokens yet)
45
+ const rgbaExemptComponents = ["Tag", "Alert"];
46
+ const violations: string[] = [];
47
+
48
+ for (const { name, cssPath } of allComponents) {
49
+ const css = readFileSync(cssPath, "utf-8");
50
+ const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, "");
51
+ // Strip var() references
52
+ const withoutVars = withoutComments.replace(/var\([^)]+\)/g, "VAR_REF");
53
+
54
+ // For exempted components, also strip rgba() calls
55
+ let searchable = withoutVars;
56
+ if (rgbaExemptComponents.includes(name)) {
57
+ searchable = searchable.replace(/rgba\([^)]+\)/g, "RGBA_REF");
58
+ }
59
+
60
+ const hexPattern = /#[0-9a-fA-F]{3,8}\b/g;
61
+ let match: RegExpExecArray | null;
62
+ while ((match = hexPattern.exec(searchable)) !== null) {
63
+ violations.push(`${name}: ${match[0]}`);
64
+ }
65
+ }
66
+
67
+ expect(violations, `Raw hex colors found:\n${violations.join("\n")}`).toEqual([]);
68
+ });
69
+
70
+ it("All component CSS uses only --strand- prefixed custom properties", () => {
71
+ const violations: string[] = [];
72
+
73
+ for (const { name, cssPath } of allComponents) {
74
+ const css = readFileSync(cssPath, "utf-8");
75
+ const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, "");
76
+
77
+ // Find all var() references
78
+ const varPattern = /var\(--([^)]+)\)/g;
79
+ let match: RegExpExecArray | null;
80
+ while ((match = varPattern.exec(withoutComments)) !== null) {
81
+ const propName = match[1];
82
+ if (!propName.startsWith("strand-")) {
83
+ violations.push(`${name}: var(--${propName})`);
84
+ }
85
+ }
86
+ }
87
+
88
+ expect(
89
+ violations,
90
+ `Non --strand- prefixed custom properties found:\n${violations.join("\n")}`,
91
+ ).toEqual([]);
92
+ });
93
+
94
+ it("Interactive components have correct hover transform", () => {
95
+ // Button and Card (interactive) should have translateY(-1px) or translateY(-2px)
96
+ const interactiveSpecs: { name: string; cssPath: string }[] = [
97
+ { name: "Button", cssPath: resolve(componentDir, "Button/Button.css") },
98
+ { name: "Card", cssPath: resolve(componentDir, "Card/Card.css") },
99
+ ];
100
+
101
+ for (const { name, cssPath } of interactiveSpecs) {
102
+ const css = readFileSync(cssPath, "utf-8");
103
+ const hasHoverTransform =
104
+ css.includes("translateY(-1px)") || css.includes("translateY(-2px)");
105
+ expect(
106
+ hasHoverTransform,
107
+ `${name} missing hover translateY(-1px) or translateY(-2px)`,
108
+ ).toBe(true);
109
+ }
110
+ });
111
+
112
+ it("Disabled states use opacity 0.4", () => {
113
+ // Components with :disabled or --disabled class
114
+ const componentsWithDisabled: { name: string; cssPath: string }[] = [
115
+ { name: "Button", cssPath: resolve(componentDir, "Button/Button.css") },
116
+ { name: "Checkbox", cssPath: resolve(componentDir, "Checkbox/Checkbox.css") },
117
+ { name: "Input", cssPath: resolve(componentDir, "Input/Input.css") },
118
+ { name: "Radio", cssPath: resolve(componentDir, "Radio/Radio.css") },
119
+ { name: "Select", cssPath: resolve(componentDir, "Select/Select.css") },
120
+ { name: "Slider", cssPath: resolve(componentDir, "Slider/Slider.css") },
121
+ { name: "Switch", cssPath: resolve(componentDir, "Switch/Switch.css") },
122
+ { name: "Textarea", cssPath: resolve(componentDir, "Textarea/Textarea.css") },
123
+ ];
124
+
125
+ for (const { name, cssPath } of componentsWithDisabled) {
126
+ const css = readFileSync(cssPath, "utf-8");
127
+ const hasDisabled = css.includes(":disabled") || css.includes("--disabled");
128
+ expect(hasDisabled, `${name} expected to have disabled styles`).toBe(true);
129
+
130
+ // Verify opacity: 0.4 is present
131
+ expect(
132
+ css,
133
+ `${name} disabled state should use opacity: 0.4`,
134
+ ).toContain("opacity: 0.4");
135
+ }
136
+ });
137
+ });
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { render } from "@testing-library/preact";
3
+ import { Button } from "../components/Button/index.js";
4
+
5
+ describe("strand-static presentation mode", () => {
6
+ it("CSS file exists and is included in build output", async () => {
7
+ const fs = await import("node:fs");
8
+ const path = await import("node:path");
9
+ const staticPath = path.resolve(__dirname, "../../dist/css/strand-ui.css");
10
+ const content = fs.readFileSync(staticPath, "utf-8");
11
+ expect(content).toContain(".strand-static");
12
+ expect(content).toContain("pointer-events: none");
13
+ });
14
+
15
+ it("disabled button inside .strand-static has opacity override class", async () => {
16
+ const fs = await import("node:fs");
17
+ const path = await import("node:path");
18
+ const staticPath = path.resolve(__dirname, "../../dist/css/strand-ui.css");
19
+ const content = fs.readFileSync(staticPath, "utf-8");
20
+ expect(content).toContain(".strand-static [disabled]");
21
+ expect(content).toContain("opacity: 1");
22
+ });
23
+
24
+ it("strand-static overrides toast position", async () => {
25
+ const fs = await import("node:fs");
26
+ const path = await import("node:path");
27
+ const staticPath = path.resolve(__dirname, "../../dist/css/strand-ui.css");
28
+ const content = fs.readFileSync(staticPath, "utf-8");
29
+ expect(content).toContain(".strand-static .strand-toast");
30
+ expect(content).toContain("position: static");
31
+ });
32
+ });
33
+
34
+ describe("layout utility classes", () => {
35
+ it("Stack gap utilities exist in build output", async () => {
36
+ const fs = await import("node:fs");
37
+ const path = await import("node:path");
38
+ const cssPath = path.resolve(__dirname, "../../dist/css/strand-ui.css");
39
+ const content = fs.readFileSync(cssPath, "utf-8");
40
+ expect(content).toContain(".strand-stack--gap-4");
41
+ expect(content).toContain("var(--strand-space-4)");
42
+ });
43
+
44
+ it("Grid column utilities exist in build output", async () => {
45
+ const fs = await import("node:fs");
46
+ const path = await import("node:path");
47
+ const cssPath = path.resolve(__dirname, "../../dist/css/strand-ui.css");
48
+ const content = fs.readFileSync(cssPath, "utf-8");
49
+ expect(content).toContain(".strand-grid--cols-3");
50
+ expect(content).toContain("repeat(3, 1fr)");
51
+ });
52
+
53
+ it("Grid gap utilities exist in build output", async () => {
54
+ const fs = await import("node:fs");
55
+ const path = await import("node:path");
56
+ const cssPath = path.resolve(__dirname, "../../dist/css/strand-ui.css");
57
+ const content = fs.readFileSync(cssPath, "utf-8");
58
+ expect(content).toContain(".strand-grid--gap-4");
59
+ });
60
+ });
@@ -0,0 +1,75 @@
1
+ /*! Strand UI | MIT License | dillingerstaffing.com */
2
+
3
+ /* ── Base ── */
4
+ .strand-alert {
5
+ position: relative;
6
+ display: flex;
7
+ align-items: flex-start;
8
+ justify-content: space-between;
9
+ width: 100%;
10
+ padding: var(--strand-space-6);
11
+ padding-left: var(--strand-space-5);
12
+ border-radius: var(--strand-radius-md);
13
+ border-left: 4px solid transparent;
14
+ font-family: var(--strand-font-sans);
15
+ font-size: var(--strand-text-sm);
16
+ }
17
+
18
+ /* ── Status variants ── */
19
+ .strand-alert--info {
20
+ background: var(--strand-blue-glow);
21
+ border-left-color: var(--strand-blue-primary);
22
+ }
23
+
24
+ .strand-alert--success {
25
+ background: rgba(34, 197, 94, 0.08);
26
+ border-left-color: var(--strand-green-positive);
27
+ }
28
+
29
+ .strand-alert--warning {
30
+ background: rgba(245, 158, 11, 0.08);
31
+ border-left-color: var(--strand-amber-caution);
32
+ }
33
+
34
+ .strand-alert--error {
35
+ background: rgba(239, 68, 68, 0.08);
36
+ border-left-color: var(--strand-red-alert);
37
+ }
38
+
39
+ /* ── Content ── */
40
+ .strand-alert__content {
41
+ flex: 1;
42
+ min-width: 0;
43
+ }
44
+
45
+ /* ── Dismiss button ── */
46
+ .strand-alert__dismiss {
47
+ flex-shrink: 0;
48
+ display: inline-flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ width: 24px;
52
+ height: 24px;
53
+ margin-left: var(--strand-space-4);
54
+ padding: 0;
55
+ border: none;
56
+ border-radius: var(--strand-radius-md);
57
+ background: transparent;
58
+ color: var(--strand-gray-500);
59
+ font-size: var(--strand-text-base);
60
+ cursor: pointer;
61
+ transition: background var(--strand-duration-fast) var(--strand-ease-out-quart),
62
+ color var(--strand-duration-fast) var(--strand-ease-out-quart);
63
+ }
64
+
65
+ .strand-alert__dismiss:hover {
66
+ background: var(--strand-gray-100);
67
+ color: var(--strand-gray-600);
68
+ }
69
+
70
+ /* ── Reduced motion ── */
71
+ @media (prefers-reduced-motion: reduce) {
72
+ .strand-alert__dismiss {
73
+ transition: none;
74
+ }
75
+ }