@fpkit/acss 0.5.12 → 0.5.13

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 (264) hide show
  1. package/README.md +89 -0
  2. package/libs/{chunk-DV56L5YX.cjs → chunk-2LTJ7HHX.cjs} +4 -4
  3. package/libs/{chunk-EQ67LF46.js → chunk-2Y7W75TT.js} +3 -3
  4. package/libs/{chunk-KKLTUJFB.cjs → chunk-3MKLDCKQ.cjs} +5 -5
  5. package/libs/chunk-3MKLDCKQ.cjs.map +1 -0
  6. package/libs/{chunk-X3EVB7VS.cjs → chunk-5S4ORA4C.cjs} +3 -3
  7. package/libs/{chunk-O6QZBB6G.js → chunk-772NRB75.js} +5 -5
  8. package/libs/chunk-772NRB75.js.map +1 -0
  9. package/libs/{chunk-6BVXFW7U.cjs → chunk-AHDJGCG5.cjs} +3 -3
  10. package/libs/{chunk-E3XP6BEX.cjs → chunk-B7F5FS6D.cjs} +3 -3
  11. package/libs/chunk-D4YLRWAO.cjs +18 -0
  12. package/libs/chunk-D4YLRWAO.cjs.map +1 -0
  13. package/libs/chunk-ETFLFC2S.js +10 -0
  14. package/libs/chunk-ETFLFC2S.js.map +1 -0
  15. package/libs/chunk-GZ4QFPRY.js +9 -0
  16. package/libs/chunk-GZ4QFPRY.js.map +1 -0
  17. package/libs/{chunk-LHVJKDMA.cjs → chunk-J32EZPYD.cjs} +3 -3
  18. package/libs/chunk-JJ43O4Y5.js +8 -0
  19. package/libs/chunk-JJ43O4Y5.js.map +1 -0
  20. package/libs/chunk-KUKIVRC2.js +7 -0
  21. package/libs/chunk-KUKIVRC2.js.map +1 -0
  22. package/libs/chunk-L75OQKEI.cjs +13 -0
  23. package/libs/chunk-L75OQKEI.cjs.map +1 -0
  24. package/libs/{chunk-LL7HTLMS.cjs → chunk-M5RRNTVX.cjs} +3 -3
  25. package/libs/{chunk-LIQJ7ZZR.js → chunk-NGTJDDFO.js} +2 -2
  26. package/libs/chunk-OK5QEIMD.cjs +17 -0
  27. package/libs/chunk-OK5QEIMD.cjs.map +1 -0
  28. package/libs/chunk-P2DC76ZZ.cjs +18 -0
  29. package/libs/chunk-P2DC76ZZ.cjs.map +1 -0
  30. package/libs/chunk-PQ2K3BM6.cjs +17 -0
  31. package/libs/chunk-PQ2K3BM6.cjs.map +1 -0
  32. package/libs/{chunk-QCMV4VQZ.js → chunk-QLZWHAMK.js} +2 -2
  33. package/libs/{chunk-BIP2NY53.js → chunk-RIVUMPOG.js} +2 -2
  34. package/libs/{chunk-ICCKQ2GC.cjs → chunk-ROZI23GS.cjs} +4 -4
  35. package/libs/{chunk-NHYXGV3L.js → chunk-SMYRLO3E.js} +2 -2
  36. package/libs/{chunk-5ZM4XL44.js → chunk-TYRCEX2L.js} +2 -2
  37. package/libs/chunk-VUH3FXGJ.js +11 -0
  38. package/libs/chunk-VUH3FXGJ.js.map +1 -0
  39. package/libs/{chunk-PPOOBUOS.js → chunk-XBA562WW.js} +2 -2
  40. package/libs/{chunk-QVV34QEH.cjs → chunk-XTQKWY7W.cjs} +3 -3
  41. package/libs/{chunk-YWOYVRFT.js → chunk-ZANSFMTD.js} +3 -3
  42. package/libs/components/alert/alert.css +1 -1
  43. package/libs/components/alert/alert.css.map +1 -1
  44. package/libs/components/alert/alert.min.css +2 -2
  45. package/libs/components/badge/badge.css +1 -1
  46. package/libs/components/badge/badge.css.map +1 -1
  47. package/libs/components/badge/badge.min.css +2 -2
  48. package/libs/components/breadcrumbs/breadcrumb.cjs +9 -5
  49. package/libs/components/breadcrumbs/breadcrumb.d.cts +271 -32
  50. package/libs/components/breadcrumbs/breadcrumb.d.ts +271 -32
  51. package/libs/components/breadcrumbs/breadcrumb.js +3 -3
  52. package/libs/components/button.cjs +4 -4
  53. package/libs/components/button.d.cts +2 -2
  54. package/libs/components/button.d.ts +2 -2
  55. package/libs/components/button.js +2 -2
  56. package/libs/components/buttons/button.css +1 -1
  57. package/libs/components/buttons/button.css.map +1 -1
  58. package/libs/components/buttons/button.min.css +2 -2
  59. package/libs/components/card.cjs +7 -7
  60. package/libs/components/card.d.cts +277 -33
  61. package/libs/components/card.d.ts +277 -33
  62. package/libs/components/card.js +2 -2
  63. package/libs/components/cards/card.css +1 -1
  64. package/libs/components/cards/card.css.map +1 -1
  65. package/libs/components/cards/card.min.css +2 -2
  66. package/libs/components/details/details.css +1 -1
  67. package/libs/components/details/details.css.map +1 -1
  68. package/libs/components/details/details.min.css +2 -2
  69. package/libs/components/dialog/dialog.cjs +7 -7
  70. package/libs/components/dialog/dialog.css +1 -1
  71. package/libs/components/dialog/dialog.css.map +1 -1
  72. package/libs/components/dialog/dialog.d.cts +88 -34
  73. package/libs/components/dialog/dialog.d.ts +88 -34
  74. package/libs/components/dialog/dialog.js +5 -5
  75. package/libs/components/dialog/dialog.min.css +2 -2
  76. package/libs/components/form/fields.cjs +4 -4
  77. package/libs/components/form/fields.d.cts +2 -2
  78. package/libs/components/form/fields.d.ts +2 -2
  79. package/libs/components/form/fields.js +2 -2
  80. package/libs/components/form/textarea.cjs +4 -4
  81. package/libs/components/form/textarea.d.cts +2 -2
  82. package/libs/components/form/textarea.d.ts +2 -2
  83. package/libs/components/form/textarea.js +2 -2
  84. package/libs/components/heading/heading.cjs +3 -3
  85. package/libs/components/heading/heading.d.cts +3 -14
  86. package/libs/components/heading/heading.d.ts +3 -14
  87. package/libs/components/heading/heading.js +2 -2
  88. package/libs/components/icons/icon.cjs +4 -4
  89. package/libs/components/icons/icon.d.cts +148 -4
  90. package/libs/components/icons/icon.d.ts +148 -4
  91. package/libs/components/icons/icon.js +2 -2
  92. package/libs/components/images/img.css +1 -1
  93. package/libs/components/images/img.css.map +1 -1
  94. package/libs/components/images/img.min.css +2 -2
  95. package/libs/components/link/link.cjs +4 -4
  96. package/libs/components/link/link.d.cts +2 -2
  97. package/libs/components/link/link.d.ts +2 -2
  98. package/libs/components/link/link.js +2 -2
  99. package/libs/components/list/list.cjs +5 -5
  100. package/libs/components/list/list.d.cts +3 -3
  101. package/libs/components/list/list.d.ts +3 -3
  102. package/libs/components/list/list.js +2 -2
  103. package/libs/components/modal.cjs +4 -4
  104. package/libs/components/modal.js +3 -3
  105. package/libs/components/nav/nav.cjs +7 -7
  106. package/libs/components/nav/nav.d.cts +2 -2
  107. package/libs/components/nav/nav.d.ts +2 -2
  108. package/libs/components/nav/nav.js +3 -3
  109. package/libs/components/text/text.cjs +5 -5
  110. package/libs/components/text/text.d.cts +2 -2
  111. package/libs/components/text/text.d.ts +2 -2
  112. package/libs/components/text/text.js +2 -2
  113. package/libs/heading-3648c538.d.ts +250 -0
  114. package/libs/hooks.cjs +7 -0
  115. package/libs/hooks.d.cts +5 -0
  116. package/libs/hooks.d.ts +5 -0
  117. package/libs/hooks.js +3 -0
  118. package/libs/icons.cjs +3 -3
  119. package/libs/icons.d.cts +1 -1
  120. package/libs/icons.d.ts +1 -1
  121. package/libs/icons.js +2 -2
  122. package/libs/index.cjs +112 -91
  123. package/libs/index.cjs.map +1 -1
  124. package/libs/index.css +1 -1
  125. package/libs/index.css.map +1 -1
  126. package/libs/index.d.cts +515 -31
  127. package/libs/index.d.ts +515 -31
  128. package/libs/index.js +31 -19
  129. package/libs/index.js.map +1 -1
  130. package/libs/ui-645f95b5.d.ts +285 -0
  131. package/package.json +2 -83
  132. package/src/components/README-UI.mdx +416 -0
  133. package/src/components/alert/ACCESSIBILITY.md +319 -0
  134. package/src/components/alert/README.mdx +475 -19
  135. package/src/components/alert/alert.scss +113 -6
  136. package/src/components/alert/alert.stories.tsx +372 -0
  137. package/src/components/alert/alert.test.tsx +762 -0
  138. package/src/components/alert/alert.tsx +331 -66
  139. package/src/components/alert/views/alert-actions.tsx +13 -0
  140. package/src/components/alert/views/alert-content.tsx +17 -0
  141. package/src/components/alert/views/alert-icon.tsx +53 -0
  142. package/src/components/alert/views/alert-screen-reader-text.tsx +30 -0
  143. package/src/components/alert/views/alert-title.tsx +23 -0
  144. package/src/components/alert/views/alert-view.tsx +158 -0
  145. package/src/components/alert/views/index.ts +12 -0
  146. package/src/components/badge/badge.mdx +186 -49
  147. package/src/components/badge/badge.scss +20 -2
  148. package/src/components/badge/badge.stories.tsx +160 -14
  149. package/src/components/badge/badge.test.tsx +179 -0
  150. package/src/components/badge/badge.tsx +97 -4
  151. package/src/components/breadcrumbs/README.mdx +364 -45
  152. package/src/components/breadcrumbs/__snapshots__/breadcrumb.test.tsx.snap +152 -0
  153. package/src/components/breadcrumbs/breadcrumb.stories.tsx +7 -3
  154. package/src/components/breadcrumbs/breadcrumb.test.tsx +490 -0
  155. package/src/components/breadcrumbs/breadcrumb.tsx +427 -170
  156. package/src/components/buttons/button.scss +34 -31
  157. package/src/components/buttons/button.stories.tsx +35 -0
  158. package/src/components/cards/README.mdx +657 -0
  159. package/src/components/cards/card.scss +22 -0
  160. package/src/components/cards/card.stories.tsx +167 -5
  161. package/src/components/cards/card.test.tsx +360 -20
  162. package/src/components/cards/card.tsx +200 -79
  163. package/src/components/cards/card.types.ts +135 -0
  164. package/src/components/cards/card.utils.ts +79 -0
  165. package/src/components/details/ACCESSIBILITY-REVIEW-LIVE.md +1050 -0
  166. package/src/components/details/ACCESSIBILITY-REVIEW.md +502 -0
  167. package/src/components/details/README.mdx +437 -69
  168. package/src/components/details/details.scss +16 -7
  169. package/src/components/details/details.test.tsx +385 -0
  170. package/src/components/details/details.tsx +101 -69
  171. package/src/components/details/details.types.ts +76 -0
  172. package/src/components/dialog/README.mdx +513 -110
  173. package/src/components/dialog/dialog-modal.tsx +79 -56
  174. package/src/components/dialog/dialog.scss +53 -3
  175. package/src/components/dialog/dialog.stories.tsx +10 -7
  176. package/src/components/dialog/dialog.test.tsx +450 -0
  177. package/src/components/dialog/dialog.tsx +69 -59
  178. package/src/components/dialog/dialog.types.ts +133 -0
  179. package/src/components/dialog/views/dialog-footer.tsx +54 -11
  180. package/src/components/dialog/views/dialog-header.tsx +20 -15
  181. package/src/components/heading/heading.stories.tsx +44 -4
  182. package/src/components/heading/heading.tsx +89 -23
  183. package/src/components/icons/README.mdx +332 -0
  184. package/src/components/icons/icon.stories.tsx +74 -1
  185. package/src/components/icons/icon.tsx +89 -1
  186. package/src/components/icons/types.ts +47 -0
  187. package/src/components/images/README.mdx +340 -24
  188. package/src/components/images/img.scss +19 -3
  189. package/src/components/images/img.stories.tsx +424 -15
  190. package/src/components/images/img.test.tsx +354 -25
  191. package/src/components/images/img.tsx +186 -63
  192. package/src/components/images/img.types.ts +211 -0
  193. package/src/components/title/MIGRATION.md +199 -0
  194. package/src/components/title/README.md +326 -0
  195. package/src/components/title/README.mdx +452 -0
  196. package/src/components/title/title.stories.tsx +393 -0
  197. package/src/components/title/title.test.tsx +251 -0
  198. package/src/components/title/title.tsx +219 -0
  199. package/src/components/ui.stories.tsx +894 -0
  200. package/src/components/ui.test.tsx +559 -0
  201. package/src/components/ui.tsx +266 -15
  202. package/src/components/word-count/README.md +240 -0
  203. package/src/hooks.ts +1 -0
  204. package/src/index.ts +10 -2
  205. package/src/sass/_properties.scss +1 -0
  206. package/src/styles/alert/alert.css +94 -4
  207. package/src/styles/alert/alert.css.map +1 -1
  208. package/src/styles/badge/badge.css +20 -2
  209. package/src/styles/badge/badge.css.map +1 -1
  210. package/src/styles/buttons/button.css +31 -31
  211. package/src/styles/buttons/button.css.map +1 -1
  212. package/src/styles/cards/card.css +16 -0
  213. package/src/styles/cards/card.css.map +1 -1
  214. package/src/styles/details/details.css +19 -8
  215. package/src/styles/details/details.css.map +1 -1
  216. package/src/styles/dialog/dialog.css +43 -2
  217. package/src/styles/dialog/dialog.css.map +1 -1
  218. package/src/styles/images/img.css +15 -3
  219. package/src/styles/images/img.css.map +1 -1
  220. package/src/styles/index.css +240 -51
  221. package/src/styles/index.css.map +1 -1
  222. package/src/test/setup.d.ts +9 -0
  223. package/src/test/setup.ts +53 -1
  224. package/libs/chunk-6TE5QEVE.cjs +0 -13
  225. package/libs/chunk-6TE5QEVE.cjs.map +0 -1
  226. package/libs/chunk-7K76RW2A.cjs +0 -18
  227. package/libs/chunk-7K76RW2A.cjs.map +0 -1
  228. package/libs/chunk-BSPKFLO4.js +0 -8
  229. package/libs/chunk-BSPKFLO4.js.map +0 -1
  230. package/libs/chunk-BV5CLH44.cjs +0 -18
  231. package/libs/chunk-BV5CLH44.cjs.map +0 -1
  232. package/libs/chunk-DKGJHKGW.js +0 -9
  233. package/libs/chunk-DKGJHKGW.js.map +0 -1
  234. package/libs/chunk-ECLD37WN.cjs +0 -16
  235. package/libs/chunk-ECLD37WN.cjs.map +0 -1
  236. package/libs/chunk-HYBZBN4G.js +0 -8
  237. package/libs/chunk-HYBZBN4G.js.map +0 -1
  238. package/libs/chunk-KKLTUJFB.cjs.map +0 -1
  239. package/libs/chunk-M5QL5TAE.cjs +0 -14
  240. package/libs/chunk-M5QL5TAE.cjs.map +0 -1
  241. package/libs/chunk-NE6YXTMC.js +0 -7
  242. package/libs/chunk-NE6YXTMC.js.map +0 -1
  243. package/libs/chunk-O6QZBB6G.js.map +0 -1
  244. package/libs/chunk-SXVZSWX6.js +0 -11
  245. package/libs/chunk-SXVZSWX6.js.map +0 -1
  246. package/libs/ui-9a6f9f8d.d.ts +0 -24
  247. package/src/components/cards/README.md +0 -80
  248. package/src/components/dialog/hooks/useClickOutside.ts +0 -33
  249. /package/libs/{chunk-DV56L5YX.cjs.map → chunk-2LTJ7HHX.cjs.map} +0 -0
  250. /package/libs/{chunk-EQ67LF46.js.map → chunk-2Y7W75TT.js.map} +0 -0
  251. /package/libs/{chunk-X3EVB7VS.cjs.map → chunk-5S4ORA4C.cjs.map} +0 -0
  252. /package/libs/{chunk-6BVXFW7U.cjs.map → chunk-AHDJGCG5.cjs.map} +0 -0
  253. /package/libs/{chunk-E3XP6BEX.cjs.map → chunk-B7F5FS6D.cjs.map} +0 -0
  254. /package/libs/{chunk-LHVJKDMA.cjs.map → chunk-J32EZPYD.cjs.map} +0 -0
  255. /package/libs/{chunk-LL7HTLMS.cjs.map → chunk-M5RRNTVX.cjs.map} +0 -0
  256. /package/libs/{chunk-LIQJ7ZZR.js.map → chunk-NGTJDDFO.js.map} +0 -0
  257. /package/libs/{chunk-QCMV4VQZ.js.map → chunk-QLZWHAMK.js.map} +0 -0
  258. /package/libs/{chunk-BIP2NY53.js.map → chunk-RIVUMPOG.js.map} +0 -0
  259. /package/libs/{chunk-ICCKQ2GC.cjs.map → chunk-ROZI23GS.cjs.map} +0 -0
  260. /package/libs/{chunk-NHYXGV3L.js.map → chunk-SMYRLO3E.js.map} +0 -0
  261. /package/libs/{chunk-5ZM4XL44.js.map → chunk-TYRCEX2L.js.map} +0 -0
  262. /package/libs/{chunk-PPOOBUOS.js.map → chunk-XBA562WW.js.map} +0 -0
  263. /package/libs/{chunk-QVV34QEH.cjs.map → chunk-XTQKWY7W.cjs.map} +0 -0
  264. /package/libs/{chunk-YWOYVRFT.js.map → chunk-ZANSFMTD.js.map} +0 -0
@@ -1,43 +1,359 @@
1
- # Images Directory
1
+ # Image Components
2
2
 
3
- This directory contains React components and utilities related to rendering images in a web application. The key files and their purposes are as follows:
3
+ Modern, accessible, and performant React image components with built-in support for responsive images, lazy loading, and WCAG 2.1 AA compliance.
4
4
 
5
- ## img.tsx
5
+ ---
6
6
 
7
- The `Img` component is a React component that renders an `<img>` element with additional props and functionality. It provides options to customize the image's source, alt text, dimensions, loading behavior, placeholder, fetch priority, and decoding method. It also supports callbacks for image load and error events.
7
+ ## Overview
8
8
 
9
- ## img.scss
9
+ This directory contains:
10
10
 
11
- This file contains CSS styles for the `<img>` element and the `<figure>` element that can contain an image. It sets default styles for image sizing, aspect ratio, object fit, and positioning. It also styles the `<figure>` element to display the image and caption in a flex layout with a background color.
11
+ - **`<Img />`** - Type-safe image component with responsive image support and error handling
12
+ - **`<Figure />`** - Semantic figure component with image and caption
13
+ - **CSS custom properties** - Flexible theming system using SCSS
14
+ - **Comprehensive tests** - 25+ tests covering accessibility, performance, and edge cases
12
15
 
13
- ## figure.tsx
16
+ ---
14
17
 
15
- The `Figure` component is a React component that renders a `<figure>` element with an image and an optional caption. It uses the `Img` component to render the image and provides props to set the image source, dimensions, and alt text, as well as the caption text.
18
+ ## Quick Start
16
19
 
17
- ## img.test.tsx
20
+ ```tsx
21
+ import { Img } from '@fpkit/acss'
18
22
 
19
- This file contains unit tests for the `Img` component, ensuring it renders correctly with various props and handles image load and error events as expected.
23
+ // Basic usage
24
+ <Img src="/photo.jpg" alt="Beautiful landscape" width={800} />
20
25
 
21
- ## img.stories.tsx and figure.stories.tsx
26
+ // Decorative image
27
+ <Img src="/border.png" alt="" />
22
28
 
23
- These files contain Storybook stories for the `Img` and `Figure` components, respectively. They provide examples and documentation for how to use the components with different props and configurations.
29
+ // Responsive image
30
+ <Img
31
+ src="/photo.jpg"
32
+ srcSet="/photo-320w.jpg 320w, /photo-640w.jpg 640w, /photo-1024w.jpg 1024w"
33
+ sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 800px"
34
+ alt="Team photo"
35
+ />
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Accessibility (WCAG 2.1 AA)
41
+
42
+ ### Decorative Images
43
+
44
+ Images that are purely visual decoration should use an empty `alt` attribute:
45
+
46
+ ```tsx
47
+ // ✅ GOOD: Decorative border
48
+ <Img src="/decorative-border.png" alt="" />
49
+
50
+ // ✅ GOOD: Background pattern
51
+ <Img src="/pattern.svg" alt="" />
52
+ ```
53
+
54
+ ### Semantic Images
55
+
56
+ Images that convey meaning must have descriptive alt text:
57
+
58
+ ```tsx
59
+ // ✅ GOOD: Informative chart
60
+ <Img
61
+ src="/sales-chart.png"
62
+ alt="Sales chart showing 30% revenue growth in Q4 2024"
63
+ />
64
+
65
+ // ✅ GOOD: Product photo
66
+ <Img
67
+ src="/laptop.jpg"
68
+ alt="Silver MacBook Pro 14-inch on wooden desk"
69
+ />
70
+
71
+ // ❌ BAD: Generic or missing alt text
72
+ <Img src="/chart.png" alt="chart" />
73
+ <Img src="/photo.jpg" />
74
+ ```
24
75
 
25
- ## Usage
76
+ ---
26
77
 
27
- To use the `Img` or `Figure` components in your React application, import them from this directory and pass in the required props. For example:
78
+ ## Performance Optimization
79
+
80
+ ### Lazy Loading (Default)
81
+
82
+ By default, images use lazy loading to improve page load performance:
83
+
84
+ ```tsx
85
+ // Lazy loaded automatically
86
+ <Img src="/photo.jpg" alt="Photo" />
87
+
88
+ // Explicit lazy loading
89
+ <Img src="/photo.jpg" alt="Photo" loading="lazy" />
90
+ ```
91
+
92
+ ### Eager Loading for Hero Images
93
+
94
+ Use eager loading for above-the-fold images:
95
+
96
+ ```tsx
97
+ <Img
98
+ src="/hero.jpg"
99
+ alt="Hero banner"
100
+ loading="eager"
101
+ fetchpriority="high"
102
+ />
103
+ ```
104
+
105
+ ### Responsive Images
106
+
107
+ Use `srcSet` and `sizes` for responsive images:
108
+
109
+ ```tsx
110
+ <Img
111
+ src="/photo.jpg"
112
+ srcSet="
113
+ /photo-320w.jpg 320w,
114
+ /photo-640w.jpg 640w,
115
+ /photo-1024w.jpg 1024w
116
+ "
117
+ sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 800px"
118
+ alt="Responsive image adapts to viewport"
119
+ />
120
+ ```
28
121
 
29
- ```jsx
30
- import Img from './components/images/img';
31
- import Figure from './components/images/figure';
122
+ ### Async Decoding
32
123
 
33
- function MyComponent() {
34
- return (
35
- <div>
36
- <Img src="/path/to/image.jpg" alt="My Image" width={300} height={200} />
37
- <Figure src="/path/to/image.jpg" alt="My Image" width={300} height={200} caption="This is a caption" />
124
+ Prevent blocking the main thread during image decoding:
125
+
126
+ ```tsx
127
+ <Img src="/large-photo.jpg" alt="Large photo" decoding="async" />
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Error Handling
133
+
134
+ ### Default Placeholder
135
+
136
+ The component automatically falls back to a placeholder on error:
137
+
138
+ ```tsx
139
+ <Img src="/might-fail.jpg" alt="Photo" />
140
+ // Falls back to default placeholder if image fails
141
+ ```
142
+
143
+ ### Custom Placeholder
144
+
145
+ Provide a custom fallback image:
146
+
147
+ ```tsx
148
+ <Img
149
+ src="/photo.jpg"
150
+ placeholder="/fallback.png"
151
+ alt="Profile photo"
152
+ />
153
+ ```
154
+
155
+ ### Custom Error Handler
156
+
157
+ Handle errors with custom logic:
158
+
159
+ ```tsx
160
+ <Img
161
+ src="/photo.jpg"
162
+ alt="Photo"
163
+ onError={(e) => {
164
+ console.error('Image failed:', e.currentTarget.src)
165
+ // Log to analytics, show notification, etc.
166
+ }}
167
+ />
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Common Use Cases
173
+
174
+ ### Profile/Avatar Images
175
+
176
+ ```tsx
177
+ <Img
178
+ src="/avatar.jpg"
179
+ alt="User profile photo"
180
+ width={150}
181
+ height={150}
182
+ loading="eager"
183
+ styles={{ borderRadius: '50%' }}
184
+ />
185
+ ```
186
+
187
+ ### Gallery Images
188
+
189
+ ```tsx
190
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1rem' }}>
191
+ <Img src="/photo1.jpg" alt="Gallery image 1" width={400} />
192
+ <Img src="/photo2.jpg" alt="Gallery image 2" width={400} />
193
+ <Img src="/photo3.jpg" alt="Gallery image 3" width={400} />
38
194
  </div>
39
- );
195
+ ```
196
+
197
+ ### Figure with Caption
198
+
199
+ ```tsx
200
+ import { Figure } from '@fpkit/acss'
201
+
202
+ <Figure
203
+ src="/chart.jpg"
204
+ alt="Q4 sales performance chart"
205
+ caption="Sales increased by 30% in Q4 2024"
206
+ />
207
+ ```
208
+
209
+ ---
210
+
211
+ ## API Reference
212
+
213
+ ### Img Props
214
+
215
+ | Prop | Type | Default | Description |
216
+ |------|------|---------|-------------|
217
+ | `src` | `string` | `"//"` | Image source URL |
218
+ | `alt` | `string` | `undefined` | Alternative text (empty for decorative, descriptive for semantic) |
219
+ | `width` | `number \| string` | `480` | Image width |
220
+ | `height` | `number \| string` | `"auto"` | Image height |
221
+ | `loading` | `"lazy" \| "eager"` | `"lazy"` | Loading strategy |
222
+ | `srcSet` | `string` | `undefined` | Responsive image sources |
223
+ | `sizes` | `string` | `undefined` | Media conditions for responsive images |
224
+ | `placeholder` | `string` | Auto-generated | Fallback image URL |
225
+ | `fetchpriority` | `"low" \| "high" \| "auto"` | `"low"` | Fetch priority hint |
226
+ | `decoding` | `"sync" \| "async" \| "auto"` | `"auto"` | Decoding hint |
227
+ | `onError` | `function` | `undefined` | Error event handler |
228
+ | `onLoad` | `function` | `undefined` | Load event handler |
229
+ | `styles` | `CSSProperties` | `undefined` | Inline styles |
230
+
231
+ All native `<img>` attributes are supported via spread props.
232
+
233
+ ---
234
+
235
+ ## Styling with CSS Custom Properties
236
+
237
+ The component uses CSS custom properties for theming:
238
+
239
+ ```scss
240
+ img[alt] {
241
+ /* Override these in your CSS */
242
+ --img-max-width: 100%;
243
+ --img-height: auto;
244
+ --img-object-fit: cover;
245
+ --img-object-position: center;
246
+ --img-aspect-ratio: auto 2/3;
247
+ --img-display: inline-block;
248
+ }
249
+ ```
250
+
251
+ Example customization:
252
+
253
+ ```tsx
254
+ <Img
255
+ src="/photo.jpg"
256
+ alt="Photo"
257
+ styles={{
258
+ '--img-object-fit': 'contain',
259
+ '--img-aspect-ratio': '16/9',
260
+ }}
261
+ />
262
+ ```
263
+
264
+ ---
265
+
266
+ ## Migration from v1.x
267
+
268
+ ### Breaking Changes
269
+
270
+ **Event handler props renamed:**
271
+
272
+ ```tsx
273
+ // ❌ Old API (v1.x)
274
+ <Img imgError={handleError} imgLoaded={handleLoad} />
275
+
276
+ // ✅ New API (v2.x)
277
+ <Img onError={handleError} onLoad={handleLoad} />
278
+ ```
279
+
280
+ **Type export renamed:**
281
+
282
+ ```tsx
283
+ // ❌ Old import
284
+ import { ImageProps } from '@fpkit/acss'
285
+
286
+ // ✅ New import
287
+ import type { ImgProps } from '@fpkit/acss'
288
+ ```
289
+
290
+ ---
291
+
292
+ ## Files in This Directory
293
+
294
+ ### Component Files
295
+
296
+ - **`img.tsx`** - Main Img component with responsive image support
297
+ - **`img.types.ts`** - TypeScript type definitions and interfaces
298
+ - **`figure.tsx`** - Figure component for images with captions
299
+
300
+ ### Styling
301
+
302
+ - **`img.scss`** - SCSS source with CSS custom properties
303
+ - Compiled to `libs/components/images/img.css` during build
304
+
305
+ ### Testing & Documentation
306
+
307
+ - **`img.test.tsx`** - 25 comprehensive unit tests
308
+ - **`img.stories.tsx`** - 11 Storybook stories with examples
309
+ - **`figure.stories.tsx`** - Figure component stories
310
+
311
+ ---
312
+
313
+ ## TypeScript Support
314
+
315
+ Full TypeScript support with comprehensive type definitions:
316
+
317
+ ```tsx
318
+ import { Img, type ImgProps } from '@fpkit/acss'
319
+
320
+ // Custom component extending Img
321
+ interface CustomImgProps extends ImgProps {
322
+ caption?: string
40
323
  }
324
+
325
+ const CustomImg: React.FC<CustomImgProps> = ({ caption, ...props }) => (
326
+ <figure>
327
+ <Img {...props} />
328
+ {caption && <figcaption>{caption}</figcaption>}
329
+ </figure>
330
+ )
331
+ ```
332
+
333
+ ---
334
+
335
+ ## Testing
336
+
337
+ Run tests for the Img component:
338
+
339
+ ```bash
340
+ npm test -- img.test.tsx
41
341
  ```
42
342
 
43
- The components provide a consistent and extensible way to render images in your application, with additional features like placeholders, loading behavior, and error handling.
343
+ All 25 tests should pass
344
+
345
+ ---
346
+
347
+ ## Related Components
348
+
349
+ - **`<Figure />`** - Semantic figure with caption support
350
+ - **`<UI />`** - Base polymorphic component used internally
351
+
352
+ ---
353
+
354
+ ## Resources
355
+
356
+ - [WCAG 2.1 Non-text Content Guidelines](https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html)
357
+ - [MDN: Responsive Images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)
358
+ - [MDN: Image loading attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#loading)
359
+ - [Component Storybook](../../stories) - View all examples in Storybook
@@ -1,4 +1,11 @@
1
+ /**
2
+ * Image component styles
3
+ *
4
+ * Applies to all images with alt attribute (both decorative alt="" and semantic).
5
+ * Uses CSS custom properties for flexible theming and responsive behavior.
6
+ */
1
7
  img[alt] {
8
+ /* CSS Custom Properties - Override these for customization */
2
9
  --img-max-width: 100%;
3
10
  --img-height: auto;
4
11
  --img-object-fit: cover;
@@ -6,17 +13,26 @@ img[alt] {
6
13
  --img-aspect-ratio: auto 2/3;
7
14
  --img-display: inline-block;
8
15
 
16
+ /* Layout - Responsive by default */
9
17
  max-width: var(--img-max-width);
10
- max-inline-size: var(--img-max-width);
18
+ max-inline-size: var(--img-max-width); /* Logical property for i18n */
11
19
  block-size: var(--img-height);
20
+ display: var(--img-display);
21
+ vertical-align: middle;
22
+
23
+ /* Object fitting for responsive images */
12
24
  object-fit: var(--img-object-fit);
13
25
  object-position: var(--img-object-position);
14
26
  aspect-ratio: var(--img-aspect-ratio);
15
- display: var(--img-display);
16
- vertical-align: middle;
27
+
28
+ /* Accessibility - Italic font for broken image alt text */
17
29
  font-style: italic;
30
+
31
+ /* Background for loading state */
18
32
  background-size: cover;
19
33
  background-repeat: no-repeat;
34
+
35
+ /* Shape margin for text wrapping */
20
36
  shape-margin: var(--spc-3);
21
37
  }
22
38