@fpkit/acss 0.4.4

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 (297) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/chunk-77CZU5XZ.cjs +9 -0
  4. package/dist/chunk-77CZU5XZ.cjs.map +1 -0
  5. package/dist/chunk-D43FJIRQ.cjs +31 -0
  6. package/dist/chunk-D43FJIRQ.cjs.map +1 -0
  7. package/dist/chunk-GJWMCDFS.js +9 -0
  8. package/dist/chunk-GJWMCDFS.js.map +1 -0
  9. package/dist/chunk-PCDUGD3C.js +5 -0
  10. package/dist/chunk-PCDUGD3C.js.map +1 -0
  11. package/dist/hooks.cjs +10 -0
  12. package/dist/hooks.cjs.map +1 -0
  13. package/dist/hooks.d.cts +32 -0
  14. package/dist/hooks.d.ts +32 -0
  15. package/dist/hooks.js +8 -0
  16. package/dist/hooks.js.map +1 -0
  17. package/dist/icon-e6044c73.d.ts +227 -0
  18. package/dist/icons.cjs +73 -0
  19. package/dist/icons.cjs.map +1 -0
  20. package/dist/icons.d.cts +252 -0
  21. package/dist/icons.d.ts +252 -0
  22. package/dist/icons.js +4 -0
  23. package/dist/icons.js.map +1 -0
  24. package/dist/index.cjs +59 -0
  25. package/dist/index.cjs.map +1 -0
  26. package/dist/index.d.cts +566 -0
  27. package/dist/index.d.ts +566 -0
  28. package/dist/index.js +11 -0
  29. package/dist/index.js.map +1 -0
  30. package/libs/chunk-GCGKYLDG.js +7 -0
  31. package/libs/chunk-GCGKYLDG.js.map +1 -0
  32. package/libs/chunk-PDD4N5P5.cjs +10 -0
  33. package/libs/chunk-PDD4N5P5.cjs.map +1 -0
  34. package/libs/chunk-QHIABQNQ.js +8 -0
  35. package/libs/chunk-QHIABQNQ.js.map +1 -0
  36. package/libs/chunk-ZOHIKF6I.cjs +31 -0
  37. package/libs/chunk-ZOHIKF6I.cjs.map +1 -0
  38. package/libs/components/badge/badge.css +1 -0
  39. package/libs/components/badge/badge.css.map +1 -0
  40. package/libs/components/badge/badge.min.css +3 -0
  41. package/libs/components/breadcrumbs/breadcrumb.css +1 -0
  42. package/libs/components/breadcrumbs/breadcrumb.css.map +1 -0
  43. package/libs/components/breadcrumbs/breadcrumb.min.css +3 -0
  44. package/libs/components/buttons/button.css +1 -0
  45. package/libs/components/buttons/button.css.map +1 -0
  46. package/libs/components/buttons/button.min.css +3 -0
  47. package/libs/components/cards/card-style.css +1 -0
  48. package/libs/components/cards/card-style.css.map +1 -0
  49. package/libs/components/cards/card-style.min.css +3 -0
  50. package/libs/components/cards/card.css +1 -0
  51. package/libs/components/cards/card.css.map +1 -0
  52. package/libs/components/cards/card.min.css +3 -0
  53. package/libs/components/details/details.css +1 -0
  54. package/libs/components/details/details.css.map +1 -0
  55. package/libs/components/details/details.min.css +3 -0
  56. package/libs/components/form/form.css +1 -0
  57. package/libs/components/form/form.css.map +1 -0
  58. package/libs/components/form/form.min.css +3 -0
  59. package/libs/components/icons/icon.css +1 -0
  60. package/libs/components/icons/icon.css.map +1 -0
  61. package/libs/components/icons/icon.min.css +3 -0
  62. package/libs/components/images/img.css +1 -0
  63. package/libs/components/images/img.css.map +1 -0
  64. package/libs/components/images/img.min.css +3 -0
  65. package/libs/components/layout/landmarks.css +1 -0
  66. package/libs/components/layout/landmarks.css.map +1 -0
  67. package/libs/components/layout/landmarks.min.css +3 -0
  68. package/libs/components/link/link.css +1 -0
  69. package/libs/components/link/link.css.map +1 -0
  70. package/libs/components/link/link.min.css +3 -0
  71. package/libs/components/nav/nav.css +1 -0
  72. package/libs/components/nav/nav.css.map +1 -0
  73. package/libs/components/nav/nav.min.css +3 -0
  74. package/libs/components/progress/progress.css +1 -0
  75. package/libs/components/progress/progress.css.map +1 -0
  76. package/libs/components/progress/progress.min.css +3 -0
  77. package/libs/components/styles/index.css +1 -0
  78. package/libs/components/styles/index.css.map +1 -0
  79. package/libs/components/styles/index.min.css +3 -0
  80. package/libs/components/tag/tag.css +1 -0
  81. package/libs/components/tag/tag.css.map +1 -0
  82. package/libs/components/tag/tag.min.css +3 -0
  83. package/libs/components/text-to-speech/text-to-speech.css +1 -0
  84. package/libs/components/text-to-speech/text-to-speech.css.map +1 -0
  85. package/libs/components/text-to-speech/text-to-speech.min.css +3 -0
  86. package/libs/hooks.cjs +12 -0
  87. package/libs/hooks.cjs.map +1 -0
  88. package/libs/hooks.d.cts +32 -0
  89. package/libs/hooks.d.ts +32 -0
  90. package/libs/hooks.js +3 -0
  91. package/libs/hooks.js.map +1 -0
  92. package/libs/icons-1f5afc0c.d.ts +318 -0
  93. package/libs/icons.cjs +12 -0
  94. package/libs/icons.cjs.map +1 -0
  95. package/libs/icons.d.cts +2 -0
  96. package/libs/icons.d.ts +2 -0
  97. package/libs/icons.js +3 -0
  98. package/libs/icons.js.map +1 -0
  99. package/libs/index.cjs +71 -0
  100. package/libs/index.cjs.map +1 -0
  101. package/libs/index.css +1 -0
  102. package/libs/index.css.map +1 -0
  103. package/libs/index.d.cts +551 -0
  104. package/libs/index.d.ts +551 -0
  105. package/libs/index.js +11 -0
  106. package/libs/index.js.map +1 -0
  107. package/package.json +125 -0
  108. package/src/App.css +42 -0
  109. package/src/App.tsx +35 -0
  110. package/src/__snapshots__/App.test.tsx.snap +56 -0
  111. package/src/components/.gitkeep +0 -0
  112. package/src/components/__snapshots__/fp.test.tsx.snap +3 -0
  113. package/src/components/badge/badge.scss +20 -0
  114. package/src/components/badge/badge.stories.tsx +54 -0
  115. package/src/components/badge/badge.tsx +17 -0
  116. package/src/components/breadcrumbs/bc-item.tsx +20 -0
  117. package/src/components/breadcrumbs/breadcrumb.scss +35 -0
  118. package/src/components/breadcrumbs/breadcrumb.stories.tsx +92 -0
  119. package/src/components/breadcrumbs/breadcrumb.tsx +218 -0
  120. package/src/components/buttons/button.scss +115 -0
  121. package/src/components/buttons/button.stories.tsx +57 -0
  122. package/src/components/buttons/button.test.tsx +104 -0
  123. package/src/components/buttons/button.tsx +64 -0
  124. package/src/components/cards/card-style.scss +0 -0
  125. package/src/components/cards/card.scss +43 -0
  126. package/src/components/cards/card.stories.tsx +114 -0
  127. package/src/components/cards/card.test.tsx +30 -0
  128. package/src/components/cards/card.tsx +135 -0
  129. package/src/components/cards/flex-card.tsx +15 -0
  130. package/src/components/details/details.scss +75 -0
  131. package/src/components/details/details.stories.tsx +122 -0
  132. package/src/components/details/details.tsx +77 -0
  133. package/src/components/form/README.mdx +70 -0
  134. package/src/components/form/fields.tsx +45 -0
  135. package/src/components/form/form.scss +87 -0
  136. package/src/components/form/form.stories.tsx +49 -0
  137. package/src/components/form/form.tsx +71 -0
  138. package/src/components/form/input.stories.tsx +155 -0
  139. package/src/components/form/inputs.tsx +84 -0
  140. package/src/components/form/select.stories.tsx +38 -0
  141. package/src/components/form/select.tsx +112 -0
  142. package/src/components/form/textarea.tsx +87 -0
  143. package/src/components/fp.test.tsx +56 -0
  144. package/src/components/fp.tsx +78 -0
  145. package/src/components/heading/heading.stories.tsx +75 -0
  146. package/src/components/heading/heading.tsx +27 -0
  147. package/src/components/icons/components/add.tsx +42 -0
  148. package/src/components/icons/components/arrow-down.tsx +52 -0
  149. package/src/components/icons/components/arrow-left.tsx +49 -0
  150. package/src/components/icons/components/arrow-right.tsx +52 -0
  151. package/src/components/icons/components/arrow-up.tsx +49 -0
  152. package/src/components/icons/components/chat.tsx +44 -0
  153. package/src/components/icons/components/code.tsx +50 -0
  154. package/src/components/icons/components/copy.tsx +51 -0
  155. package/src/components/icons/components/down.tsx +33 -0
  156. package/src/components/icons/components/home.tsx +57 -0
  157. package/src/components/icons/components/left.tsx +43 -0
  158. package/src/components/icons/components/minus.tsx +42 -0
  159. package/src/components/icons/components/pause-solid.tsx +48 -0
  160. package/src/components/icons/components/pause.tsx +63 -0
  161. package/src/components/icons/components/play-solid.tsx +44 -0
  162. package/src/components/icons/components/play.tsx +51 -0
  163. package/src/components/icons/components/remove.tsx +42 -0
  164. package/src/components/icons/components/resume-solid.tsx +52 -0
  165. package/src/components/icons/components/resume.tsx +57 -0
  166. package/src/components/icons/components/right.tsx +43 -0
  167. package/src/components/icons/components/star.tsx +38 -0
  168. package/src/components/icons/components/stop-solid.tsx +44 -0
  169. package/src/components/icons/components/stop.tsx +54 -0
  170. package/src/components/icons/components/svg.tsx +44 -0
  171. package/src/components/icons/components/up.tsx +31 -0
  172. package/src/components/icons/components/user.tsx +46 -0
  173. package/src/components/icons/icon.scss +15 -0
  174. package/src/components/icons/icon.stories.tsx +208 -0
  175. package/src/components/icons/icon.tsx +100 -0
  176. package/src/components/icons/index.ts +29 -0
  177. package/src/components/icons/types.ts +12 -0
  178. package/src/components/images/README.mdx +43 -0
  179. package/src/components/images/figure.stories.tsx +34 -0
  180. package/src/components/images/figure.tsx +44 -0
  181. package/src/components/images/img.scss +43 -0
  182. package/src/components/images/img.stories.tsx +24 -0
  183. package/src/components/images/img.test.tsx +43 -0
  184. package/src/components/images/img.tsx +93 -0
  185. package/src/components/images/place-holder.png +0 -0
  186. package/src/components/kit.tsx +56 -0
  187. package/src/components/layout/_header.scss +72 -0
  188. package/src/components/layout/footer.stories.tsx +34 -0
  189. package/src/components/layout/landmarks.scss +51 -0
  190. package/src/components/layout/landmarks.stories.tsx +54 -0
  191. package/src/components/layout/landmarks.tsx +149 -0
  192. package/src/components/layout/main.stories.tsx +90 -0
  193. package/src/components/link/link.scss +92 -0
  194. package/src/components/link/link.stories.tsx +74 -0
  195. package/src/components/link/link.tsx +48 -0
  196. package/src/components/list/list.stories.tsx +52 -0
  197. package/src/components/list/list.tsx +74 -0
  198. package/src/components/modal/dialog.tsx +50 -0
  199. package/src/components/modal/modal.tsx +85 -0
  200. package/src/components/nav/nav.scss +90 -0
  201. package/src/components/nav/nav.stories.tsx +96 -0
  202. package/src/components/nav/nav.tsx +76 -0
  203. package/src/components/popover/node_modules/.vitest/results.json +1 -0
  204. package/src/components/popover/popover.stories.tsx +31 -0
  205. package/src/components/popover/popover.test.tsx +39 -0
  206. package/src/components/popover/popover.tsx +85 -0
  207. package/src/components/progress/progress.scss +70 -0
  208. package/src/components/progress/progress.stories.tsx +51 -0
  209. package/src/components/progress/progress.tsx +82 -0
  210. package/src/components/readme.stories.mdx +7 -0
  211. package/src/components/styles/index.css +520 -0
  212. package/src/components/styles/index.css.map +1 -0
  213. package/src/components/tables/table-elements.tsx +57 -0
  214. package/src/components/tables/table.tsx +57 -0
  215. package/src/components/tag/tag.scss +56 -0
  216. package/src/components/tag/tag.stories.tsx +39 -0
  217. package/src/components/tag/tag.tsx +25 -0
  218. package/src/components/text/text.stories.tsx +67 -0
  219. package/src/components/text/text.tsx +93 -0
  220. package/src/components/text-to-speech/README.mdx +192 -0
  221. package/src/components/text-to-speech/TextInput.tsx +19 -0
  222. package/src/components/text-to-speech/TextToSpeech.stories.tsx +145 -0
  223. package/src/components/text-to-speech/TextToSpeech.tsx +94 -0
  224. package/src/components/text-to-speech/text-to-speech.scss +31 -0
  225. package/src/components/text-to-speech/useTextToSpeech.mdx +182 -0
  226. package/src/components/text-to-speech/useTextToSpeech.tsx +176 -0
  227. package/src/components/text-to-speech/views/TextToSpeechControls.tsx +117 -0
  228. package/src/components/ui.tsx +67 -0
  229. package/src/favicon.svg +15 -0
  230. package/src/hooks/popover/__snapshots__/popover.test.tsx.snap +88 -0
  231. package/src/hooks/popover/node_modules/.vitest/results.json +1 -0
  232. package/src/hooks/popover/popover.tsx +71 -0
  233. package/src/hooks/popover/use-popover.tsx +83 -0
  234. package/src/hooks.ts +1 -0
  235. package/src/icons.ts +1 -0
  236. package/src/index.css +13 -0
  237. package/src/index.scss +19 -0
  238. package/src/index.ts +35 -0
  239. package/src/libs/content.ts +30 -0
  240. package/src/logo.svg +7 -0
  241. package/src/main.tsx +10 -0
  242. package/src/patterns/.gitkeep +0 -0
  243. package/src/patterns/page/page-header.stories.tsx +44 -0
  244. package/src/patterns/page/page-header.tsx +78 -0
  245. package/src/sass/_elements.scss +17 -0
  246. package/src/sass/_globals.scss +162 -0
  247. package/src/sass/_layout.scss +51 -0
  248. package/src/sass/_loading-animation.scss +35 -0
  249. package/src/sass/_mixins.scss +10 -0
  250. package/src/sass/_properties.scss +106 -0
  251. package/src/sass/_reset.scss +183 -0
  252. package/src/sass/_type.scss +43 -0
  253. package/src/setupTest.ts +1 -0
  254. package/src/styles/badge/badge.css +22 -0
  255. package/src/styles/badge/badge.css.map +1 -0
  256. package/src/styles/breadcrumbs/breadcrumb.css +42 -0
  257. package/src/styles/breadcrumbs/breadcrumb.css.map +1 -0
  258. package/src/styles/buttons/button.css +93 -0
  259. package/src/styles/buttons/button.css.map +1 -0
  260. package/src/styles/cards/card-style.css +3 -0
  261. package/src/styles/cards/card-style.css.map +1 -0
  262. package/src/styles/cards/card.css +48 -0
  263. package/src/styles/cards/card.css.map +1 -0
  264. package/src/styles/details/details.css +69 -0
  265. package/src/styles/details/details.css.map +1 -0
  266. package/src/styles/dropdowns/dropdown.css.map +1 -0
  267. package/src/styles/form/form.css +93 -0
  268. package/src/styles/form/form.css.map +1 -0
  269. package/src/styles/form/style.css.map +1 -0
  270. package/src/styles/icons/icon.css +16 -0
  271. package/src/styles/icons/icon.css.map +1 -0
  272. package/src/styles/images/img.css +42 -0
  273. package/src/styles/images/img.css.map +1 -0
  274. package/src/styles/index.css +1330 -0
  275. package/src/styles/index.css.map +1 -0
  276. package/src/styles/layout/landmarks.css +155 -0
  277. package/src/styles/layout/landmarks.css.map +1 -0
  278. package/src/styles/link/link.css +88 -0
  279. package/src/styles/link/link.css.map +1 -0
  280. package/src/styles/nav/nav.css +85 -0
  281. package/src/styles/nav/nav.css.map +1 -0
  282. package/src/styles/progress/progress.css +54 -0
  283. package/src/styles/progress/progress.css.map +1 -0
  284. package/src/styles/progress/sass/progress.css.map +1 -0
  285. package/src/styles/styles/index.css +562 -0
  286. package/src/styles/styles/index.css.map +1 -0
  287. package/src/styles/tag/badge.css.map +1 -0
  288. package/src/styles/tag/tag.css +71 -0
  289. package/src/styles/tag/tag.css.map +1 -0
  290. package/src/styles/text-to-speech/text-to-speech.css +32 -0
  291. package/src/styles/text-to-speech/text-to-speech.css.map +1 -0
  292. package/src/test/setup.ts +6 -0
  293. package/src/types/component-props.ts +36 -0
  294. package/src/types/index.ts +2 -0
  295. package/src/types/input-props.ts +28 -0
  296. package/src/types/shared.ts +57 -0
  297. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,43 @@
1
+ import { Img } from './img'
2
+ import React from 'react'
3
+ import { fireEvent, render, screen } from '@testing-library/react'
4
+ import jest from 'jest-mock'
5
+ describe('Img', () => {
6
+ it('should render an img element with passed props', () => {
7
+ const src = 'test.jpg'
8
+ const alt = 'Test image'
9
+ const width = 100
10
+
11
+ const { container } = render(<Img src={src} alt={alt} width={width} />)
12
+
13
+ const img = screen.getByRole('img')
14
+ expect(img).toBeInTheDocument()
15
+ expect(img).toHaveAttribute('src', src)
16
+ expect(img).toHaveAttribute('alt', alt)
17
+ expect(img).toHaveAttribute('width', width.toString())
18
+ })
19
+
20
+ it('should apply default styles when renderStyles is true', () => {
21
+ const { container } = render(<Img src="" alt="" />)
22
+ })
23
+
24
+ it('should call imgError callback on error', () => {
25
+ const onError = jest.fn()
26
+ render(<Img src="bad.jpg" alt="" imgError={onError} />)
27
+
28
+ const img = screen.getByRole('img')
29
+ fireEvent.error(img)
30
+
31
+ expect(onError).toHaveBeenCalledTimes(1)
32
+ })
33
+
34
+ it('should call imgLoaded callback on load', () => {
35
+ const onLoad = jest.fn()
36
+ render(<Img src="good.jpg" alt="" imgLoaded={onLoad} />)
37
+
38
+ const img = screen.getByRole('img')
39
+ fireEvent.load(img)
40
+
41
+ expect(onLoad).toHaveBeenCalledTimes(1)
42
+ })
43
+ })
@@ -0,0 +1,93 @@
1
+ import UI from '../ui'
2
+ import React from 'react'
3
+ /*
4
+ * ImageProps interface
5
+ *
6
+ * Extends ComponentProps and defines additional props for the Img component.
7
+ *
8
+ * @property {string} [src] - The image source URL
9
+ * @property {string} alt - Required alt text for image accessibility
10
+ * @property {number} width - Required width of image
11
+ * @property {number} [height] - Optional height of image
12
+ * @property {"eager" | "lazy"} [loading="lazy"] - Loading behavior
13
+ * @property {string} [placeholder] - Fallback placeholder image
14
+ * @property {"high" | "low"} [fetchpriority="low"] - Image fetch priority
15
+ * @property {"sync" | "async" | "auto"} [decoding="auto"] - Decode setting
16
+ * @property {function} [imgError] - Error callback
17
+ * @property {function} [imgLoaded] - Loaded callback
18
+ */
19
+ export type ImageProps = React.ComponentProps<'img'> &
20
+ React.ComponentProps<typeof UI>
21
+
22
+ /*
23
+ * Img component
24
+ *
25
+ * Renders an <img> element with custom props.
26
+ *
27
+ * @param {string} src - The image source URL.
28
+ * @param {string} alt - The alt text for the image.
29
+ * @param {number} [width=480] - The width of the image.
30
+ * @param {number} [height] - The height of the image.
31
+ * @param {Object} [styles] - Additional CSS styles to apply.
32
+ * @param {boolean} [renderStyles=true] - Whether to render the default styles.
33
+ * @param {"eager" | "lazy"} [loading="lazy"] - The loading attribute.
34
+ * @param {string} [placeholder] - A placeholder image URL.
35
+ * @param {"high" | "low"} [fetchpriority="low"] - The fetchpriority attribute.
36
+ * @param {"sync" | "async" | "auto"} [decoding="auto"] - The decoding attribute.
37
+ * @param {function} [imgLoaded] - Callback when image loads successfully.
38
+ * @param {function} [imgError] - Callback when image errors.
39
+ *
40
+ * @returns {JSX.Element} The Img component.
41
+ */
42
+ export const Img = ({
43
+ src = '//',
44
+ alt,
45
+ width = 480,
46
+ height,
47
+ styles,
48
+ loading = 'lazy',
49
+ placeholder = `https://via.placeholder.com/${width}?text=PLACEHOLDER`,
50
+ fetchpriority = 'low',
51
+ decoding = 'auto',
52
+ imgLoaded,
53
+ imgError,
54
+ ...props
55
+ }: ImageProps) => {
56
+ const handleImgError = (
57
+ e: React.SyntheticEvent<HTMLImageElement, Event>,
58
+ ): void => {
59
+ if (imgError) {
60
+ imgError?.(e)
61
+ return
62
+ }
63
+ if (e.currentTarget.src !== placeholder) {
64
+ e.currentTarget.src = placeholder
65
+ }
66
+ }
67
+
68
+ const handleImgLoad = (
69
+ e: React.SyntheticEvent<HTMLImageElement, Event>,
70
+ ): void => {
71
+ imgLoaded?.(e)
72
+ }
73
+
74
+ return (
75
+ <UI
76
+ as="img"
77
+ src={src}
78
+ alt={alt}
79
+ width={width}
80
+ height={height || 'auto'}
81
+ loading={loading}
82
+ style={styles}
83
+ onError={handleImgError}
84
+ onLoad={handleImgLoad}
85
+ fetchPriority={fetchpriority}
86
+ decoding={decoding}
87
+ {...props}
88
+ />
89
+ )
90
+ }
91
+
92
+ export default Img
93
+ Img.displayName = 'Img'
@@ -0,0 +1,56 @@
1
+ import React from 'react'
2
+
3
+ type Rainbow =
4
+ | 'red'
5
+ | 'orange'
6
+ | 'yellow'
7
+ | 'green'
8
+ | 'blue'
9
+ | 'indigo'
10
+ | 'violet'
11
+
12
+ type PolymorphicRef<C extends React.ElementType> =
13
+ React.ComponentPropsWithRef<C>['ref']
14
+
15
+ type AsProp<C extends React.ElementType> = {
16
+ as?: C
17
+ }
18
+
19
+ type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P)
20
+
21
+ type PolymorphicComponentProp<
22
+ C extends React.ElementType,
23
+ Props = {},
24
+ > = React.PropsWithChildren<Props & AsProp<C>> &
25
+ Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>
26
+
27
+ type PolymorphicComponentPropWithRef<
28
+ C extends React.ElementType,
29
+ Props = {},
30
+ > = PolymorphicComponentProp<C, Props> & { ref?: PolymorphicRef<C> }
31
+
32
+ type TextProps<C extends React.ElementType> = PolymorphicComponentPropWithRef<
33
+ C,
34
+ { color?: Rainbow | 'black' }
35
+ >
36
+
37
+ type TextComponent = <C extends React.ElementType = 'span'>(
38
+ props: TextProps<C>,
39
+ ) => React.ReactElement | any
40
+
41
+ export const Text: TextComponent = React.forwardRef(
42
+ <C extends React.ElementType = 'span'>(
43
+ { as, color, children }: TextProps<C>,
44
+ ref?: PolymorphicRef<C>,
45
+ ) => {
46
+ const Component = as || 'span'
47
+
48
+ const style = color ? { style: { color } } : {}
49
+
50
+ return (
51
+ <Component {...style} ref={ref}>
52
+ {children}
53
+ </Component>
54
+ )
55
+ },
56
+ )
@@ -0,0 +1,72 @@
1
+ header,
2
+ [data-hero],
3
+ [data-grid~='overlay'] {
4
+ // Name of the grid area
5
+ --overlay-grid-area: overlay;
6
+ // Placement of items in grid area
7
+ --overlay-placement: center;
8
+ // Controls display of grid
9
+ --overlay-display: grid;
10
+ // Overlay padding
11
+ --overlay-padding: 2rem;
12
+ --overlay-w: 100%;
13
+ --overlay-h: 40vh;
14
+ --overlay-max-h: 500px;
15
+ --overlay-color: currentColor;
16
+ --overlay-content-w: 80%;
17
+ --overlay-gap: 2rem;
18
+ --overlay-bg: whitesmoke;
19
+ --overlay-px: auto;
20
+ --overlay-py: auto;
21
+ --overlay-mx: auto;
22
+ --overlay-my: auto;
23
+
24
+ grid-template-areas: 'overlay';
25
+ display: var(--overlay-display);
26
+ place-items: var(--overlay-placement);
27
+ align-items: var(--overlay-placement);
28
+ min-height: var(--overlay-h);
29
+ width: var(--overlay-w);
30
+ color: var(--overlay-color);
31
+ background-color: var(--overlay-bg);
32
+ min-width: 20rem;
33
+ > * {
34
+ grid-area: overlay;
35
+ }
36
+ > img {
37
+ width: var(--overlay-w);
38
+ // height: auto;
39
+ background-size: contain;
40
+ }
41
+ > div,
42
+ > section {
43
+ --overlay-display: flex;
44
+ max-width: var(--overlay-content-w);
45
+ padding-inline: var(--spc-4);
46
+ margin-inline: var(--overlay-mx);
47
+ gap: var(--overlay-gap);
48
+ text-align: center;
49
+ p {
50
+ width: auto;
51
+ max-width: 60ch;
52
+ font-size: var(--fs-8);
53
+ line-height: 1.4;
54
+ }
55
+ > h1,
56
+ > h2 {
57
+ line-height: var(--header-lh, 1.1);
58
+ font-weight: 500;
59
+ }
60
+
61
+ > h1 {
62
+ font-size: var(--fs-12);
63
+ }
64
+ > h2 {
65
+ font-size: var(--fs-11);
66
+ }
67
+ > h3 {
68
+ font-size: var(--fs-10);
69
+ }
70
+
71
+ }
72
+ }
@@ -0,0 +1,34 @@
1
+ import { StoryObj, Meta } from '@storybook/react'
2
+ /**
3
+ * Import testing library dependencies
4
+ */
5
+ import { within, userEvent } from '@storybook/testing-library'
6
+
7
+ /**
8
+ * Import jest matchers
9
+ */
10
+ import { expect } from '@storybook/jest'
11
+
12
+ import { Footer } from './landmarks'
13
+
14
+ const meta: Meta<typeof Footer> = {
15
+ title: 'FP.React Components/Layout/Landmarks',
16
+ component: Footer,
17
+ args: {
18
+ children: 'Main Landmark',
19
+ // @ts-ignore
20
+ 'data-testid': 'main',
21
+ },
22
+ } as Meta
23
+
24
+ const mainChildren = () => (
25
+ <>
26
+ <h2>Header Title</h2>
27
+ <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero, unde?</p>
28
+ </>
29
+ )
30
+
31
+ export default meta
32
+ type Story = StoryObj<typeof Footer>
33
+
34
+ export const BasicFooter: Story = {}
@@ -0,0 +1,51 @@
1
+ @use './header';
2
+
3
+
4
+ main,
5
+ footer {
6
+ --content-w: min(100%, 1480px);
7
+ --content-mx: auto;
8
+ --content-px: 1rem;
9
+ --content-gap: 2rem;
10
+ padding-block: var(--overlay-padding);
11
+ > section {
12
+ width: var(--content-w);
13
+ margin-inline: var(--content-mx);
14
+ padding-inline: var(--spc-6);
15
+ }
16
+ }
17
+
18
+ main {
19
+ flex: 1;
20
+ font-size: var(--fs-3);
21
+ > section[aria-label],
22
+ > section {
23
+ width: var(--content-w);
24
+ margin-inline: var(--content-mx);
25
+ &:has(> article, > aside) {
26
+ display: flex;
27
+ flex-wrap: wrap;
28
+ flex: 1;
29
+ gap: var(--content-gap);
30
+ > article {
31
+ flex-basis: 0;
32
+ flex-grow: 999;
33
+ min-inline-size: 50%;
34
+ }
35
+ > aside {
36
+ flex-basis: 20rem;
37
+ flex-grow: 1;
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ footer {
44
+ > div {
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ min-height: 5rem;
49
+ text-align: center;
50
+ }
51
+ }
@@ -0,0 +1,54 @@
1
+ import { StoryObj, Meta } from '@storybook/react'
2
+ /**
3
+ * Import testing library dependencies
4
+ */
5
+ import { within, userEvent } from '@storybook/testing-library'
6
+
7
+ /**
8
+ * Import jest matchers
9
+ */
10
+ import { expect } from '@storybook/jest'
11
+
12
+ import { Header } from './landmarks'
13
+
14
+ import Img from '#components/images/img'
15
+
16
+ const meta: Meta<typeof Header> = {
17
+ title: 'FP.React Components/Layout/Landmarks',
18
+ component: Header,
19
+ args: {
20
+ children: 'Default Header',
21
+ // @ts-ignore
22
+ 'data-testid': 'banner',
23
+ },
24
+ } as Meta
25
+
26
+ const headerChildren = () => (
27
+ <>
28
+ <h2>Header Title</h2>
29
+ <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero, unde?</p>
30
+ </>
31
+ )
32
+
33
+ export default meta
34
+ type Story = StoryObj<typeof Header>
35
+
36
+ export const LandmarkDefault: Story = {}
37
+
38
+ export const HeroHeader: Story = {
39
+ args: {
40
+ children: headerChildren(),
41
+ headerBackground: <Img src="https://picsum.photos/2000/1000" alt="" />,
42
+ styles: { color: 'red' },
43
+ classNames: 'header-class',
44
+ 'data-styles': 'blue',
45
+ },
46
+ play: async ({ canvasElement }) => {
47
+ const canvas = within(canvasElement)
48
+ const header = canvas.getByRole('banner')
49
+ expect(header).toBeInTheDocument()
50
+ const title = canvas.getByRole('heading')
51
+ expect(title).toBeInTheDocument()
52
+ expect(title).toHaveTextContent(/header title/i)
53
+ },
54
+ } as Story
@@ -0,0 +1,149 @@
1
+ import UI from '../ui'
2
+ import React, { ReactNode } from 'react'
3
+
4
+ type ComponentProps = React.ComponentProps<typeof UI>
5
+
6
+ /**
7
+ * Renders children elements without any wrapping component.
8
+ * Can be used as a placeholder when no semantic landmark is needed.
9
+ */
10
+ export const Landmarks = (children?: React.FC) => <>{children}</>
11
+
12
+ type HeaderProps = {
13
+ headerBackground?: ReactNode
14
+ } & ComponentProps
15
+ /**
16
+ * Header component.
17
+ *
18
+ * Renders a header landmark with a section child.
19
+ *
20
+ * @param children - The content to render inside the header.
21
+ * @param styles - Optional styles object.
22
+ * @param props - Other props.
23
+ */
24
+ export const Header = ({
25
+ id,
26
+ children,
27
+ headerBackground,
28
+ styles,
29
+ classes,
30
+ ...props
31
+ }: HeaderProps) => {
32
+ return (
33
+ <UI as="header" id={id} styles={styles} className={classes} {...props}>
34
+ {headerBackground}
35
+ <UI as="section">{children}</UI>
36
+ </UI>
37
+ )
38
+ }
39
+
40
+ /**
41
+ * Main component.
42
+ *
43
+ * Renders a main landmark.
44
+ *
45
+ * @param children - The content to render inside the main element.
46
+ * @param styles - Optional styles object.
47
+ * @param props - Other props.
48
+ */
49
+ export const Main = ({
50
+ id,
51
+ children,
52
+ styles,
53
+ classes,
54
+ ...props
55
+ }: ComponentProps) => {
56
+ return (
57
+ <UI as="main" id={id} styles={styles} {...props} className={classes}>
58
+ {children}
59
+ </UI>
60
+ )
61
+ }
62
+
63
+ /**
64
+ * Footer component that renders a footer element with a section element inside.
65
+ * @param {ReactNode} children - Child elements to render inside the section element.
66
+ * @param styles - CSS styles to apply to the footer element.
67
+ * @param props - Additional props to pass to the footer element.
68
+ * @returns A React component that renders a footer element with a section element inside.
69
+ */
70
+ export const Footer = ({
71
+ id,
72
+ classes,
73
+ children,
74
+ styles = {},
75
+ ...props
76
+ }: ComponentProps) => {
77
+ return (
78
+ <UI as="footer" id={id} className={classes} styles={styles} {...props}>
79
+ <UI as="section">{children || 'Copyright © 2022'}</UI>
80
+ </UI>
81
+ )
82
+ }
83
+
84
+ export const Aside = ({
85
+ id,
86
+ children,
87
+ styles = {},
88
+ classes,
89
+ ...props
90
+ }: ComponentProps) => {
91
+ return (
92
+ <UI as="aside" id={id} styles={styles} className={classes} {...props}>
93
+ <UI as="section">{children}</UI>
94
+ </UI>
95
+ )
96
+ }
97
+
98
+ /**
99
+ * Section component that renders a section element.
100
+ *
101
+ * @param children - Child elements to render inside the section.
102
+ * @param styles - CSS styles to apply to the section.
103
+ * @param props - Other props.
104
+ */
105
+ export const Section = ({
106
+ id,
107
+ children,
108
+ styles,
109
+ classes,
110
+ ...props
111
+ }: ComponentProps) => {
112
+ return (
113
+ <UI as="section" id={id} styles={styles} className={classes} {...props}>
114
+ {children}
115
+ </UI>
116
+ )
117
+ }
118
+
119
+ /**
120
+ * Article component renders an HTML <article> element.
121
+ *
122
+ * @param children - Child elements to render inside the article.
123
+ * @param styles - CSS styles to apply to the article.
124
+ * @param props - Additional props to pass to the article element.
125
+ */
126
+ export const Article = ({
127
+ id,
128
+ children,
129
+
130
+ styles,
131
+ classes,
132
+ ...props
133
+ }: ComponentProps) => {
134
+ return (
135
+ <UI as="article" id={id} styles={styles} className={classes} {...props}>
136
+ {children}
137
+ </UI>
138
+ )
139
+ }
140
+
141
+ export default Landmarks
142
+
143
+ Landmarks.displayName = 'Landmarks'
144
+ Landmarks.Header = Header
145
+ Landmarks.Main = Main
146
+ Landmarks.Footer = Footer
147
+ Landmarks.Aside = Aside
148
+ Landmarks.Section = Section
149
+ Landmarks.Article = Article
@@ -0,0 +1,90 @@
1
+ import { StoryObj, Meta } from '@storybook/react'
2
+ /**
3
+ * Import testing library dependencies
4
+ */
5
+ import { within, userEvent } from '@storybook/testing-library'
6
+
7
+ /**
8
+ * Import jest matchers
9
+ */
10
+ import { expect } from '@storybook/jest'
11
+
12
+ import { Main } from './landmarks'
13
+
14
+ const meta: Meta<typeof Main> = {
15
+ title: 'FP.React Components/Layout/Landmarks',
16
+ component: Main,
17
+ args: {
18
+ // @ts-ignore
19
+ children: (
20
+ <section>
21
+ The main HTML element represents the dominant content of the body of a
22
+ document.
23
+ </section>
24
+ ),
25
+ // @ts-ignore
26
+ 'data-testid': 'main',
27
+ },
28
+ decorators: [
29
+ (Story) => (
30
+ <div style={{ minHeight: '80vh', display: 'flex' }}>
31
+ <Story />
32
+ </div>
33
+ ),
34
+ ],
35
+ } as Meta
36
+
37
+ const mainChildren = () => (
38
+ <>
39
+ <section aria-label="main-content">
40
+ <article>
41
+ <h3>Header Title</h3>
42
+ <p>
43
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis alias,
44
+ labore quibusdam, culpa dolorum rerum fugiat laborum deserunt sed ad
45
+ eveniet, modi reprehenderit vero pariatur enim esse eaque consectetur
46
+ nulla.
47
+ </p>
48
+ <hr />
49
+ <p>
50
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis alias,
51
+ labore quibusdam, culpa dolorum rerum fugiat laborum deserunt sed ad
52
+ eveniet, modi reprehenderit vero pariatur enim esse eaque consectetur
53
+ nulla.
54
+ </p>
55
+ <div>Lorem ipsum dolor sit amet.</div>
56
+ </article>
57
+ <aside>
58
+ <p>
59
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero, unde?
60
+ </p>
61
+ </aside>
62
+ </section>
63
+ </>
64
+ )
65
+
66
+ export default meta
67
+ type Story = StoryObj<typeof Main>
68
+
69
+ export const MainLandmark: Story = {
70
+ play: async ({ canvasElement }) => {
71
+ const canvas = within(canvasElement)
72
+ const main = canvas.getByRole('main')
73
+ expect(main).toBeInTheDocument()
74
+ },
75
+ }
76
+
77
+ export const MainArticles: Story = {
78
+ args: {
79
+ // @ts-ignore
80
+ children: mainChildren(),
81
+ },
82
+ play: async ({ canvasElement }) => {
83
+ const canvas = within(canvasElement)
84
+ const main = canvas.getByRole('main')
85
+ expect(main).toBeInTheDocument()
86
+ const title = canvas.getByRole('heading')
87
+ expect(title).toBeInTheDocument()
88
+ expect(title).toHaveTextContent('Header Title')
89
+ },
90
+ }