@latte-macchiat-io/latte-vanilla-components 0.0.174 → 0.0.176

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 (41) hide show
  1. package/dist/css/index.cjs +1 -0
  2. package/dist/css/index.js +34 -0
  3. package/dist/index.cjs.js +2 -2
  4. package/dist/index.es.js +3195 -6144
  5. package/dist/styles/sprinkles.css.ts +2 -0
  6. package/dist/theme/contract.css.ts +5 -5
  7. package/dist/theme.css-CNjMk-g_.cjs +1 -0
  8. package/dist/theme.css-Dj6kL9o0.js +3020 -0
  9. package/dist/types/css/index.d.ts +28 -0
  10. package/dist/types/index.d.ts +1 -1
  11. package/dist/types/styles/mediaqueries.d.ts +16 -0
  12. package/dist/types/styles/sprinkles.css.d.ts +3423 -0
  13. package/dist/types/theme/baseThemeValues.d.ts +158 -0
  14. package/dist/types/theme/contract.css.d.ts +79 -0
  15. package/dist/types/theme/index.d.ts +3 -0
  16. package/dist/types/theme/utils.d.ts +86 -0
  17. package/package.json +22 -8
  18. package/src/assets/styles/default-theme.ts +312 -0
  19. package/src/components/Button/stories.ts +127 -0
  20. package/src/components/Columns/stories.ts +35 -0
  21. package/src/components/Icon/path.ts +36 -0
  22. package/src/components/Icon/stories.ts +29 -0
  23. package/src/components/Main/stories.ts +33 -0
  24. package/src/components/Section/stories.ts +64 -0
  25. package/src/css/index.ts +33 -0
  26. package/src/index.ts +96 -0
  27. package/src/styles/mediaqueries.ts +17 -0
  28. package/src/styles/sprinkles.css.ts +2 -0
  29. package/src/theme/baseThemeValues.ts +160 -0
  30. package/src/theme/contract.css.ts +5 -5
  31. package/src/theme/index.ts +6 -0
  32. package/src/theme/utils.ts +76 -0
  33. package/src/themes/dark.ts +3 -0
  34. package/src/themes/index.ts +5 -0
  35. package/src/themes/light.ts +3 -0
  36. package/src/types/theme.ts +295 -0
  37. package/src/types/withClassName.ts +3 -0
  38. package/src/utils/cookie.ts +24 -0
  39. package/src/utils/deep-merge-objects.ts +15 -0
  40. package/src/utils/use-breakpoint-key.ts +30 -0
  41. package/src/utils/use-window-size.ts +37 -0
@@ -1,8 +1,8 @@
1
- import { createThemeContract } from '@vanilla-extract/css';
1
+ import { createGlobalThemeContract } from '@vanilla-extract/css';
2
2
 
3
- // Define a theme contract with design tokens
4
- // This contract can then be reused to create multiple themes or overrides on a global level
5
- export const themeContract = createThemeContract({
3
+ // Define a theme contract with design tokens using createGlobalThemeContract
4
+ // This enables predictable CSS variable names for external consumption and runtime overrides
5
+ export const themeContract = createGlobalThemeContract({
6
6
  colors: {
7
7
  primary: null,
8
8
  secondary: null,
@@ -80,4 +80,4 @@ export const themeContract = createThemeContract({
80
80
  footer: {
81
81
  height: null,
82
82
  },
83
- });
83
+ }, (value) => `latte-${value}`);
@@ -0,0 +1,6 @@
1
+ // Theme contract and values
2
+ export { themeContract } from './contract.css';
3
+ export { baseLightTheme, baseDarkTheme } from './baseThemeValues';
4
+
5
+ // Theme utilities
6
+ export { createThemeOverride, getThemeContract, getThemeValues, toggleTheme, setTheme, getCurrentTheme, type ThemeValues } from './utils';
@@ -0,0 +1,76 @@
1
+ import { baseDarkTheme, baseLightTheme } from './baseThemeValues';
2
+ import { themeContract } from './contract.css';
3
+
4
+ export type ThemeValues = typeof baseLightTheme;
5
+
6
+ export const createThemeOverride = (overrides: Partial<ThemeValues>): ThemeValues => {
7
+ return {
8
+ ...baseLightTheme,
9
+ ...overrides,
10
+ colors: {
11
+ ...baseLightTheme.colors,
12
+ ...overrides.colors,
13
+ },
14
+ space: {
15
+ ...baseLightTheme.space,
16
+ ...overrides.space,
17
+ },
18
+ radii: {
19
+ ...baseLightTheme.radii,
20
+ ...overrides.radii,
21
+ },
22
+ fonts: {
23
+ ...baseLightTheme.fonts,
24
+ ...overrides.fonts,
25
+ },
26
+ fontSizes: {
27
+ ...baseLightTheme.fontSizes,
28
+ ...overrides.fontSizes,
29
+ },
30
+ lineHeights: {
31
+ ...baseLightTheme.lineHeights,
32
+ ...overrides.lineHeights,
33
+ },
34
+ shadows: {
35
+ ...baseLightTheme.shadows,
36
+ ...overrides.shadows,
37
+ },
38
+ };
39
+ };
40
+
41
+ export const getThemeContract = () => themeContract;
42
+
43
+ export const getThemeValues = (isDark = false): ThemeValues => {
44
+ return isDark ? baseDarkTheme : baseLightTheme;
45
+ };
46
+
47
+ export const toggleTheme = () => {
48
+ const html = document.documentElement;
49
+ const currentTheme = html.getAttribute('data-theme');
50
+ const newTheme = currentTheme === 'dark' ? null : 'dark';
51
+
52
+ if (newTheme) {
53
+ html.setAttribute('data-theme', newTheme);
54
+ } else {
55
+ html.removeAttribute('data-theme');
56
+ }
57
+
58
+ return newTheme === 'dark';
59
+ };
60
+
61
+ export const setTheme = (theme: 'light' | 'dark') => {
62
+ const html = document.documentElement;
63
+
64
+ if (theme === 'dark') {
65
+ html.setAttribute('data-theme', 'dark');
66
+ } else {
67
+ html.removeAttribute('data-theme');
68
+ }
69
+ };
70
+
71
+ export const getCurrentTheme = (): 'light' | 'dark' => {
72
+ if (typeof window === 'undefined') return 'light';
73
+
74
+ const html = document.documentElement;
75
+ return html.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
76
+ };
@@ -0,0 +1,3 @@
1
+ // Pre-built dark theme export
2
+ export { baseDarkTheme as darkTheme } from '../theme/baseThemeValues';
3
+ export { createDarkTheme } from '../utils/theme.css';
@@ -0,0 +1,5 @@
1
+ // Theme exports for consuming applications
2
+ export * from './light';
3
+ export * from './dark';
4
+ export { themeContract } from '../theme/contract.css';
5
+ export { createLightTheme, createDarkTheme, type ThemeOverrides } from '../utils/theme.css';
@@ -0,0 +1,3 @@
1
+ // Pre-built light theme export
2
+ export { baseLightTheme as lightTheme } from '../theme/baseThemeValues';
3
+ export { createLightTheme } from '../utils/theme.css';
@@ -0,0 +1,295 @@
1
+ export interface Theme {
2
+ global: {
3
+ maxWidth: number;
4
+ paddingLeft: number[];
5
+ paddingRight: number[];
6
+
7
+ brandFont: string;
8
+ defaultFont: string;
9
+ };
10
+
11
+ colors: {
12
+ defaultText: string;
13
+ defaultLightText: string;
14
+ defaultTitle: string;
15
+ defaultLink: string;
16
+ defaultIcon: string;
17
+ };
18
+
19
+ backgroundColors: {
20
+ modal: string;
21
+ modalOverlay: string;
22
+ cookieConsent: string;
23
+ };
24
+
25
+ borders: {
26
+ hr: string;
27
+ cookieConsent: string;
28
+ };
29
+
30
+ borderRadius: {
31
+ modal: number;
32
+ input: number;
33
+ cookieConsent: number;
34
+ };
35
+
36
+ widthSizes: {
37
+ modal: number;
38
+ cookieConsent: number;
39
+ };
40
+
41
+ transitions: {
42
+ global: string;
43
+ };
44
+
45
+ actions: {
46
+ gap: number[];
47
+ paddingTop: number[];
48
+ paddingBottom: number[];
49
+ };
50
+
51
+ button: {
52
+ minWidth: number;
53
+ fontWeight: number;
54
+ letterSpacing: number;
55
+ color: {
56
+ primary: string;
57
+ secondary: string;
58
+ disabled: string;
59
+ };
60
+ backgroundColor: {
61
+ primary: string;
62
+ secondary: string;
63
+ disabled: string;
64
+ };
65
+ border: {
66
+ width: number;
67
+ style: string;
68
+ color: {
69
+ primary: string;
70
+ secondary: string;
71
+ disabled: string;
72
+ };
73
+ };
74
+ borderRadius: number;
75
+ paddingTop: {
76
+ small: number[];
77
+ medium: number[];
78
+ large: number[];
79
+ };
80
+ paddingBottom: {
81
+ small: number[];
82
+ medium: number[];
83
+ large: number[];
84
+ };
85
+ paddingRight: {
86
+ small: number[];
87
+ medium: number[];
88
+ large: number[];
89
+ };
90
+ paddingLeft: {
91
+ small: number[];
92
+ medium: number[];
93
+ large: number[];
94
+ };
95
+ transition: string;
96
+ };
97
+
98
+ keyNumber: {
99
+ paddingBottom: number[];
100
+ };
101
+
102
+ columns: {
103
+ gap: number;
104
+ };
105
+
106
+ form: {
107
+ width: number;
108
+ backgroundColor: string;
109
+ padding: [string | number, string | number];
110
+
111
+ row: {
112
+ gap: [string | number, string | number];
113
+ padding: [string | number, string | number];
114
+ };
115
+
116
+ label: {
117
+ font: string;
118
+ color: string;
119
+ fontWeight: number;
120
+ paddingBottom: number;
121
+ };
122
+
123
+ input: {
124
+ font: string;
125
+ color: string;
126
+ border: string;
127
+ padding: number[];
128
+ borderRadius: number;
129
+ backgroundColor: string;
130
+ placeholder: {
131
+ color: string;
132
+ opacity: number;
133
+ };
134
+ };
135
+
136
+ textarea: {
137
+ font: string;
138
+ color: string;
139
+ border: string;
140
+ padding: number[];
141
+ borderRadius: number;
142
+ backgroundColor: string;
143
+ placeholder: {
144
+ color: string;
145
+ opacity: number;
146
+ };
147
+ };
148
+
149
+ error: {
150
+ color: string;
151
+ };
152
+ };
153
+
154
+ header: {
155
+ gap: number[];
156
+ height: number[];
157
+ backgroundColor: string;
158
+
159
+ paddingTop: number[];
160
+ paddingLeft: number[];
161
+ paddingRight: number[];
162
+ paddingBottom: number[];
163
+
164
+ overlayGap: number[];
165
+ overlayBackgroundColor: string;
166
+
167
+ toggleNavColor: string;
168
+ toggleNavOpenColor: string;
169
+ toggleNavBorderRadius: number;
170
+ };
171
+
172
+ main: {
173
+ minHeight: string;
174
+ backgroundColor: string;
175
+ };
176
+
177
+ section: {
178
+ paddingTop: number[];
179
+ paddingBottom: number[];
180
+ };
181
+
182
+ footer: {
183
+ gap: number[];
184
+ height: number[];
185
+ backgroundColor: string;
186
+
187
+ paddingTop: number[];
188
+ paddingBottom: number[];
189
+ };
190
+
191
+ modal: {
192
+ border: string;
193
+ width: number[];
194
+ borderRadius: number;
195
+ backgroundColor: string;
196
+
197
+ overlayBlur: number;
198
+ overlayOpacity: number;
199
+ overlayBackgroundColor: string;
200
+
201
+ paddingTop: number[];
202
+ paddingLeft: number[];
203
+ paddingRight: number[];
204
+ paddingBottom: number[];
205
+ };
206
+
207
+ languageSwitcher: {
208
+ color: string;
209
+ border: string;
210
+ width: number[];
211
+ borderRadius: number;
212
+ backgroundColor: string;
213
+
214
+ paddingTop: number[];
215
+ paddingLeft: number[];
216
+ paddingRight: number[];
217
+ paddingBottom: number[];
218
+ };
219
+
220
+ carousel: {
221
+ gap: number[];
222
+ bullet: {
223
+ width: number[];
224
+ height: number[];
225
+ borderRadius: number;
226
+ backgroundColor: string;
227
+ };
228
+ };
229
+
230
+ video: {
231
+ playButton: {
232
+ color: string;
233
+ border: string;
234
+ width: number[];
235
+ height: number[];
236
+ borderRadius: number;
237
+ backgroundColor: string;
238
+ };
239
+ pauseButton: {
240
+ color: string;
241
+ border: string;
242
+ width: number[];
243
+ height: number[];
244
+ borderRadius: number;
245
+ backgroundColor: string;
246
+ };
247
+ soundButton: {
248
+ color: string;
249
+ border: string;
250
+ width: number[];
251
+ height: number[];
252
+ borderRadius: number;
253
+ backgroundColor: string;
254
+ };
255
+ closeButton: {
256
+ color: string;
257
+ border: string;
258
+ width: number[];
259
+ height: number[];
260
+ borderRadius: number;
261
+ backgroundColor: string;
262
+ };
263
+ };
264
+
265
+ consentCookie: {
266
+ borders: number;
267
+ width: number[];
268
+ borderRadius: number;
269
+ backgroundColor: string;
270
+
271
+ overlayBlur: number;
272
+ overlayOpacity: number;
273
+ overlayBackgroundColor: string;
274
+
275
+ paddingTop: number[];
276
+ paddingLeft: number[];
277
+ paddingRight: number[];
278
+ paddingBottom: number[];
279
+
280
+ actionsGap: number;
281
+ actionsPaddingTop: number[];
282
+ };
283
+
284
+ nav: {
285
+ gap: number[];
286
+ };
287
+
288
+ navSocial: {
289
+ gap: number[];
290
+ };
291
+
292
+ navLegal: {
293
+ gap: number[];
294
+ };
295
+ }
@@ -0,0 +1,3 @@
1
+ export type WithClassName = {
2
+ className: string;
3
+ };
@@ -0,0 +1,24 @@
1
+ // https://www.w3schools.com/js/js_cookies.asp
2
+
3
+ export function getCookie(name: string) {
4
+ const fullName = name + '=';
5
+ const decodedCookie = decodeURIComponent(document.cookie);
6
+ const ca = decodedCookie.split(';');
7
+ for (let i = 0; i < ca.length; i++) {
8
+ let c = ca[i];
9
+ while (c.charAt(0) == ' ') {
10
+ c = c.substring(1);
11
+ }
12
+ if (c.indexOf(fullName) == 0) {
13
+ return c.substring(fullName.length, c.length);
14
+ }
15
+ }
16
+ return '';
17
+ }
18
+
19
+ export function setCookie(name: string, value: string, expirationInDays: number) {
20
+ const d = new Date();
21
+ d.setTime(d.getTime() + expirationInDays * 24 * 60 * 60 * 1000);
22
+ const expires = 'expires=' + d.toUTCString();
23
+ document.cookie = name + '=' + value + ';' + expires + ';path=/';
24
+ }
@@ -0,0 +1,15 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ const deepMergeObjects = (obj1: any, obj2: any) => {
3
+ for (const key in obj2) {
4
+ if (obj2.hasOwnProperty(key)) {
5
+ if (obj2[key] instanceof Object && obj1[key] instanceof Object) {
6
+ obj1[key] = deepMergeObjects(obj1[key], obj2[key]);
7
+ } else {
8
+ obj1[key] = obj2[key];
9
+ }
10
+ }
11
+ }
12
+ return obj1;
13
+ };
14
+
15
+ export default deepMergeObjects;
@@ -0,0 +1,30 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import { breakpoints } from '../assets/styles/mediaqueries';
4
+
5
+ type BreakpointKey = keyof typeof breakpoints;
6
+
7
+ export function useBreakpointKey(): number {
8
+ const [breakpoint, setBreakpoint] = useState<BreakpointKey>('mobile');
9
+
10
+ useEffect(() => {
11
+ function handleResize() {
12
+ const width = window.innerWidth;
13
+
14
+ if (width >= breakpoints['2xl']) setBreakpoint('2xl');
15
+ else if (width >= breakpoints.xl) setBreakpoint('xl');
16
+ else if (width >= breakpoints.lg) setBreakpoint('lg');
17
+ else if (width >= breakpoints.md) setBreakpoint('md');
18
+ else if (width >= breakpoints.sm) setBreakpoint('sm');
19
+ else setBreakpoint('mobile');
20
+ }
21
+
22
+ handleResize(); // init au mount
23
+ window.addEventListener('resize', handleResize);
24
+ return () => window.removeEventListener('resize', handleResize);
25
+ }, []);
26
+
27
+ // Map Breakpoint → Index numérique
28
+ const order: BreakpointKey[] = ['mobile', 'sm', 'md', 'lg', 'xl', '2xl'];
29
+ return order.indexOf(breakpoint);
30
+ }
@@ -0,0 +1,37 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ function useWindowSize() {
4
+ // Initialize state with undefined width/height so server and client renders match
5
+ // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
6
+ const [windowSize, setWindowSize] = useState<{
7
+ width: undefined | number;
8
+ height: undefined | number;
9
+ }>({
10
+ width: undefined,
11
+ height: undefined,
12
+ });
13
+
14
+ useEffect(() => {
15
+ // only execute all the code below in client side
16
+ // Handler to call on window resize
17
+ function handleResize() {
18
+ // Set window width/height to state
19
+ setWindowSize({
20
+ width: window.innerWidth,
21
+ height: window.innerHeight,
22
+ });
23
+ }
24
+
25
+ // Add event listener
26
+ window.addEventListener('resize', handleResize);
27
+
28
+ // Call handler right away so state gets updated with initial window size
29
+ handleResize();
30
+
31
+ // Remove event listener on cleanup
32
+ return () => window.removeEventListener('resize', handleResize);
33
+ }, []); // Empty array ensures that effect is only run on mount
34
+ return windowSize;
35
+ }
36
+
37
+ export default useWindowSize;