@myelmut/design-system 0.1.46 → 0.1.48

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 (318) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.es.js +720 -697
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/types/index.d.ts +6 -1
  6. package/package.json +3 -2
  7. package/src/assets/fonts/PPMori-Regular.woff2 +0 -0
  8. package/src/assets/fonts/PPMori-RegularItalic.woff2 +0 -0
  9. package/src/assets/fonts/PPMori-SemiBold.woff2 +0 -0
  10. package/src/assets/fonts/PPPangaia-Medium.woff2 +0 -0
  11. package/src/assets/fonts/PPPangaia-MediumItalic.woff2 +0 -0
  12. package/src/assets/fonts/PPPangaia-Ultralight.woff2 +0 -0
  13. package/src/assets/illustrations/balls-light.webp +0 -0
  14. package/src/assets/illustrations/balls.webp +0 -0
  15. package/src/assets/illustrations/basket-light.webp +0 -0
  16. package/src/assets/illustrations/basket.webp +0 -0
  17. package/src/assets/illustrations/bowl-2-light.webp +0 -0
  18. package/src/assets/illustrations/bowl-2.webp +0 -0
  19. package/src/assets/illustrations/bowl-light.webp +0 -0
  20. package/src/assets/illustrations/bowl.webp +0 -0
  21. package/src/assets/illustrations/box-2-light.webp +0 -0
  22. package/src/assets/illustrations/box-2.webp +0 -0
  23. package/src/assets/illustrations/box-light.webp +0 -0
  24. package/src/assets/illustrations/box.webp +0 -0
  25. package/src/assets/illustrations/calendar-light.webp +0 -0
  26. package/src/assets/illustrations/calendar.webp +0 -0
  27. package/src/assets/illustrations/can-light.webp +0 -0
  28. package/src/assets/illustrations/can.webp +0 -0
  29. package/src/assets/illustrations/carrot-light.webp +0 -0
  30. package/src/assets/illustrations/carrot.webp +0 -0
  31. package/src/assets/illustrations/cat-light.webp +0 -0
  32. package/src/assets/illustrations/cat.webp +0 -0
  33. package/src/assets/illustrations/check-rounded.webp +0 -0
  34. package/src/assets/illustrations/chicken-light.webp +0 -0
  35. package/src/assets/illustrations/chicken.webp +0 -0
  36. package/src/assets/illustrations/cross-rounded.webp +0 -0
  37. package/src/assets/illustrations/crown-light.webp +0 -0
  38. package/src/assets/illustrations/crown.webp +0 -0
  39. package/src/assets/illustrations/dog-light.webp +0 -0
  40. package/src/assets/illustrations/dog.webp +0 -0
  41. package/src/assets/illustrations/face-light.webp +0 -0
  42. package/src/assets/illustrations/face.webp +0 -0
  43. package/src/assets/illustrations/food-bag-light.webp +0 -0
  44. package/src/assets/illustrations/food-bag.webp +0 -0
  45. package/src/assets/illustrations/france-light.webp +0 -0
  46. package/src/assets/illustrations/france.webp +0 -0
  47. package/src/assets/illustrations/fridge-light.webp +0 -0
  48. package/src/assets/illustrations/fridge.webp +0 -0
  49. package/src/assets/illustrations/glasses-light.webp +0 -0
  50. package/src/assets/illustrations/glasses.webp +0 -0
  51. package/src/assets/illustrations/half-label-light.webp +0 -0
  52. package/src/assets/illustrations/half-label.webp +0 -0
  53. package/src/assets/illustrations/kitten-light.webp +0 -0
  54. package/src/assets/illustrations/kitten.webp +0 -0
  55. package/src/assets/illustrations/label-light.webp +0 -0
  56. package/src/assets/illustrations/label.webp +0 -0
  57. package/src/assets/illustrations/leaf-light.webp +0 -0
  58. package/src/assets/illustrations/leaf.webp +0 -0
  59. package/src/assets/illustrations/liquid-light.webp +0 -0
  60. package/src/assets/illustrations/liquid.webp +0 -0
  61. package/src/assets/illustrations/magnifying-glass-light.webp +0 -0
  62. package/src/assets/illustrations/magnifying-glass.webp +0 -0
  63. package/src/assets/illustrations/meat-light.webp +0 -0
  64. package/src/assets/illustrations/meat.webp +0 -0
  65. package/src/assets/illustrations/molecule-light.webp +0 -0
  66. package/src/assets/illustrations/molecule.webp +0 -0
  67. package/src/assets/illustrations/paws-light.webp +0 -0
  68. package/src/assets/illustrations/paws.webp +0 -0
  69. package/src/assets/illustrations/plate-light.webp +0 -0
  70. package/src/assets/illustrations/plate.webp +0 -0
  71. package/src/assets/illustrations/pot-big-2-light.webp +0 -0
  72. package/src/assets/illustrations/pot-big-2.webp +0 -0
  73. package/src/assets/illustrations/pot-big-light.webp +0 -0
  74. package/src/assets/illustrations/pot-big.webp +0 -0
  75. package/src/assets/illustrations/pot-light.webp +0 -0
  76. package/src/assets/illustrations/pot.webp +0 -0
  77. package/src/assets/illustrations/puppy-light.webp +0 -0
  78. package/src/assets/illustrations/puppy.webp +0 -0
  79. package/src/assets/illustrations/quantity-light.webp +0 -0
  80. package/src/assets/illustrations/quantity.webp +0 -0
  81. package/src/assets/illustrations/sausage-light.webp +0 -0
  82. package/src/assets/illustrations/sausage.webp +0 -0
  83. package/src/assets/illustrations/sausages-light.webp +0 -0
  84. package/src/assets/illustrations/sausages.webp +0 -0
  85. package/src/assets/illustrations/skeleton-light.webp +0 -0
  86. package/src/assets/illustrations/skeleton.webp +0 -0
  87. package/src/assets/illustrations/sofa-light.webp +0 -0
  88. package/src/assets/illustrations/sofa.webp +0 -0
  89. package/src/assets/illustrations/sport-light.webp +0 -0
  90. package/src/assets/illustrations/sport.webp +0 -0
  91. package/src/assets/illustrations/steak-light.webp +0 -0
  92. package/src/assets/illustrations/steak.webp +0 -0
  93. package/src/assets/illustrations/truck-light.webp +0 -0
  94. package/src/assets/illustrations/truck.webp +0 -0
  95. package/src/assets/illustrations/warning-light.webp +0 -0
  96. package/src/assets/illustrations/warning.webp +0 -0
  97. package/src/assets/images/cat.webp +0 -0
  98. package/src/assets/images/dog.webp +0 -0
  99. package/src/assets/images/frequency/food-in-fridge.webp +0 -0
  100. package/src/assets/images/ingredients/beef.webp +0 -0
  101. package/src/assets/images/ingredients/beer-yeast.webp +0 -0
  102. package/src/assets/images/ingredients/calcium.webp +0 -0
  103. package/src/assets/images/ingredients/carrot.webp +0 -0
  104. package/src/assets/images/ingredients/chicken.webp +0 -0
  105. package/src/assets/images/ingredients/courgette.webp +0 -0
  106. package/src/assets/images/ingredients/dry-apple.webp +0 -0
  107. package/src/assets/images/ingredients/dry-carrot.webp +0 -0
  108. package/src/assets/images/ingredients/duck.webp +0 -0
  109. package/src/assets/images/ingredients/fish.webp +0 -0
  110. package/src/assets/images/ingredients/liquid.webp +0 -0
  111. package/src/assets/images/ingredients/oil.webp +0 -0
  112. package/src/assets/images/ingredients/pork.webp +0 -0
  113. package/src/assets/images/ingredients/potato.webp +0 -0
  114. package/src/assets/images/ingredients/quinoa.webp +0 -0
  115. package/src/assets/images/ingredients/rice.webp +0 -0
  116. package/src/assets/images/ingredients/seaweed.webp +0 -0
  117. package/src/assets/images/ingredients/turkey.webp +0 -0
  118. package/src/assets/images/ingredients/vitamins.webp +0 -0
  119. package/src/assets/images/tips/claudine-head.webp +0 -0
  120. package/src/assets/images/tips/claudine-tips-head-mobile.webp +0 -0
  121. package/src/assets/images/tips/claudine-tips-head-mobile@2x.webp +0 -0
  122. package/src/assets/images/tips/claudine-tips-head.webp +0 -0
  123. package/src/assets/images/tips/claudine-tips-head@2x.webp +0 -0
  124. package/src/assets/images/tips/claudine-tips-mobile.webp +0 -0
  125. package/src/assets/images/tips/claudine-tips-mobile@2x.webp +0 -0
  126. package/src/assets/images/tips/claudine-tips.webp +0 -0
  127. package/src/assets/images/tips/claudine-tips@2x.webp +0 -0
  128. package/src/assets/images/tips/payment-mobile.webp +0 -0
  129. package/src/assets/images/tips/payment-mobile@2x.webp +0 -0
  130. package/src/assets/images/tips/payment.webp +0 -0
  131. package/src/assets/images/tips/payment@2x.webp +0 -0
  132. package/src/assets/images/trash/dog-product-mobile.webp +0 -0
  133. package/src/assets/images/trash/dog-product.webp +0 -0
  134. package/src/assets/images/trash/full-cat.png +0 -0
  135. package/src/assets/images/trash/testimonial-1-mobile.webp +0 -0
  136. package/src/assets/images/trash/testimonial-1-mobile@2x.webp +0 -0
  137. package/src/assets/images/trash/testimonial-1.webp +0 -0
  138. package/src/assets/images/trash/testimonial-1@2x.webp +0 -0
  139. package/src/components/Accordions/FAQ/FaqItem.stories.tsx +61 -0
  140. package/src/components/Accordions/FAQ/FaqItem.tsx +55 -0
  141. package/src/components/Accordions/Ingredient/Ingredient.stories.tsx +38 -0
  142. package/src/components/Accordions/Ingredient/Ingredient.tsx +93 -0
  143. package/src/components/Accordions/index.tsx +4 -0
  144. package/src/components/Base/Banner/Banner.stories.tsx +33 -0
  145. package/src/components/Base/Banner/Banner.tsx +23 -0
  146. package/src/components/Base/Emblem/Emblem.stories.tsx +40 -0
  147. package/src/components/Base/Emblem/Emblem.tsx +22 -0
  148. package/src/components/Base/Logo/Logo.stories.tsx +46 -0
  149. package/src/components/Base/Logo/Logo.tsx +34 -0
  150. package/src/components/Base/ResponsiveImage/ResponsiveImage.stories.tsx +78 -0
  151. package/src/components/Base/ResponsiveImage/ResponsiveImage.tsx +56 -0
  152. package/src/components/Base/Text/Text.stories.tsx +115 -0
  153. package/src/components/Base/Text/Text.tsx +60 -0
  154. package/src/components/Base/Title/Title.stories.tsx +145 -0
  155. package/src/components/Base/Title/Title.tsx +77 -0
  156. package/src/components/Base/VideoPlayer/VideoPlayer.stories.tsx +60 -0
  157. package/src/components/Base/VideoPlayer/VideoPlayer.tsx +78 -0
  158. package/src/components/Base/index.tsx +9 -0
  159. package/src/components/Buttons/Button/Button.stories.tsx +158 -0
  160. package/src/components/Buttons/Button/Button.tsx +68 -0
  161. package/src/components/Buttons/CardButton/CardButton.stories.tsx +47 -0
  162. package/src/components/Buttons/CardButton/CardButton.tsx +25 -0
  163. package/src/components/Buttons/ClearButton/ClearButton.stories.tsx +26 -0
  164. package/src/components/Buttons/ClearButton/ClearButton.tsx +18 -0
  165. package/src/components/Buttons/FAQButton/FAQButton.stories.tsx +33 -0
  166. package/src/components/Buttons/FAQButton/FAQButton.tsx +27 -0
  167. package/src/components/Buttons/IllustratedCardButton/IllustratedCardButton.stories.tsx +71 -0
  168. package/src/components/Buttons/IllustratedCardButton/IllustratedCardButton.tsx +45 -0
  169. package/src/components/Buttons/SimpleIllustratedCardButton/SimpleIllustratedCardButton.stories.tsx +74 -0
  170. package/src/components/Buttons/SimpleIllustratedCardButton/SimpleIllustratedCardButton.tsx +43 -0
  171. package/src/components/Buttons/SocialButton/SocialButton.stories.tsx +56 -0
  172. package/src/components/Buttons/SocialButton/SocialButton.tsx +28 -0
  173. package/src/components/Buttons/Toggle/Toggle.tsx +64 -0
  174. package/src/components/Buttons/Toggle/Toogle.stories.tsx +66 -0
  175. package/src/components/Buttons/index.ts +10 -0
  176. package/src/components/Cards/CTACard/CTACard.stories.tsx +83 -0
  177. package/src/components/Cards/CTACard/CTACard.tsx +47 -0
  178. package/src/components/Cards/FeatureCard/FeatureCard.stories.tsx +96 -0
  179. package/src/components/Cards/FeatureCard/FeatureCard.tsx +50 -0
  180. package/src/components/Cards/FeatureIllustration/FeatureIllustration.stories.tsx +96 -0
  181. package/src/components/Cards/FeatureIllustration/FeatureIllustration.tsx +56 -0
  182. package/src/components/Cards/FeatureIllustration/index.ts +2 -0
  183. package/src/components/Cards/FoodCard/FoodCard.stories.tsx +43 -0
  184. package/src/components/Cards/FoodCard/FoodCard.tsx +37 -0
  185. package/src/components/Cards/FrequencySelectorCard/FrequencySelectorCard.stories.tsx +140 -0
  186. package/src/components/Cards/FrequencySelectorCard/FrequencySelectorCard.tsx +90 -0
  187. package/src/components/Cards/FrequencySelectorCard/index.ts +2 -0
  188. package/src/components/Cards/IllustratedCard/IllustratedCard.stories.tsx +54 -0
  189. package/src/components/Cards/IllustratedCard/IllustratedCard.tsx +44 -0
  190. package/src/components/Cards/PaymentCard/PaymentCard.stories.tsx +35 -0
  191. package/src/components/Cards/PaymentCard/PaymentCard.tsx +31 -0
  192. package/src/components/Cards/PlanCard/PlanCard.stories.tsx +140 -0
  193. package/src/components/Cards/PlanCard/PlanCard.tsx +119 -0
  194. package/src/components/Cards/Polaroid/Polaroid.stories.tsx +118 -0
  195. package/src/components/Cards/Polaroid/Polaroid.tsx +66 -0
  196. package/src/components/Cards/RecetteCard/RecetteCard.stories.tsx +86 -0
  197. package/src/components/Cards/RecetteCard/RecetteCard.tsx +47 -0
  198. package/src/components/Cards/StatCard/StatCard.stories.tsx +69 -0
  199. package/src/components/Cards/StatCard/StatCard.tsx +45 -0
  200. package/src/components/Cards/Testimonial/Testimonial.stories.tsx +65 -0
  201. package/src/components/Cards/Testimonial/Testimonial.tsx +62 -0
  202. package/src/components/Cards/TestimonialSlider/TestimonialSlider.stories.ts +53 -0
  203. package/src/components/Cards/TestimonialSlider/TestimonialSlider.tsx +50 -0
  204. package/src/components/Cards/Tips/Tips.stories.tsx +32 -0
  205. package/src/components/Cards/Tips/Tips.tsx +40 -0
  206. package/src/components/Cards/TransitionCard/TransitionCard.stories.tsx +50 -0
  207. package/src/components/Cards/TransitionCard/TransitionCard.tsx +66 -0
  208. package/src/components/Cards/UpCard/UpCard.stories.tsx +94 -0
  209. package/src/components/Cards/UpCard/UpCard.tsx +50 -0
  210. package/src/components/Cards/WizardTips/WizardTips.stories.tsx +48 -0
  211. package/src/components/Cards/WizardTips/WizardTips.tsx +33 -0
  212. package/src/components/Cards/index.ts +19 -0
  213. package/src/components/Inputs/ButtonSelect/ButtonSelect.stories.tsx +51 -0
  214. package/src/components/Inputs/ButtonSelect/ButtonSelect.tsx +34 -0
  215. package/src/components/Inputs/Checkbox/Checkbox.stories.tsx +47 -0
  216. package/src/components/Inputs/Checkbox/Checkbox.tsx +35 -0
  217. package/src/components/Inputs/Dropdown/Dropdown.stories.tsx +61 -0
  218. package/src/components/Inputs/Dropdown/Dropdown.tsx +108 -0
  219. package/src/components/Inputs/DropdownMenu/DropdownMenu.stories.tsx +75 -0
  220. package/src/components/Inputs/DropdownMenu/DropdownMenu.tsx +109 -0
  221. package/src/components/Inputs/ErrorMessage/ErrorMessage.stories.tsx +33 -0
  222. package/src/components/Inputs/ErrorMessage/ErrorMessage.tsx +18 -0
  223. package/src/components/Inputs/Filters/Filters.stories.tsx +54 -0
  224. package/src/components/Inputs/Filters/Filters.tsx +75 -0
  225. package/src/components/Inputs/Label/Label.stories.tsx +34 -0
  226. package/src/components/Inputs/Label/Label.tsx +16 -0
  227. package/src/components/Inputs/Newsletter/Newsletter.stories.tsx +67 -0
  228. package/src/components/Inputs/Newsletter/Newsletter.tsx +70 -0
  229. package/src/components/Inputs/QuantityInput/QuantityInput.stories.tsx +54 -0
  230. package/src/components/Inputs/QuantityInput/QuantityInput.tsx +46 -0
  231. package/src/components/Inputs/Tag/Tag.stories.tsx +33 -0
  232. package/src/components/Inputs/Tag/Tag.tsx +19 -0
  233. package/src/components/Inputs/TagSelect/TagSelect.stories.tsx +50 -0
  234. package/src/components/Inputs/TagSelect/TagSelect.tsx +48 -0
  235. package/src/components/Inputs/TextInput/TextInput.stories.tsx +40 -0
  236. package/src/components/Inputs/TextInput/TextInput.tsx +38 -0
  237. package/src/components/Inputs/WizardDropdown/WizardDropdown.stories.tsx +59 -0
  238. package/src/components/Inputs/WizardDropdown/WizardDropdown.tsx +93 -0
  239. package/src/components/Inputs/WizardTextInput/WizardTextInput.stories.tsx +40 -0
  240. package/src/components/Inputs/WizardTextInput/WizardTextInput.tsx +31 -0
  241. package/src/components/Inputs/index.ts +16 -0
  242. package/src/components/Navigation/Footer/Footer.stories.tsx +28 -0
  243. package/src/components/Navigation/Footer/Footer.tsx +130 -0
  244. package/src/components/Navigation/FooterTips/FooterTips.stories.tsx +22 -0
  245. package/src/components/Navigation/FooterTips/FooterTips.tsx +24 -0
  246. package/src/components/Navigation/MobileMenu/MobileMenu.stories.tsx +56 -0
  247. package/src/components/Navigation/MobileMenu/MobileMenu.tsx +45 -0
  248. package/src/components/Navigation/Navbar/Navbar.stories.tsx +128 -0
  249. package/src/components/Navigation/Navbar/Navbar.tsx +66 -0
  250. package/src/components/Navigation/NavbarDesktop/NavbarDesktop.stories.tsx +57 -0
  251. package/src/components/Navigation/NavbarDesktop/NavbarDesktop.tsx +48 -0
  252. package/src/components/Navigation/NavbarMobile/NavbarMobile.stories.tsx +59 -0
  253. package/src/components/Navigation/NavbarMobile/NavbarMobile.tsx +75 -0
  254. package/src/components/Navigation/Stepper/Stepper.stories.tsx +41 -0
  255. package/src/components/Navigation/Stepper/Stepper.tsx +19 -0
  256. package/src/components/Navigation/Tabs/Tabs.stories.tsx +120 -0
  257. package/src/components/Navigation/Tabs/Tabs.tsx +92 -0
  258. package/src/components/Navigation/WizardNavbar/WizardNavbar.stories.tsx +59 -0
  259. package/src/components/Navigation/WizardNavbar/WizardNavbar.tsx +83 -0
  260. package/src/components/Navigation/index.ts +11 -0
  261. package/src/components/SVG/Facebook.svg +3 -0
  262. package/src/components/SVG/Instagram.svg +5 -0
  263. package/src/components/SVG/Linkedin.svg +5 -0
  264. package/src/components/SVG/Tiktok.svg +7 -0
  265. package/src/components/SVG/arrow-plain.svg +3 -0
  266. package/src/components/SVG/arrow.svg +3 -0
  267. package/src/components/SVG/calendar.svg +4 -0
  268. package/src/components/SVG/check-circle.svg +5 -0
  269. package/src/components/SVG/check-rounded.svg +4 -0
  270. package/src/components/SVG/check.svg +3 -0
  271. package/src/components/SVG/cross-rounded.svg +3 -0
  272. package/src/components/SVG/cross.svg +4 -0
  273. package/src/components/SVG/dollar-rounded.svg +6 -0
  274. package/src/components/SVG/double-arrow.svg +3 -0
  275. package/src/components/SVG/filters.svg +8 -0
  276. package/src/components/SVG/hat-cook.svg +4 -0
  277. package/src/components/SVG/home-hero-shape-mobile.svg +3 -0
  278. package/src/components/SVG/home-hero-shape-small.svg +3 -0
  279. package/src/components/SVG/home-hero-shape.svg +3 -0
  280. package/src/components/SVG/index.ts +75 -0
  281. package/src/components/SVG/info.svg +3 -0
  282. package/src/components/SVG/magic-wand.svg +9 -0
  283. package/src/components/SVG/menu.svg +5 -0
  284. package/src/components/SVG/minus.svg +3 -0
  285. package/src/components/SVG/mute.svg +4 -0
  286. package/src/components/SVG/pause.svg +3 -0
  287. package/src/components/SVG/play.svg +3 -0
  288. package/src/components/SVG/plus.svg +4 -0
  289. package/src/components/SVG/polaroid-thread.svg +17 -0
  290. package/src/components/SVG/profil.svg +4 -0
  291. package/src/components/SVG/quote.svg +3 -0
  292. package/src/components/SVG/recipe-bg-shape.svg +3 -0
  293. package/src/components/SVG/star.svg +3 -0
  294. package/src/components/SVG/subtract.svg +3 -0
  295. package/src/components/SVG/trustpilot.svg +14 -0
  296. package/src/components/SVG/unmute.svg +5 -0
  297. package/src/components/index.ts +20 -0
  298. package/src/components/styles/Color/Color.stories.tsx +89 -0
  299. package/src/components/styles/Color/Color.tsx +47 -0
  300. package/src/components/styles/Icon/Icon.stories.tsx +103 -0
  301. package/src/components/styles/Icon/Icon.tsx +6 -0
  302. package/src/components/styles/Illustration/Illustration.stories.tsx +253 -0
  303. package/src/components/styles/Illustration/Illustration.tsx +45 -0
  304. package/src/components/styles/Typography/Typography.stories.tsx +37 -0
  305. package/src/components/styles/Typography/Typography.tsx +19 -0
  306. package/src/helpers/accordions.ts +39 -0
  307. package/src/helpers/clickoutside.ts +31 -0
  308. package/src/helpers/debounce.ts +10 -0
  309. package/src/helpers/index.ts +9 -0
  310. package/src/helpers/intersectionObserver.ts +101 -0
  311. package/src/helpers/keyboardControl.ts +58 -0
  312. package/src/helpers/responsive.ts +29 -0
  313. package/src/helpers/scroll.ts +16 -0
  314. package/src/index.ts +8 -0
  315. package/src/lib/i18n.ts +20 -0
  316. package/src/lib/locales/fr/design.json +42 -0
  317. package/src/styles/globals.css +205 -0
  318. package/src/types/svg.d.ts +8 -0
@@ -0,0 +1,45 @@
1
+ import clsx from 'clsx';
2
+
3
+ export const Illustration = ({
4
+ name,
5
+ variant = 'dark',
6
+ hasBorder = true,
7
+ hasBackground = true,
8
+ className = ''
9
+ }: {
10
+ name: string;
11
+ variant?: 'light' | 'dark';
12
+ hasBorder?: boolean;
13
+ hasBackground?: boolean;
14
+ className?: string;
15
+ }) => {
16
+ const imageVariant = variant === 'light' ? '-light' : '';
17
+
18
+ return (
19
+ <div className={clsx(
20
+ // Base rounded corners when border or background is present
21
+ (hasBorder || hasBackground) && 'rounded-card',
22
+ // Border styles
23
+ hasBorder && 'border',
24
+ // Background and padding styles
25
+ hasBackground && [
26
+ 'p-6',
27
+ variant === 'dark'
28
+ ? 'bg-beige-light text-claret-violet-dark'
29
+ : 'bg-claret-violet-dark text-beige-light'
30
+ ],
31
+ // Remove padding when neither border nor background
32
+ !hasBackground && !hasBorder && 'p-0',
33
+ className
34
+ )}>
35
+ {hasBackground && (
36
+ <p className="font-pangaia text-heading-md mb-3">
37
+ {' '}
38
+ {name}
39
+ {imageVariant}
40
+ </p>
41
+ )}
42
+ <img src={`/illustrations/${name}${imageVariant}.webp`} alt={name} />
43
+ </div>
44
+ );
45
+ };
@@ -0,0 +1,37 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { Typography } from './Typography';
4
+
5
+ const meta = {
6
+ title: 'Styles/Typography',
7
+ component: Typography,
8
+ globals: {
9
+ backgrounds: {
10
+ value: 'light',
11
+ },
12
+ },
13
+ parameters: {
14
+ layout: 'centered',
15
+ },
16
+ argTypes: {
17
+ variant: {
18
+ control: 'select',
19
+ options: ['pangaia', 'mori'],
20
+ },
21
+ },
22
+ } satisfies Meta<typeof Typography>;
23
+
24
+ export default meta;
25
+ type Story = StoryObj<typeof meta>;
26
+
27
+ export const Pangaia: Story = {
28
+ args: {
29
+ variant: 'pangaia',
30
+ },
31
+ };
32
+
33
+ export const Mori: Story = {
34
+ args: {
35
+ variant: 'mori',
36
+ },
37
+ };
@@ -0,0 +1,19 @@
1
+ import clsx from 'clsx';
2
+
3
+ export interface TypographyProps {
4
+ variant?: 'pangaia' | 'mori';
5
+ }
6
+
7
+ const variantStyles = {
8
+ pangaia: 'font-pangaia',
9
+ mori: 'font-mori',
10
+ };
11
+
12
+ export const Typography = ({ variant = 'pangaia' }: TypographyProps) => {
13
+ return (
14
+ <div className={clsx(variantStyles[variant], 'text-claret-violet-dark text-4xl')}>
15
+ <p>{variant}</p>
16
+ <p>{variant.toUpperCase()}</p>
17
+ </div>
18
+ );
19
+ };
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+
3
+ export const closeOthersAccordions = (details: HTMLDetailsElement) => {
4
+ document.querySelectorAll<HTMLDetailsElement>('details[data-accordion]').forEach((other) => {
5
+ if (other !== details) {
6
+ other.removeAttribute('data-open');
7
+ other.removeAttribute('open');
8
+ const content = other.querySelector<HTMLElement>('[data-accordion-content]');
9
+ if (content) {
10
+ content.style.maxHeight = '0px';
11
+ }
12
+ }
13
+ });
14
+ };
15
+
16
+ export const accordionAnimation = (e: Event, details: HTMLDetailsElement, content: HTMLElement, delay: number = 300) => {
17
+ e.preventDefault();
18
+
19
+ const isOpening = !details.hasAttribute('open');
20
+ if (isOpening) {
21
+ closeOthersAccordions(details);
22
+
23
+ details.setAttribute('open', '');
24
+
25
+ requestAnimationFrame(() => {
26
+ const contentHeight = content.scrollHeight;
27
+ content.style.transition = 'max-height 0.3s ease';
28
+ content.style.maxHeight = `${contentHeight}px`;
29
+ });
30
+ details.setAttribute('data-open', 'true');
31
+ } else {
32
+ content.style.transition = 'max-height 0.15s ease';
33
+ content.style.maxHeight = '0px';
34
+ details.removeAttribute('data-open');
35
+ setTimeout(() => {
36
+ details.removeAttribute('open');
37
+ }, delay);
38
+ }
39
+ };
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef } from 'react';
4
+
5
+ /**
6
+ * Calls `handler` when a pointer interaction finishes outside the element referenced by `ref`.
7
+ *
8
+ * The hook ensures the most recent `handler` is invoked even if it changes between renders.
9
+ *
10
+ * @param ref - React ref of the element to detect outside interactions for
11
+ * @param handler - Callback invoked with the originating `MouseEvent` or `TouchEvent` when a `pointerup` occurs outside the referenced element
12
+ */
13
+ export function useClickOutside<T extends HTMLElement>(ref: React.RefObject<T>, handler: (event: MouseEvent | TouchEvent) => void) {
14
+ const handlerRef = useRef(handler);
15
+ useEffect(() => {
16
+ handlerRef.current = handler;
17
+ }, [handler]);
18
+
19
+ useEffect(() => {
20
+ function listener(event: MouseEvent | TouchEvent) {
21
+ const el = ref.current;
22
+ if (!el || el.contains(event.target as Node)) return;
23
+ handlerRef.current(event);
24
+ }
25
+
26
+ document.addEventListener('pointerup', listener);
27
+ return () => {
28
+ document.removeEventListener('pointerup', listener);
29
+ };
30
+ }, [ref]);
31
+ }
@@ -0,0 +1,10 @@
1
+ export const debounce = (func: (...args: any[]) => void, wait: number) => {
2
+ let timeout: ReturnType<typeof setTimeout>;
3
+ return function executedFunction(...args: any[]) {
4
+ const later = () => {
5
+ func(...args);
6
+ };
7
+ clearTimeout(timeout);
8
+ timeout = setTimeout(later, wait);
9
+ };
10
+ };
@@ -0,0 +1,9 @@
1
+ import { accordionAnimation, closeOthersAccordions } from './accordions';
2
+ import { useClickOutside } from './clickoutside';
3
+ import { debounce } from './debounce';
4
+ import { useIntersectionObserver, useMultiIntersectionObserver } from './intersectionObserver';
5
+ import { onKeyDown, onNumericInput, useArrowNavigation } from './keyboardControl';
6
+ import { useIsDesktop, useIsMobile } from './responsive';
7
+ import { disableScroll, enableScroll } from './scroll';
8
+
9
+ export { useClickOutside, onKeyDown, useArrowNavigation, onNumericInput, closeOthersAccordions, accordionAnimation, useIsMobile, useIntersectionObserver, useMultiIntersectionObserver, debounce, disableScroll, enableScroll, useIsDesktop };
@@ -0,0 +1,101 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+ import type { RefObject } from 'react';
5
+
6
+ type UseSingleObserverOptions = {
7
+ ref: RefObject<HTMLElement>;
8
+ onAppear: () => void;
9
+ onDisappear: () => void;
10
+ threshold?: number;
11
+ };
12
+
13
+ type UseMultiObserverOptions = {
14
+ targets: Element[];
15
+ callback: (entry: IntersectionObserverEntry, element: HTMLElement) => void;
16
+ root?: Element | null;
17
+ threshold?: number;
18
+ rootMargin?: string;
19
+ };
20
+
21
+ /**
22
+ * Observes a single element and invokes callbacks when it enters or leaves the viewport threshold.
23
+ *
24
+ * @param ref - RefObject pointing to the target HTMLElement to observe
25
+ * @param onAppear - Callback invoked when the element becomes intersecting
26
+ * @param onDisappear - Callback invoked when the element stops being intersecting
27
+ * @param threshold - Intersection ratio threshold between 0 and 1 that determines when callbacks fire
28
+ */
29
+ export function useIntersectionObserver({ ref, onAppear, onDisappear, threshold = 0.5 }: UseSingleObserverOptions) {
30
+ useEffect(() => {
31
+ const element = ref.current;
32
+ if (!element) return;
33
+
34
+ const observer = new IntersectionObserver(
35
+ (entries) => {
36
+ entries.forEach((entry) => {
37
+ if (entry.isIntersecting) {
38
+ onAppear();
39
+ } else {
40
+ onDisappear();
41
+ }
42
+ });
43
+ },
44
+ { threshold },
45
+ );
46
+
47
+ observer.observe(element);
48
+
49
+ return () => {
50
+ observer.disconnect();
51
+ };
52
+ }, [onAppear, onDisappear, threshold]);
53
+ }
54
+
55
+ /**
56
+ * Observe all elements matching a CSS selector and invoke a callback for each element when it becomes visible.
57
+ *
58
+ * Observers are created for the currently matched elements and re-created on window resize to account for layout changes; observers are cleaned up on unmount or when inputs change.
59
+ *
60
+ * @param selector - CSS selector used to find elements to observe. If falsy, no observer is created.
61
+ * @param callback - Invoked for each intersecting entry as `callback(entry, element)`, where `entry` is the IntersectionObserverEntry and `element` is the observed HTMLElement.
62
+ * @param root - Optional ancestor element used as the viewport for intersection; `null` means the browser viewport.
63
+ * @param threshold - Intersection ratio at which the callback is triggered (e.g., `0.1`).
64
+ * @param rootMargin - Margin around the root bounding box (e.g., `'0px'`, `'10px 0px'`).
65
+ */
66
+ export function useMultiIntersectionObserver({ targets, callback, root = null, threshold = 0.1, rootMargin = '0px' }: UseMultiObserverOptions) {
67
+ useEffect(() => {
68
+ if (!targets?.length) return;
69
+
70
+ const createObserver = () => {
71
+ const observer = new IntersectionObserver(
72
+ (entries) => {
73
+ entries.forEach((entry) => {
74
+ if (entry.isIntersecting) {
75
+ const el = entry.target as HTMLElement;
76
+ callback(entry, el);
77
+ }
78
+ });
79
+ },
80
+ { root, threshold, rootMargin },
81
+ );
82
+
83
+ targets.forEach((el) => observer.observe(el));
84
+ return observer;
85
+ };
86
+
87
+ let observer = createObserver();
88
+
89
+ const handleResize = () => {
90
+ observer.disconnect();
91
+ observer = createObserver();
92
+ };
93
+
94
+ window.addEventListener('resize', handleResize);
95
+
96
+ return () => {
97
+ window.removeEventListener('resize', handleResize);
98
+ observer.disconnect();
99
+ };
100
+ }, [targets, root, threshold, rootMargin, callback]);
101
+ }
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef } from 'react';
4
+
5
+ export const onKeyDown = (e: React.KeyboardEvent<HTMLLIElement | HTMLLabelElement | HTMLDivElement>, callback: () => void) => {
6
+ if (e.key === 'Enter' || e.key === ' ') {
7
+ callback();
8
+ }
9
+ };
10
+
11
+ export function useArrowNavigation<T extends HTMLElement = HTMLElement>(selector: string) {
12
+ const containerRef = useRef<T | null>(null);
13
+
14
+ useEffect(() => {
15
+ const container = containerRef.current;
16
+ if (!container) return;
17
+
18
+ const handleKeyDown = (e: KeyboardEvent) => {
19
+ const items = Array.from(container.querySelectorAll<HTMLElement>(selector));
20
+ const currentIndex = items.indexOf(document.activeElement as HTMLElement);
21
+
22
+ if (items.length === 0) return;
23
+
24
+ switch (e.key) {
25
+ case 'ArrowDown': {
26
+ e.preventDefault();
27
+ const nextIndex = (currentIndex + 1) % items.length;
28
+ items[nextIndex].focus();
29
+ break;
30
+ }
31
+ case 'ArrowUp': {
32
+ e.preventDefault();
33
+ const prevIndex = (currentIndex - 1 + items.length) % items.length;
34
+ items[prevIndex].focus();
35
+ break;
36
+ }
37
+ }
38
+ };
39
+
40
+ container.addEventListener('keydown', handleKeyDown as EventListener);
41
+ return () => container.removeEventListener('keydown', handleKeyDown as EventListener);
42
+ }, [selector]);
43
+
44
+ return containerRef;
45
+ }
46
+
47
+ export const onNumericInput = (e: React.KeyboardEvent<HTMLInputElement>) => {
48
+ const allowed = ['Backspace', 'Delete', 'Tab', 'Escape', 'Enter', 'ArrowLeft', 'ArrowRight'];
49
+
50
+ if (allowed.includes(e.key)) {
51
+ return;
52
+ }
53
+
54
+ // Block everything except numbers
55
+ if (!/^[0-9]$/.test(e.key)) {
56
+ e.preventDefault();
57
+ }
58
+ };
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = useState(false);
7
+
8
+ useEffect(() => {
9
+ const check = () => setIsMobile(window.innerWidth < 768);
10
+ check();
11
+ window.addEventListener('resize', check);
12
+ return () => window.removeEventListener('resize', check);
13
+ }, []);
14
+
15
+ return isMobile;
16
+ }
17
+
18
+ export function useIsDesktop() {
19
+ const [isDesktop, setIsDesktop] = useState(false);
20
+
21
+ useEffect(() => {
22
+ const check = () => setIsDesktop(window.innerWidth >= 1024);
23
+ check();
24
+ window.addEventListener('resize', check);
25
+ return () => window.removeEventListener('resize', check);
26
+ }, []);
27
+
28
+ return isDesktop;
29
+ }
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+
3
+ export const disableScroll = () => {
4
+ const scrollY = window.scrollY;
5
+ document.body.style.position = 'fixed';
6
+ document.body.style.top = `-${scrollY}px`;
7
+ document.body.style.width = '100%';
8
+ };
9
+
10
+ export const enableScroll = () => {
11
+ const scrollY = document.body.style.top;
12
+ document.body.style.position = '';
13
+ document.body.style.top = '';
14
+ document.body.style.width = '';
15
+ window.scrollTo(0, parseInt(scrollY || '0') * -1);
16
+ };
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ //locales
2
+ export * from './lib/i18n';
3
+
4
+ // Components
5
+ export * from './components';
6
+
7
+ // Helpers
8
+ export * from './helpers';
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+
3
+ import i18n from 'i18next';
4
+ import { initReactI18next } from 'react-i18next';
5
+
6
+ import fr from './locales/fr/design.json';
7
+
8
+ if (!i18n.isInitialized) {
9
+ i18n.use(initReactI18next).init({
10
+ resources: { fr: { design: fr } },
11
+ lng: 'fr',
12
+ ns: ['design'],
13
+ defaultNS: 'design',
14
+ interpolation: { escapeValue: false },
15
+ });
16
+ }
17
+
18
+ export default i18n;
19
+
20
+ export { default as frDesign } from './locales/fr/design.json';
@@ -0,0 +1,42 @@
1
+ {
2
+ "homeVoucher": "PROMO : -{{percentage}}% SUR VOTRE PREMIÈRE COMMANDE + LIVRAISON OFFERTE",
3
+ "dropdown": {
4
+ "noResults": "Aucun résultat trouvé"
5
+ },
6
+ "navigation": {
7
+ "home": "Accueil",
8
+ "recipes": "Recettes",
9
+ "history": "Histoire",
10
+ "blog": "Blog",
11
+ "sponsorship": "Parrainage",
12
+ "signIn": "Se connecter",
13
+ "tryTwoWeeks": "Essayer 2 semaines",
14
+ "contact": "Contact",
15
+ "help": "Aide",
16
+ "dog": "Chien",
17
+ "cat": "Chat",
18
+ "homemadeRation": "Ration ménagère",
19
+ "ourStory": "Notre histoire",
20
+ "whyElmut": "Pourquoi Elmut",
21
+ "faq": "FAQ",
22
+ "legal": "Mentions légales",
23
+ "terms": "CGV",
24
+ "sitemap": "Plan du site",
25
+ "back": "Retour"
26
+ },
27
+ "footer": {
28
+ "copyright": "Tous droits réservés · Elmut"
29
+ },
30
+ "newsletter": {
31
+ "emailPlaceholder": "claudinethequeen@gmail.com",
32
+ "subscribeLabel": "S'abonner"
33
+ },
34
+ "wizard": {
35
+ "myPet": "Mon poilu",
36
+ "promotionBanner": "30% de réduction sur votre commande d'essai"
37
+ },
38
+ "tips": {
39
+ "claudineAlt": "Claudine",
40
+ "footerMessage": "Recevez régulièrement de mes nouvelles ! Et un peu d'Elmut aussi !"
41
+ }
42
+ }
@@ -0,0 +1,205 @@
1
+ @import 'tailwindcss';
2
+ @import 'tailwindcss/theme';
3
+
4
+ @font-face {
5
+ font-family: 'Pangaia';
6
+ src: url('../assets/fonts/PPPangaia-Medium.woff2') format('woff2');
7
+ font-weight: 500;
8
+ font-style: normal;
9
+ font-display: swap;
10
+ }
11
+
12
+ @font-face {
13
+ font-family: 'Pangaia';
14
+ src: url('../assets/fonts/PPPangaia-MediumItalic.woff2') format('woff2');
15
+ font-weight: 500;
16
+ font-style: italic;
17
+ font-display: swap;
18
+ }
19
+
20
+ @font-face {
21
+ font-family: 'Pangaia';
22
+ src: url('../assets/fonts/PPPangaia-Ultralight.woff2') format('woff2');
23
+ font-weight: 300;
24
+ font-style: normal;
25
+ font-display: swap;
26
+ }
27
+
28
+ @font-face {
29
+ font-family: 'Mori';
30
+ src: url('../assets/fonts/PPMori-Regular.woff2') format('woff2');
31
+ font-weight: 400;
32
+ font-style: normal;
33
+ font-display: swap;
34
+ }
35
+
36
+ @font-face {
37
+ font-family: 'Mori';
38
+ src: url('../assets/fonts/PPMori-SemiBold.woff2') format('woff2');
39
+ font-weight: 600;
40
+ font-style: normal;
41
+ font-display: swap;
42
+ }
43
+
44
+ @font-face {
45
+ font-family: 'Mori';
46
+ src: url('../assets/fonts/PPMori-RegularItalic.woff2') format('woff2');
47
+ font-weight: 400;
48
+ font-style: italic;
49
+ font-display: swap;
50
+ }
51
+
52
+ @theme {
53
+ /* Colors */
54
+ --color-claret-violet-dark: #671a36; /* Claret Violet 500 */
55
+ --color-claret-violet-light: #802948; /* Claret Violet 400 */
56
+ --color-pink-oyster-dark: #eeaeb4; /* Pink Oyster 600 */
57
+ --color-pink-oyster-light: #f7c8cc; /* Pink Oyster 500 */
58
+ --color-beige-ultra-light: #fffbf3; /* Beige Ultra Light */
59
+ --color-beige-dark: #f0e4c8; /* Beige 600 */
60
+ --color-beige-light: #f7efdc; /* Beige 500 */
61
+ --color-beige-ultra-dark: #dfd0ac; /* Beige ultra dark */
62
+ --color-lavender-blue-dark: #adadf0; /* Lavender Blue 600 */
63
+ --color-lavender-blue-light: #c3c3fe; /* Lavender Blue 500 */
64
+ --color-red-error: #b1002c;
65
+ /* Fonts */
66
+ --font-pangaia: 'Pangaia', serif;
67
+ --font-mori: 'Mori', sans-serif;
68
+
69
+ /* Title font sizes */
70
+ --text-display-xl: 8rem; /* Display 01 */
71
+ --text-display-xl-mobile: 2.5rem; /* Display */
72
+ --text-display-lg: 6rem; /* Display 02 */
73
+ --text-display-lg-mobile: 1.5rem; /* Heading 01 */
74
+ --text-display-md: 3rem; /* Display 03 */
75
+ --text-display-md-mobile: 1.5rem; /* Heading 01 italic */
76
+ --text-heading-xl: 4rem; /* Heading 01 */
77
+ --text-heading-xl-mobile: 1.25rem; /* Heading 02 */
78
+ --text-heading-lg: 3rem; /* Heading 02 */
79
+ --text-heading-lg-mobile: 1rem; /* Heading 03 | Big text when semibold */
80
+ --text-heading-md: 2rem; /* Heading 03 */
81
+ --text-heading-md-mobile: 0.875rem; /* Regular Text */
82
+
83
+ /* Body font sizes */
84
+ --text-xl: 1.25rem; /* Big text */
85
+ --text-xl-mobile: 0.875rem; /* Button */
86
+ --text-lg: 1.25rem; /* Big Text */
87
+ --text-lg-mobile: 0.75rem; /* Small Text */
88
+ --text-md: 1rem; /* Regular Text */
89
+ --text-md-mobile: 0.75rem; /* Small Text */
90
+ --text-sm: 0.875rem; /* Small Text */
91
+ --text-sm-mobile: 0.625rem; /* Overline */
92
+ --text-legend: 0.875rem; /* Legend */
93
+ --text-button: 1rem; /* Button */
94
+ --text-button-mobile: 0.875rem; /* Button */
95
+
96
+ /* Border radius */
97
+ --radius-card: 1.25rem;
98
+ --radius-illustrated-card-button: 1.125rem;
99
+ --radius-card-button-mobile: 0.625rem;
100
+ --radius-input: 0.625rem;
101
+ --radius-food-card: 1rem;
102
+ --radius-table: 1.875rem;
103
+
104
+ /* Custom breakpoints (affect all @md, @lg, etc. utilities) */
105
+ --breakpoint-xs: 155px;
106
+
107
+ /* Custom container widths */
108
+ --container-xs: 100vw;
109
+ --container-sm: 640px;
110
+ --container-md: 768px;
111
+ --container-lg: 1024px;
112
+ --container-xl: 1280px;
113
+ --container-2xl: 1536px;
114
+
115
+ /* animation */
116
+ --animate-fade-in: fade-in 300ms ease-in;
117
+ --animate-fade-out: fade-out 300ms ease-out;
118
+ --animate-banner: banner 15s linear infinite;
119
+ }
120
+
121
+ @layer utilities {
122
+ /* Delete the arrows on the number inputs */
123
+ input[type='number']::-webkit-inner-spin-button,
124
+ input[type='number']::-webkit-outer-spin-button {
125
+ @apply m-0 appearance-none;
126
+ }
127
+
128
+ input[type='number'] {
129
+ @apply [appearance:textfield]; /* standard + Firefox */
130
+ }
131
+
132
+ summary {
133
+ list-style: none;
134
+ }
135
+
136
+ summary::-webkit-details-marker {
137
+ display: none;
138
+ }
139
+
140
+ @keyframes fade-in {
141
+ from {
142
+ opacity: 0;
143
+ }
144
+ to {
145
+ opacity: 1;
146
+ }
147
+ }
148
+
149
+ @keyframes fade-out {
150
+ from {
151
+ opacity: 1;
152
+ }
153
+ to {
154
+ opacity: 0;
155
+ }
156
+ }
157
+
158
+ @keyframes banner {
159
+ from {
160
+ transform: translateX(0);
161
+ }
162
+ to {
163
+ transform: translateX(-50%);
164
+ }
165
+ }
166
+
167
+ .scrollbar-hide {
168
+ -ms-overflow-style: none; /* IE and Edge */
169
+ scrollbar-width: none; /* Firefox */
170
+ }
171
+
172
+ .scrollbar-hide::-webkit-scrollbar {
173
+ display: none; /* Chrome, Safari, Opera */
174
+ }
175
+ }
176
+
177
+ @utility container {
178
+ width: 100%;
179
+ margin-inline: auto;
180
+ padding-inline: 1.5rem;
181
+ max-width: var(--container-xs);
182
+
183
+ @variant sm {
184
+ max-width: var(--container-sm);
185
+ }
186
+
187
+ @variant md {
188
+ padding-inline: 2rem;
189
+ max-width: var(--container-md);
190
+ }
191
+
192
+ @variant lg {
193
+ max-width: var(--container-lg);
194
+ }
195
+
196
+ @variant xl {
197
+ padding-inline: 3.75rem;
198
+ max-width: var(--container-xl);
199
+ }
200
+
201
+ @variant 2xl {
202
+ padding-inline: 5rem;
203
+ max-width: var(--container-2xl);
204
+ }
205
+ }