@telefonica/mistica 16.56.0 → 16.57.0-beta.1

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 (295) hide show
  1. package/README.md +31 -0
  2. package/css/mistica.css +1 -1
  3. package/dist/accordion.css-mistica.js +16 -16
  4. package/dist/align.css-mistica.js +2 -2
  5. package/dist/autocomplete.css-mistica.js +6 -6
  6. package/dist/avatar.css-mistica.js +3 -3
  7. package/dist/badge.css-mistica.js +7 -7
  8. package/dist/box.css-mistica.js +15 -15
  9. package/dist/boxed.css-mistica.js +31 -31
  10. package/dist/button-group.css-mistica.js +10 -10
  11. package/dist/button-layout.css-mistica.js +21 -21
  12. package/dist/button.css-mistica.js +51 -51
  13. package/dist/callout.css-mistica.js +16 -16
  14. package/dist/card-internal.css-mistica.js +36 -36
  15. package/dist/carousel.css-mistica.js +18 -18
  16. package/dist/checkbox.css-mistica.js +21 -21
  17. package/dist/chip.css-mistica.js +30 -30
  18. package/dist/circle.css-mistica.js +2 -2
  19. package/dist/community/advanced-data-card.css-mistica.js +26 -26
  20. package/dist/community/blocks.css-mistica.js +3 -3
  21. package/dist/community/example-component.css-mistica.js +2 -2
  22. package/dist/counter.css-mistica.js +12 -12
  23. package/dist/cover-hero.css-mistica.js +16 -16
  24. package/dist/credit-card-number-field.css-mistica.js +6 -6
  25. package/dist/date-field.css-mistica.js +1 -1
  26. package/dist/date-time-picker.css-mistica.js +2 -2
  27. package/dist/dialog.css-mistica.js +15 -15
  28. package/dist/divider.css-mistica.js +5 -5
  29. package/dist/double-field.css-mistica.js +4 -4
  30. package/dist/drawer.css-mistica.js +15 -15
  31. package/dist/empty-state-card.css-mistica.js +4 -4
  32. package/dist/empty-state.css-mistica.js +14 -14
  33. package/dist/fade-in.css-mistica.js +1 -1
  34. package/dist/feedback.css-mistica.js +14 -14
  35. package/dist/file-upload.css-mistica.js +14 -14
  36. package/dist/fixed-footer-layout.css-mistica.js +12 -12
  37. package/dist/form.css-mistica.js +2 -2
  38. package/dist/grid-layout.css-mistica.js +9 -9
  39. package/dist/grid.css-mistica.js +147 -147
  40. package/dist/header.css-mistica.js +5 -5
  41. package/dist/hero.css-mistica.js +11 -11
  42. package/dist/horizontal-scroll.css-mistica.js +3 -3
  43. package/dist/icon-button.css-mistica.js +66 -66
  44. package/dist/icons/icon-chevron.css-mistica.js +6 -6
  45. package/dist/icons/icon-error.css-mistica.js +3 -3
  46. package/dist/image.css-mistica.js +11 -11
  47. package/dist/inline.css-mistica.js +16 -16
  48. package/dist/list.css-mistica.js +15 -15
  49. package/dist/loading-bar.css-mistica.js +5 -5
  50. package/dist/loading-screen.css-mistica.js +15 -15
  51. package/dist/logo-blau-shell.d.ts +10 -0
  52. package/dist/logo-blau-shell.js +25 -0
  53. package/dist/logo-blau.d.ts +1 -1
  54. package/dist/logo-blau.js +25 -40
  55. package/dist/logo-common.d.ts +0 -1
  56. package/dist/logo-esimflag-shell.d.ts +10 -0
  57. package/dist/logo-esimflag-shell.js +25 -0
  58. package/dist/logo-esimflag.d.ts +1 -1
  59. package/dist/logo-esimflag.js +11 -26
  60. package/dist/logo-movistar-new-shell.d.ts +10 -0
  61. package/dist/logo-movistar-new-shell.js +25 -0
  62. package/dist/logo-movistar-new.d.ts +1 -1
  63. package/dist/logo-movistar-new.js +84 -103
  64. package/dist/logo-movistar-shell.d.ts +10 -0
  65. package/dist/logo-movistar-shell.js +25 -0
  66. package/dist/logo-movistar.d.ts +1 -1
  67. package/dist/logo-movistar.js +16 -31
  68. package/dist/logo-o2-new-shell.d.ts +8 -0
  69. package/dist/logo-o2-new-shell.js +24 -0
  70. package/dist/logo-o2-new.d.ts +1 -1
  71. package/dist/logo-o2-new.js +6 -13
  72. package/dist/logo-o2-shell.d.ts +8 -0
  73. package/dist/logo-o2-shell.js +24 -0
  74. package/dist/logo-o2.d.ts +1 -1
  75. package/dist/logo-o2.js +6 -13
  76. package/dist/logo-telefonica-shell.d.ts +10 -0
  77. package/dist/logo-telefonica-shell.js +25 -0
  78. package/dist/logo-telefonica.d.ts +1 -1
  79. package/dist/logo-telefonica.js +11 -26
  80. package/dist/logo-tu-shell.d.ts +8 -0
  81. package/dist/logo-tu-shell.js +24 -0
  82. package/dist/logo-tu.d.ts +1 -1
  83. package/dist/logo-tu.js +10 -14
  84. package/dist/logo-vivo-shell.d.ts +10 -0
  85. package/dist/logo-vivo-shell.js +25 -0
  86. package/dist/logo-vivo.d.ts +1 -1
  87. package/dist/logo-vivo.js +11 -26
  88. package/dist/logo.css-mistica.js +9 -9
  89. package/dist/logo.js +223 -205
  90. package/dist/menu.css-mistica.js +24 -24
  91. package/dist/mosaic.css-mistica.js +3 -3
  92. package/dist/navigation-bar.css-mistica.js +45 -45
  93. package/dist/navigation-breadcrumbs.css-mistica.js +5 -5
  94. package/dist/package-version.js +2 -2
  95. package/dist/pin-field.css-mistica.js +10 -10
  96. package/dist/popover.css-mistica.js +2 -2
  97. package/dist/progress-bar.css-mistica.js +12 -12
  98. package/dist/radio-button.css-mistica.js +33 -33
  99. package/dist/rating.css-mistica.js +12 -12
  100. package/dist/responsive-layout.css-mistica.js +20 -20
  101. package/dist/screen-reader-only.css-mistica.js +2 -2
  102. package/dist/select.css-mistica.js +29 -29
  103. package/dist/sheet-action-row.css-mistica.js +2 -2
  104. package/dist/sheet-common.css-mistica.js +16 -16
  105. package/dist/sheet-info.css-mistica.js +4 -4
  106. package/dist/skeletons.css-mistica.js +12 -12
  107. package/dist/skins/skin-contract.css-mistica.js +686 -686
  108. package/dist/skip-link.css-mistica.js +3 -3
  109. package/dist/slider.css-mistica.js +28 -28
  110. package/dist/snackbar.css-mistica.js +16 -16
  111. package/dist/spinner.css-mistica.js +5 -5
  112. package/dist/square.css-mistica.js +2 -2
  113. package/dist/stack.css-mistica.js +9 -9
  114. package/dist/stacking-group.css-mistica.js +2 -2
  115. package/dist/stepper.css-mistica.js +16 -16
  116. package/dist/switch-component.css-mistica.js +53 -53
  117. package/dist/table.css-mistica.js +24 -24
  118. package/dist/tabs.css-mistica.js +30 -30
  119. package/dist/tag.css-mistica.js +5 -5
  120. package/dist/text-field-base.css-mistica.js +30 -30
  121. package/dist/text-field-base.js +52 -51
  122. package/dist/text-field-components.css-mistica.js +20 -17
  123. package/dist/text-field-components.css.d.ts +1 -0
  124. package/dist/text-field-components.d.ts +2 -1
  125. package/dist/text-field-components.js +25 -25
  126. package/dist/text-link.css-mistica.js +10 -10
  127. package/dist/text.css-mistica.js +13 -13
  128. package/dist/theme-context.css-mistica.js +2 -2
  129. package/dist/timeline.css-mistica.js +18 -18
  130. package/dist/timer.css-mistica.js +13 -13
  131. package/dist/tooltip.css-mistica.js +12 -12
  132. package/dist/touchable.css-mistica.js +5 -5
  133. package/dist/utils/aspect-ratio-support.css-mistica.js +7 -7
  134. package/dist/video.css-mistica.js +2 -2
  135. package/dist/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +3 -3
  136. package/dist-es/accordion.css-mistica.js +7 -7
  137. package/dist-es/align.css-mistica.js +2 -2
  138. package/dist-es/autocomplete.css-mistica.js +2 -2
  139. package/dist-es/avatar.css-mistica.js +2 -2
  140. package/dist-es/badge.css-mistica.js +2 -2
  141. package/dist-es/box.css-mistica.js +15 -15
  142. package/dist-es/boxed.css-mistica.js +25 -25
  143. package/dist-es/button-group.css-mistica.js +2 -2
  144. package/dist-es/button-layout.css-mistica.js +16 -16
  145. package/dist-es/button.css-mistica.js +38 -38
  146. package/dist-es/callout.css-mistica.js +12 -12
  147. package/dist-es/card-internal.css-mistica.js +20 -20
  148. package/dist-es/carousel.css-mistica.js +10 -10
  149. package/dist-es/checkbox.css-mistica.js +14 -14
  150. package/dist-es/chip.css-mistica.js +17 -17
  151. package/dist-es/circle.css-mistica.js +2 -2
  152. package/dist-es/community/advanced-data-card.css-mistica.js +7 -7
  153. package/dist-es/community/blocks.css-mistica.js +2 -2
  154. package/dist-es/community/example-component.css-mistica.js +2 -2
  155. package/dist-es/counter.css-mistica.js +2 -2
  156. package/dist-es/cover-hero.css-mistica.js +4 -4
  157. package/dist-es/credit-card-number-field.css-mistica.js +4 -4
  158. package/dist-es/date-field.css-mistica.js +1 -1
  159. package/dist-es/date-time-picker.css-mistica.js +2 -2
  160. package/dist-es/dialog.css-mistica.js +5 -5
  161. package/dist-es/divider.css-mistica.js +5 -5
  162. package/dist-es/double-field.css-mistica.js +4 -4
  163. package/dist-es/drawer.css-mistica.js +2 -2
  164. package/dist-es/empty-state-card.css-mistica.js +2 -2
  165. package/dist-es/empty-state.css-mistica.js +7 -7
  166. package/dist-es/fade-in.css-mistica.js +1 -1
  167. package/dist-es/feedback.css-mistica.js +2 -2
  168. package/dist-es/file-upload.css-mistica.js +8 -8
  169. package/dist-es/fixed-footer-layout.css-mistica.js +4 -4
  170. package/dist-es/form.css-mistica.js +2 -2
  171. package/dist-es/grid-layout.css-mistica.js +4 -4
  172. package/dist-es/grid.css-mistica.js +127 -127
  173. package/dist-es/header.css-mistica.js +2 -2
  174. package/dist-es/hero.css-mistica.js +4 -4
  175. package/dist-es/horizontal-scroll.css-mistica.js +2 -2
  176. package/dist-es/icon-button.css-mistica.js +56 -56
  177. package/dist-es/icons/icon-chevron.css-mistica.js +4 -4
  178. package/dist-es/icons/icon-error.css-mistica.js +2 -2
  179. package/dist-es/image.css-mistica.js +4 -4
  180. package/dist-es/inline.css-mistica.js +10 -10
  181. package/dist-es/list.css-mistica.js +2 -2
  182. package/dist-es/loading-bar.css-mistica.js +2 -2
  183. package/dist-es/loading-screen.css-mistica.js +5 -5
  184. package/dist-es/logo-blau-shell.js +16 -0
  185. package/dist-es/logo-blau.js +36 -51
  186. package/dist-es/logo-esimflag-shell.js +16 -0
  187. package/dist-es/logo-esimflag.js +13 -28
  188. package/dist-es/logo-movistar-new-shell.js +16 -0
  189. package/dist-es/logo-movistar-new.js +86 -105
  190. package/dist-es/logo-movistar-shell.js +16 -0
  191. package/dist-es/logo-movistar.js +18 -33
  192. package/dist-es/logo-o2-new-shell.js +15 -0
  193. package/dist-es/logo-o2-new.js +7 -14
  194. package/dist-es/logo-o2-shell.js +15 -0
  195. package/dist-es/logo-o2.js +7 -14
  196. package/dist-es/logo-telefonica-shell.js +16 -0
  197. package/dist-es/logo-telefonica.js +13 -28
  198. package/dist-es/logo-tu-shell.js +15 -0
  199. package/dist-es/logo-tu.js +12 -16
  200. package/dist-es/logo-vivo-shell.js +16 -0
  201. package/dist-es/logo-vivo.js +13 -28
  202. package/dist-es/logo.css-mistica.js +7 -7
  203. package/dist-es/logo.js +221 -203
  204. package/dist-es/menu.css-mistica.js +15 -15
  205. package/dist-es/mosaic.css-mistica.js +2 -2
  206. package/dist-es/navigation-bar.css-mistica.js +20 -20
  207. package/dist-es/navigation-breadcrumbs.css-mistica.js +2 -2
  208. package/dist-es/package-version.js +2 -2
  209. package/dist-es/pin-field.css-mistica.js +2 -2
  210. package/dist-es/popover.css-mistica.js +2 -2
  211. package/dist-es/progress-bar.css-mistica.js +8 -8
  212. package/dist-es/radio-button.css-mistica.js +25 -25
  213. package/dist-es/rating.css-mistica.js +4 -4
  214. package/dist-es/responsive-layout.css-mistica.js +7 -7
  215. package/dist-es/screen-reader-only.css-mistica.js +2 -2
  216. package/dist-es/select.css-mistica.js +20 -20
  217. package/dist-es/sheet-action-row.css-mistica.js +2 -2
  218. package/dist-es/sheet-common.css-mistica.js +2 -2
  219. package/dist-es/sheet-info.css-mistica.js +2 -2
  220. package/dist-es/skeletons.css-mistica.js +8 -8
  221. package/dist-es/skins/skin-contract.css-mistica.js +686 -686
  222. package/dist-es/skip-link.css-mistica.js +2 -2
  223. package/dist-es/slider.css-mistica.js +20 -20
  224. package/dist-es/snackbar.css-mistica.js +5 -5
  225. package/dist-es/spinner.css-mistica.js +2 -2
  226. package/dist-es/square.css-mistica.js +2 -2
  227. package/dist-es/stack.css-mistica.js +7 -7
  228. package/dist-es/stacking-group.css-mistica.js +2 -2
  229. package/dist-es/stepper.css-mistica.js +4 -4
  230. package/dist-es/style.css +1 -1
  231. package/dist-es/switch-component.css-mistica.js +41 -41
  232. package/dist-es/table.css-mistica.js +11 -11
  233. package/dist-es/tabs.css-mistica.js +21 -21
  234. package/dist-es/tag.css-mistica.js +2 -2
  235. package/dist-es/text-field-base.css-mistica.js +17 -17
  236. package/dist-es/text-field-base.js +52 -51
  237. package/dist-es/text-field-components.css-mistica.js +4 -4
  238. package/dist-es/text-field-components.js +52 -52
  239. package/dist-es/text-link.css-mistica.js +9 -9
  240. package/dist-es/text.css-mistica.js +8 -8
  241. package/dist-es/theme-context.css-mistica.js +2 -2
  242. package/dist-es/timeline.css-mistica.js +11 -11
  243. package/dist-es/timer.css-mistica.js +7 -7
  244. package/dist-es/tooltip.css-mistica.js +3 -3
  245. package/dist-es/touchable.css-mistica.js +2 -2
  246. package/dist-es/utils/aspect-ratio-support.css-mistica.js +4 -4
  247. package/dist-es/video.css-mistica.js +2 -2
  248. package/dist-es/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +2 -2
  249. package/doc/analytics.md +145 -0
  250. package/doc/components.md +730 -0
  251. package/doc/design-tokens.md +199 -0
  252. package/doc/fonts.md +287 -0
  253. package/doc/forms.md +177 -0
  254. package/doc/images/layout/box.svg +6 -0
  255. package/doc/images/layout/grid-layout-desktop-5-4.svg +8 -0
  256. package/doc/images/layout/grid-layout-desktop-6-6.svg +8 -0
  257. package/doc/images/layout/grid-layout-desktop-8-4.svg +8 -0
  258. package/doc/images/layout/grid-layout-desktop.svg +16 -0
  259. package/doc/images/layout/grid-layout-mobile-5-4.svg +9 -0
  260. package/doc/images/layout/grid-layout-mobile-6-6.svg +9 -0
  261. package/doc/images/layout/grid-layout-mobile-8-4.svg +9 -0
  262. package/doc/images/layout/grid-layout-mobile.svg +5 -0
  263. package/doc/images/layout/grid-layout-tablet-5-4.svg +8 -0
  264. package/doc/images/layout/grid-layout-tablet-6-6.svg +8 -0
  265. package/doc/images/layout/grid-layout-tablet-8-4.svg +8 -0
  266. package/doc/images/layout/grid-layout-tablet.svg +5 -0
  267. package/doc/images/layout/header-layout-desktop.svg +6 -0
  268. package/doc/images/layout/header-layout-mobile.svg +6 -0
  269. package/doc/images/layout/header-layout-tablet.svg +6 -0
  270. package/doc/images/layout/inline-around.svg +6 -0
  271. package/doc/images/layout/inline-between.svg +6 -0
  272. package/doc/images/layout/inline-evenly.svg +6 -0
  273. package/doc/images/layout/inline.svg +6 -0
  274. package/doc/images/layout/master-detail-layout-desktop.svg +8 -0
  275. package/doc/images/layout/master-detail-layout-mobile-detail.svg +6 -0
  276. package/doc/images/layout/master-detail-layout-mobile-master.svg +6 -0
  277. package/doc/images/layout/negative-box-ok-outline.svg +17 -0
  278. package/doc/images/layout/negative-box-ok-preview.svg +11 -0
  279. package/doc/images/layout/negative-box-wrong-outline.svg +14 -0
  280. package/doc/images/layout/negative-box-wrong-preview.svg +11 -0
  281. package/doc/images/layout/responsive-layout-desktop.svg +4 -0
  282. package/doc/images/layout/responsive-layout-mobile.svg +4 -0
  283. package/doc/images/layout/responsive-layout-tablet.svg +4 -0
  284. package/doc/images/layout/stack.svg +8 -0
  285. package/doc/images/layout/vertical-rhythm.png +0 -0
  286. package/doc/layout.md +527 -0
  287. package/doc/llms.md +290 -0
  288. package/doc/lottie.md +17 -0
  289. package/doc/migration-guide.md +76 -0
  290. package/doc/patterns.md +548 -0
  291. package/doc/sheet.md +122 -0
  292. package/doc/testing.md +43 -0
  293. package/doc/texts.md +42 -0
  294. package/doc/theme-config.md +124 -0
  295. package/package.json +2 -1
@@ -0,0 +1,548 @@
1
+ # Patterns and Best Practices
2
+
3
+ ## Critical rules
4
+
5
+ 1. **NEVER hardcode colors.** Always use `skinVars.colors.*` from `@telefonica/mistica`.
6
+ 2. **NEVER use raw `<div>` for layout.** Use `Box`, `Stack`, `Inline`, `ResponsiveLayout`, `GridLayout`,
7
+ `Grid`.
8
+ 3. **NEVER set font sizes manually.** Use text components (`Text1`-`Text10`, `Title1`-`Title4`).
9
+ 4. **NEVER set border radius manually.** Use `skinVars.borderRadii.*` or components that handle it (`Boxed`,
10
+ cards, etc.).
11
+ 5. **Always wrap your app** with `ThemeContextProvider` and import `@telefonica/mistica/css/mistica.css`.
12
+ 6. **Always namespace React hooks**: `React.useState`, `React.useEffect`, `React.useRef`, etc.
13
+ 7. **Add `'use client';`** directive to client components when using Next.js app router.
14
+ 8. **Set `font-family` and `body` background color.** See [llms.md](./llms.md) rules 9–10 and
15
+ [fonts.md](./fonts.md) for the per-skin font table, `@font-face` setup, and the `GlobalStyles` pattern.
16
+
17
+ ## Page layout composition
18
+
19
+ A standard Mistica page follows this structure:
20
+
21
+ ```tsx
22
+ // Navigation
23
+ <MainNavigationBar ... />
24
+
25
+ // Header section
26
+ <HeaderLayout
27
+ header={<Header pretitle="Section" title="Page Title" description="Description" />}
28
+ />
29
+
30
+ // Content sections
31
+ <ResponsiveLayout>
32
+ <Box paddingY={24}>
33
+ <Stack space={32}>
34
+ {/* Section 1 */}
35
+ <Stack space={16}>
36
+ <Title1 as="h2">Section Title</Title1>
37
+ <Text2 regular as="p">Section description</Text2>
38
+ </Stack>
39
+
40
+ {/* Section 2 - List */}
41
+ <Stack space={16}>
42
+ <Title1 as="h2">Another Section</Title1>
43
+ <NegativeBox>
44
+ <RowList>
45
+ <Row title="Item 1" onPress={() => {}} />
46
+ <Row title="Item 2" onPress={() => {}} />
47
+ </RowList>
48
+ </NegativeBox>
49
+ </Stack>
50
+ </Stack>
51
+ </Box>
52
+ </ResponsiveLayout>
53
+ ```
54
+
55
+ ### Vertical rhythm
56
+
57
+ Follow the 24/32/16 rule:
58
+
59
+ - **Containers**: 24px top and bottom padding (`<Box paddingY={24}>`)
60
+ - **Sections**: 32px space between them (`<Stack space={32}>`)
61
+ - **Elements**: 16px space between them (`<Stack space={16}>`)
62
+
63
+ ## Layout dos and don'ts
64
+
65
+ ### DO: Use layout components
66
+
67
+ ```tsx
68
+ // Vertical spacing
69
+ <Stack space={16}>
70
+ <Text2 regular>First</Text2>
71
+ <Text2 regular>Second</Text2>
72
+ </Stack>
73
+
74
+ // Horizontal spacing
75
+ <Inline space={16}>
76
+ <ButtonPrimary onPress={() => {}}>Accept</ButtonPrimary>
77
+ <ButtonSecondary onPress={() => {}}>Cancel</ButtonSecondary>
78
+ </Inline>
79
+
80
+ // Padding
81
+ <Box padding={16}>
82
+ <Text2 regular>Padded content</Text2>
83
+ </Box>
84
+
85
+ // Page container
86
+ <ResponsiveLayout>
87
+ <Text2 regular>Responsive content</Text2>
88
+ </ResponsiveLayout>
89
+ ```
90
+
91
+ ### DON'T: Use divs for spacing/layout
92
+
93
+ ```tsx
94
+ // BAD - raw divs for spacing
95
+ <div style={{display: 'flex', flexDirection: 'column', gap: 16}}>
96
+ <div style={{padding: 16}}>
97
+ <span style={{fontSize: 14, color: '#333'}}>Text</span>
98
+ </div>
99
+ </div>
100
+
101
+ // GOOD - Mistica components
102
+ <Stack space={16}>
103
+ <Box padding={16}>
104
+ <Text2 regular>Text</Text2>
105
+ </Box>
106
+ </Stack>
107
+ ```
108
+
109
+ ## Color dos and don'ts
110
+
111
+ ### DO: Use design tokens
112
+
113
+ ```tsx
114
+ import {skinVars, applyAlpha} from '@telefonica/mistica';
115
+
116
+ // Direct token usage
117
+ <Text2 regular color={skinVars.colors.textSecondary}>Secondary text</Text2>
118
+
119
+ // In styles (when absolutely needed)
120
+ <div style={{backgroundColor: skinVars.colors.backgroundContainer}}>...</div>
121
+
122
+ // Semi-transparent colors
123
+ const overlay = applyAlpha(skinVars.rawColors.backgroundBrand, 0.8);
124
+ ```
125
+
126
+ ### DON'T: Hardcode colors
127
+
128
+ ```tsx
129
+ // BAD - hardcoded colors
130
+ <Text2 regular color="#666">Text</Text2>
131
+ <div style={{backgroundColor: '#f5f5f5'}}>...</div>
132
+ <div style={{color: 'rgb(0, 112, 224)'}}>...</div>
133
+
134
+ // GOOD - design tokens
135
+ <Text2 regular color={skinVars.colors.textSecondary}>Text</Text2>
136
+ <Boxed><Text2 regular>Content in container</Text2></Boxed>
137
+ ```
138
+
139
+ ## Responsive patterns
140
+
141
+ ### Conditional rendering by screen size
142
+
143
+ ```tsx
144
+ const {isDesktopOrBigger, isTabletOrSmaller} = useScreenSize();
145
+
146
+ return (
147
+ <ResponsiveLayout>
148
+ <Box paddingY={24}>
149
+ {isDesktopOrBigger ? (
150
+ <GridLayout template="6+6" left={<LeftContent />} right={<RightContent />} />
151
+ ) : (
152
+ <Stack space={16}>
153
+ <LeftContent />
154
+ <RightContent />
155
+ </Stack>
156
+ )}
157
+ </Box>
158
+ </ResponsiveLayout>
159
+ );
160
+ ```
161
+
162
+ ### Grid templates for desktop/mobile
163
+
164
+ ```tsx
165
+ <ResponsiveLayout>
166
+ <GridLayout template="8+4" left={<MainContent />} right={<Sidebar />} />
167
+ </ResponsiveLayout>
168
+ ```
169
+
170
+ On mobile, GridLayout stacks content vertically automatically.
171
+
172
+ ### Master-detail pattern
173
+
174
+ ```tsx
175
+ const [selectedId, setSelectedId] = React.useState(null);
176
+
177
+ <MasterDetailLayout
178
+ isOpen={!!selectedId}
179
+ master={
180
+ <RowList>
181
+ {items.map((item) => (
182
+ <Row key={item.id} title={item.name} onPress={() => setSelectedId(item.id)} />
183
+ ))}
184
+ </RowList>
185
+ }
186
+ detail={selectedId ? <DetailView id={selectedId} /> : <Text2 regular>Select an item</Text2>}
187
+ />;
188
+ ```
189
+
190
+ ## Form patterns
191
+
192
+ ### Automatic state management (preferred)
193
+
194
+ ```tsx
195
+ <Form initialValues={{email: '', name: ''}} onSubmit={(formData) => api.submit(formData)}>
196
+ <Stack space={16}>
197
+ <TextField name="name" label="Name" />
198
+ <EmailField name="email" label="Email" />
199
+ <PhoneNumberField name="phone" label="Phone" optional />
200
+ <DoubleField>
201
+ <DateField name="startDate" label="Start date" />
202
+ <DateField name="endDate" label="End date" />
203
+ </DoubleField>
204
+ <Select
205
+ name="country"
206
+ label="Country"
207
+ options={[
208
+ {value: 'es', text: 'Spain'},
209
+ {value: 'uk', text: 'United Kingdom'},
210
+ ]}
211
+ />
212
+ <Switch name="newsletter">Receive newsletter</Switch>
213
+ <ButtonLayout
214
+ primaryButton={
215
+ <ButtonPrimary submit loadingText="Sending...">
216
+ Submit
217
+ </ButtonPrimary>
218
+ }
219
+ />
220
+ </Stack>
221
+ </Form>
222
+ ```
223
+
224
+ ### Form with fixed footer
225
+
226
+ ```tsx
227
+ <ButtonFixedFooterLayout button={<ButtonPrimary submit>Continue</ButtonPrimary>}>
228
+ <ResponsiveLayout>
229
+ <Box paddingY={24}>
230
+ <Form onSubmit={handleSubmit}>
231
+ <Stack space={16}>
232
+ <TextField name="name" label="Name" />
233
+ <EmailField name="email" label="Email" />
234
+ </Stack>
235
+ </Form>
236
+ </Box>
237
+ </ResponsiveLayout>
238
+ </ButtonFixedFooterLayout>
239
+ ```
240
+
241
+ ## Carousel patterns
242
+
243
+ `Carousel`, `CenteredCarousel`, and `Slideshow` are **horizontal-scroll** components. Use them when content
244
+ needs to be navigated left-to-right (e.g. product cards, promotional banners, image galleries).
245
+
246
+ ### Carousel vs CenteredCarousel vs Slideshow
247
+
248
+ | Component | Best for | Visible items |
249
+ | ------------------ | -------------------------------------------------- | --------------------------- |
250
+ | `Carousel` | Card collections, product grids | Configurable per breakpoint |
251
+ | `CenteredCarousel` | Featured/highlighted items with neighbour peek | 1 on mobile, 3 on desktop |
252
+ | `Slideshow` | Full-width hero banners, autoplay image slideshows | 1 at a time |
253
+
254
+ ### Placement rules
255
+
256
+ - **`Carousel` and `CenteredCarousel`**: place inside `ResponsiveLayout` so they respect the page grid.
257
+ - **`Slideshow`**: place **outside** `ResponsiveLayout` — it bleeds full-width on mobile and tablet
258
+ automatically.
259
+
260
+ ```tsx
261
+ {
262
+ /* Carousel inside ResponsiveLayout */
263
+ }
264
+ <ResponsiveLayout>
265
+ <Box paddingY={24}>
266
+ <Stack space={16}>
267
+ <Title2 as="h2">Featured products</Title2>
268
+ <Carousel
269
+ itemsPerPage={{mobile: 1, tablet: 2, desktop: 3}}
270
+ withBullets
271
+ items={products.map((p, i) => (
272
+ <DataCard
273
+ key={i}
274
+ title={p.name}
275
+ description={p.description}
276
+ buttonPrimary={
277
+ <ButtonPrimary small onPress={() => navigate(p.url)}>
278
+ View
279
+ </ButtonPrimary>
280
+ }
281
+ />
282
+ ))}
283
+ />
284
+ </Stack>
285
+ </Box>
286
+ </ResponsiveLayout>;
287
+
288
+ {
289
+ /* Slideshow at page level (outside ResponsiveLayout) */
290
+ }
291
+ <Slideshow
292
+ withBullets
293
+ autoplay={{time: 5000, loop: true}}
294
+ items={[
295
+ <CoverCard key="1" imageSrc="/banner1.jpg" title="Offer 1" />,
296
+ <CoverCard key="2" imageSrc="/banner2.jpg" title="Offer 2" />,
297
+ ]}
298
+ />;
299
+ ```
300
+
301
+ ## Card patterns
302
+
303
+ ### Asset pattern for cards and rows
304
+
305
+ The idiomatic way to create card/row assets is `Circle` + icon:
306
+
307
+ ```tsx
308
+ <Circle backgroundColor={skinVars.colors.brandLow} size={40}>
309
+ <IconShopRegular color={skinVars.colors.brand} />
310
+ </Circle>
311
+ ```
312
+
313
+ ### Card grid
314
+
315
+ ```tsx
316
+ <ResponsiveLayout>
317
+ <Box paddingY={24}>
318
+ <Stack space={16}>
319
+ <Title1 as="h2">Featured</Title1>
320
+ <Carousel
321
+ itemsPerPage={{mobile: 1, tablet: 2, desktop: 3}}
322
+ items={products.map((p) => (
323
+ <DataCard
324
+ key={p.id}
325
+ asset={
326
+ <Circle backgroundColor={skinVars.colors.brandLow} size={40}>
327
+ <IconShopRegular color={skinVars.colors.brand} />
328
+ </Circle>
329
+ }
330
+ title={p.name}
331
+ description={p.description}
332
+ buttonPrimary={
333
+ <ButtonPrimary small onPress={() => navigate(p.url)}>
334
+ View
335
+ </ButtonPrimary>
336
+ }
337
+ />
338
+ ))}
339
+ />
340
+ </Stack>
341
+ </Box>
342
+ </ResponsiveLayout>
343
+ ```
344
+
345
+ ## List patterns
346
+
347
+ ### Unbounded list with NegativeBox
348
+
349
+ When placing a `RowList` inside a `ResponsiveLayout`, wrap it with `NegativeBox` so hover effects and
350
+ alignment are correct:
351
+
352
+ ```tsx
353
+ <ResponsiveLayout>
354
+ <Box paddingY={24}>
355
+ <Stack space={16}>
356
+ <Title1>Settings</Title1>
357
+ <NegativeBox>
358
+ <RowList>
359
+ <Row
360
+ asset={
361
+ <Circle backgroundColor={skinVars.colors.brandLow} size={40}>
362
+ <IconSettingsRegular color={skinVars.colors.brand} />
363
+ </Circle>
364
+ }
365
+ title="General"
366
+ description="App settings"
367
+ onPress={() => {}}
368
+ />
369
+ <Row
370
+ asset={
371
+ <Circle backgroundColor={skinVars.colors.brandLow} size={40}>
372
+ <IconBellRegular color={skinVars.colors.brand} />
373
+ </Circle>
374
+ }
375
+ title="Notifications"
376
+ description="Manage alerts"
377
+ onPress={() => {}}
378
+ />
379
+ </RowList>
380
+ </NegativeBox>
381
+ </Stack>
382
+ </Box>
383
+ </ResponsiveLayout>
384
+ ```
385
+
386
+ ### Boxed list (no NegativeBox needed)
387
+
388
+ ```tsx
389
+ <BoxedRowList>
390
+ <BoxedRow title="Option A" onPress={() => {}} />
391
+ <BoxedRow title="Option B" onPress={() => {}} />
392
+ </BoxedRowList>
393
+ ```
394
+
395
+ ## Variant sections
396
+
397
+ Use `variant` on `ResponsiveLayout` to create colored sections. Components inside adapt automatically:
398
+
399
+ ```tsx
400
+ {
401
+ /* Default section */
402
+ }
403
+ <ResponsiveLayout>
404
+ <Box paddingY={24}>
405
+ <Text2 regular>Default background</Text2>
406
+ </Box>
407
+ </ResponsiveLayout>;
408
+
409
+ {
410
+ /* Brand section */
411
+ }
412
+ <ResponsiveLayout variant="brand" fullWidth>
413
+ <ResponsiveLayout>
414
+ <Box paddingY={24}>
415
+ <Stack space={16}>
416
+ <Title1>Brand Section</Title1>
417
+ <Text2 regular>Colors adapt automatically</Text2>
418
+ <ButtonPrimary onPress={() => {}}>Action</ButtonPrimary>
419
+ </Stack>
420
+ </Box>
421
+ </ResponsiveLayout>
422
+ </ResponsiveLayout>;
423
+
424
+ {
425
+ /* Alternative section */
426
+ }
427
+ <ResponsiveLayout variant="alternative" fullWidth>
428
+ <ResponsiveLayout>
429
+ <Box paddingY={24}>
430
+ <Text2 regular>Alternative background</Text2>
431
+ </Box>
432
+ </ResponsiveLayout>
433
+ </ResponsiveLayout>;
434
+ ```
435
+
436
+ ## Boxed containers
437
+
438
+ Use `Boxed` for card-like containers without card semantics:
439
+
440
+ ```tsx
441
+ <Boxed>
442
+ <Box padding={16}>
443
+ <Stack space={16}>
444
+ <Title3>Container Title</Title3>
445
+ <Text2 regular>Container content</Text2>
446
+ </Stack>
447
+ </Box>
448
+ </Boxed>
449
+ ```
450
+
451
+ ## Skeleton loading patterns
452
+
453
+ Replace content with matching skeleton shapes while loading:
454
+
455
+ ```tsx
456
+ const {data, isLoading} = useFetch('/api/data');
457
+
458
+ if (isLoading) {
459
+ return (
460
+ <Stack space={16}>
461
+ <SkeletonLine width="40%" />
462
+ <SkeletonText />
463
+ <Inline space={16}>
464
+ <SkeletonCircle size={40} />
465
+ <SkeletonLine width="60%" />
466
+ </Inline>
467
+ </Stack>
468
+ );
469
+ }
470
+
471
+ return (
472
+ <Stack space={16}>
473
+ <Title1>{data.title}</Title1>
474
+ <Text2 regular>{data.description}</Text2>
475
+ <Inline space={16}>
476
+ <Avatar size={40} src={data.avatar} />
477
+ <Text2 regular>{data.author}</Text2>
478
+ </Inline>
479
+ </Stack>
480
+ );
481
+ ```
482
+
483
+ ## Funnel / step-by-step flow
484
+
485
+ ```tsx
486
+ <FunnelNavigationBar
487
+ right={
488
+ <NavigationBarActionGroup>
489
+ <NavigationBarAction aria-label="Close" onPress={handleClose}>
490
+ <IconCloseRegular color="currentColor" />
491
+ </NavigationBarAction>
492
+ </NavigationBarActionGroup>
493
+ }
494
+ />
495
+ <Stepper currentIndex={currentStep} steps={['Personal', 'Address', 'Payment', 'Confirm']} />
496
+ <ResponsiveLayout>
497
+ <Box paddingY={24}>
498
+ {currentStep === 0 && <PersonalInfoForm />}
499
+ {currentStep === 1 && <AddressForm />}
500
+ {currentStep === 2 && <PaymentForm />}
501
+ {currentStep === 3 && <ConfirmationScreen />}
502
+ </Box>
503
+ </ResponsiveLayout>
504
+ ```
505
+
506
+ ## Next.js integration
507
+
508
+ ### Link configuration
509
+
510
+ ```tsx
511
+ import Link from 'next/link';
512
+
513
+ const theme = {
514
+ skin: getMovistarNewSkin(),
515
+ i18n: {locale: 'es-ES', phoneNumberFormattingRegionCode: 'ES'},
516
+ Link: {type: 'Next14', Component: Link},
517
+ };
518
+ ```
519
+
520
+ ### React Router integration
521
+
522
+ ```tsx
523
+ import {Link} from 'react-router-dom';
524
+
525
+ const theme = {
526
+ skin: getMovistarNewSkin(),
527
+ i18n: {locale: 'es-ES', phoneNumberFormattingRegionCode: 'ES'},
528
+ Link: {type: 'ReactRouter6', Component: Link},
529
+ };
530
+ ```
531
+
532
+ After configuring, use the `to` prop on touchable components for client-side navigation:
533
+
534
+ ```tsx
535
+ <ButtonPrimary to="/dashboard">Go to dashboard</ButtonPrimary>
536
+ <Row title="Profile" to="/profile" />
537
+ <TextLink to="/settings">Settings</TextLink>
538
+ ```
539
+
540
+ ## Dark mode
541
+
542
+ Mistica supports dark mode out of the box via `colorScheme` in theme config:
543
+
544
+ - `'auto'` (default) -- follows OS/browser preference
545
+ - `'light'` -- force light mode
546
+ - `'dark'` -- force dark mode
547
+
548
+ All `skinVars.colors.*` tokens automatically resolve to their dark mode values. No additional code is needed.
package/doc/sheet.md ADDED
@@ -0,0 +1,122 @@
1
+ # Sheet
2
+
3
+ Mística provides a sheet component that can be used to display a modal-like content from over the main content
4
+ of the screen.
5
+
6
+ ## Predefined sheets
7
+
8
+ Some predefined sheets are available: `RadioListSheet`, `ActionsListSheet`, `InfoSheet` and `ActionsSheet`.
9
+ You can see examples in Storybook.
10
+
11
+ To use them, first you must configure the `SheetRoot` component in your app:
12
+
13
+ ```jsx
14
+ import {SheetRoot} from '@telefonica/mistica';
15
+
16
+ export const App = () => {
17
+ return (
18
+ <SheetRoot>
19
+ <MyApplication />
20
+ </SheetRoot>
21
+ );
22
+ };
23
+ ```
24
+
25
+ Then you can call `showSheet` from anywhere:
26
+
27
+ ```jsx
28
+ import {showSheet} from '@telefonica/mistica';
29
+
30
+ const MyComponent = () => {
31
+ return (
32
+ <ButtonPrimary
33
+ onPress={() =>
34
+ showSheet({
35
+ type: 'RADIO_LIST',
36
+ props: {
37
+ title: 'Select an fruit',
38
+ items: [
39
+ {id: '1', title: 'Apple'},
40
+ {id: '2', title: 'Banana'},
41
+ {id: '3', title: 'Orange'},
42
+ ],
43
+ },
44
+ }).then((result) => {
45
+ // The promise is resolved when the sheet is closed
46
+ console.log(result);
47
+ })
48
+ }
49
+ >
50
+ show sheet
51
+ </ButtonPrimary>
52
+ );
53
+ };
54
+ ```
55
+
56
+ ### Native implementation
57
+
58
+ If your app is served inside a webview and uses the `webview-bridge` library, the native implementation of the
59
+ predefined sheets will be used.
60
+
61
+ ```tsx
62
+ import {bottomSheet, isWebViewBridgeAvailable} from '@tef-novum/webview-bridge';
63
+
64
+ // ...
65
+
66
+ <SheetRoot nativeImplementation={isWebViewBridgeAvailable() ? bottomSheet : undefined}>
67
+ ```
68
+
69
+ When possible, always use the native implementation, as it provides a better user experience.
70
+
71
+ ## Custom sheets
72
+
73
+ You can show any content you want inside the sheet by passing it as a child of the component.
74
+
75
+ ```jsx
76
+ import {Sheet} from 'mistica';
77
+
78
+ const MyComponent = () => {
79
+ const [showSheet, setShowSheet] = useState(false);
80
+ return (
81
+ <>
82
+ <ButtonPrimary onPress={() => setShowSheet(true)}>show sheet</ButtonPrimary>
83
+ {showSheet && (
84
+ <Sheet onClose={() => setShowSheet(false)}>
85
+ <Placeholder />
86
+ </Sheet>
87
+ )}
88
+ </>
89
+ );
90
+ };
91
+ ```
92
+
93
+ The sheet will close when the user does the swipe down gesture, when the background overlay is touched, or by
94
+ using the visible dismiss button (close icon) that appears in the top-right corner aligned with the title. The
95
+ `onClose` callback is called when the closing animation finishes, that's the right place to unmount the sheet
96
+ as shown in the example above.
97
+
98
+ You can also close the sheet programmatically using the render prop:
99
+
100
+ ```jsx
101
+ import {Sheet} from 'mistica';
102
+
103
+ const MyComponent = () => {
104
+ const [showSheet, setShowSheet] = useState(false);
105
+ return (
106
+ <>
107
+ <ButtonPrimary onPress={() => setShowSheet(true)}>show sheet</ButtonPrimary>
108
+ {showSheet && (
109
+ <Sheet onClose={() => setShowSheet(false)}>
110
+ {({closeModal, modalTitleId}) => (
111
+ <>
112
+ <Title1 id={modalTitleId}>My sheet</Title1>
113
+ <Placeholder />
114
+ <ButtonPrimary onPress={closeModal}>Close</ButtonPrimary>
115
+ </>
116
+ )}
117
+ </Sheet>
118
+ )}
119
+ </>
120
+ );
121
+ };
122
+ ```
package/doc/testing.md ADDED
@@ -0,0 +1,43 @@
1
+ # Testing notes
2
+
3
+ ## Environment variables
4
+
5
+ Our code uses the NODE_ENV var to define code blocks that should be executed in specific environments, like
6
+ `'test'` or `'production'`.
7
+
8
+ For example:
9
+
10
+ ```js
11
+ if (process.env.NODE_ENV !== 'test') {
12
+ // this will only be executed if not inside a test
13
+ }
14
+ ```
15
+
16
+ Make sure your bundler (webpack, rollup, parcel, etc) makes the correct substitutions and the minifier
17
+ effectively removes unused code.
18
+
19
+ ## Unit tests
20
+
21
+ Unit tests usually don't require CSS because they test component functionality. Some Mistica components have
22
+ different layouts in mobile and desktop, and there are cases in which we use CSS styles to achieve this
23
+ result. If you are trying to test something that depends on these styles, you can either import `mistica.css`
24
+ classes in the HTML content you render in the test or write acceptance/screenshot tests instead.
25
+
26
+ ## Acceptance tests
27
+
28
+ To change some behaviors to facilitate acceptance tests (tests that run in a browser), a helper function is
29
+ used.
30
+
31
+ `isRunningAcceptanceTest` returns true if the user agent includes the `'acceptance-test'` string.
32
+
33
+ To make this work in your test environment, you should add that string to the browser's user agent. For
34
+ example in `Puppeteer`:
35
+
36
+ ```js
37
+ await page.setUserAgent(`${currentUserAgent} acceptance-test`);
38
+ ```
39
+
40
+ ## Questions?
41
+
42
+ Don't hesistate to ask at
43
+ [Mistica Teams Channel](https://teams.microsoft.com/l/channel/19%3ad2e3607a32ec411b8bf492f43cd0fe0c%40thread.tacv2/General?groupId=e265fe99-929f-45d1-8154-699649674a40&tenantId=9744600e-3e04-492e-baa1-25ec245c6f10)