@latte-macchiat-io/latte-vanilla-components 0.0.180 → 0.0.181
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.
- package/package.json +1 -1
- package/src/index.ts +7 -1
- package/src/themes/dark.css.ts +19 -0
- package/src/themes/light.css.ts +19 -0
- package/src/utils/createCustomTheme.ts +52 -0
- package/src/utils/themeOverrides.css.ts +10 -0
- package/src/utils/deep-merge-objects.ts +0 -15
- package/src/utils/use-breakpoint-key.ts +0 -30
- package/src/utils/use-window-size.ts +0 -37
package/package.json
CHANGED
package/src/index.ts
CHANGED
@@ -86,11 +86,17 @@ export { type TextareaVariants } from './components/Form/TextField/Textarea/Text
|
|
86
86
|
|
87
87
|
export { ToRemove } from './components/ToRemove/ToRemove';
|
88
88
|
|
89
|
-
// Theme utilities
|
89
|
+
// Theme utilities
|
90
90
|
export { createDarkTheme, createLightTheme, type ThemeOverrides } from './utils/theme';
|
91
|
+
export { createCustomLightTheme, createCustomDarkTheme, type ThemeOverrides as CustomThemeOverrides } from './utils/createCustomTheme';
|
92
|
+
export { createThemeOverride } from './utils/themeOverrides.css';
|
91
93
|
// Theme contract and values
|
92
94
|
export { themeContract } from './theme/contract.css';
|
93
95
|
export { baseLightTheme, baseDarkTheme } from './theme/baseThemeValues';
|
94
96
|
|
97
|
+
// Pre-built themes (these actually generate CSS variables)
|
98
|
+
export { lightTheme } from './themes/light.css';
|
99
|
+
export { darkTheme } from './themes/dark.css';
|
100
|
+
|
95
101
|
// Theme utilities
|
96
102
|
export { createThemeOverride, getThemeContract, getThemeValues, toggleTheme, setTheme, getCurrentTheme, type ThemeValues } from './theme/utils';
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { createGlobalTheme } from '@vanilla-extract/css';
|
2
|
+
import { themeContract } from '../theme/contract.css';
|
3
|
+
import { baseDarkTheme } from '../theme/baseThemeValues';
|
4
|
+
|
5
|
+
// Create the dark theme at module level so Vanilla Extract can process it
|
6
|
+
export const darkTheme = createGlobalTheme('html[data-theme="dark"]', themeContract, {
|
7
|
+
colors: baseDarkTheme.colors,
|
8
|
+
space: baseDarkTheme.space,
|
9
|
+
radii: baseDarkTheme.radii,
|
10
|
+
fonts: baseDarkTheme.fonts,
|
11
|
+
maxWidth: `${baseDarkTheme.maxWidth}px`,
|
12
|
+
fontSizes: baseDarkTheme.fontSizes,
|
13
|
+
fontWeights: baseDarkTheme.fontWeights,
|
14
|
+
lineHeights: baseDarkTheme.lineHeights,
|
15
|
+
shadows: baseDarkTheme.shadows,
|
16
|
+
section: baseDarkTheme.section,
|
17
|
+
header: baseDarkTheme.header,
|
18
|
+
footer: baseDarkTheme.footer,
|
19
|
+
});
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { createGlobalTheme } from '@vanilla-extract/css';
|
2
|
+
import { themeContract } from '../theme/contract.css';
|
3
|
+
import { baseLightTheme } from '../theme/baseThemeValues';
|
4
|
+
|
5
|
+
// Create the light theme at module level so Vanilla Extract can process it
|
6
|
+
export const lightTheme = createGlobalTheme(':root', themeContract, {
|
7
|
+
colors: baseLightTheme.colors,
|
8
|
+
space: baseLightTheme.space,
|
9
|
+
radii: baseLightTheme.radii,
|
10
|
+
fonts: baseLightTheme.fonts,
|
11
|
+
maxWidth: `${baseLightTheme.maxWidth}px`,
|
12
|
+
fontSizes: baseLightTheme.fontSizes,
|
13
|
+
fontWeights: baseLightTheme.fontWeights,
|
14
|
+
lineHeights: baseLightTheme.lineHeights,
|
15
|
+
shadows: baseLightTheme.shadows,
|
16
|
+
section: baseLightTheme.section,
|
17
|
+
header: baseLightTheme.header,
|
18
|
+
footer: baseLightTheme.footer,
|
19
|
+
});
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import { baseLightTheme, baseDarkTheme } from '../theme/baseThemeValues';
|
2
|
+
|
3
|
+
// Type for partial theme overrides
|
4
|
+
export type ThemeOverrides = {
|
5
|
+
colors?: Partial<typeof baseLightTheme.colors>;
|
6
|
+
space?: Partial<typeof baseLightTheme.space>;
|
7
|
+
radii?: Partial<typeof baseLightTheme.radii>;
|
8
|
+
fonts?: Partial<typeof baseLightTheme.fonts>;
|
9
|
+
maxWidth?: string;
|
10
|
+
fontSizes?: Partial<typeof baseLightTheme.fontSizes>;
|
11
|
+
fontWeights?: Partial<typeof baseLightTheme.fontWeights>;
|
12
|
+
lineHeights?: Partial<typeof baseLightTheme.lineHeights>;
|
13
|
+
shadows?: Partial<typeof baseLightTheme.shadows>;
|
14
|
+
section?: Partial<typeof baseLightTheme.section>;
|
15
|
+
header?: Partial<typeof baseLightTheme.header>;
|
16
|
+
footer?: Partial<typeof baseLightTheme.footer>;
|
17
|
+
};
|
18
|
+
|
19
|
+
// Utility to merge overrides with base theme
|
20
|
+
export const createCustomLightTheme = (overrides: ThemeOverrides = {}) => {
|
21
|
+
return {
|
22
|
+
colors: { ...baseLightTheme.colors, ...overrides.colors },
|
23
|
+
space: { ...baseLightTheme.space, ...overrides.space },
|
24
|
+
radii: { ...baseLightTheme.radii, ...overrides.radii },
|
25
|
+
fonts: { ...baseLightTheme.fonts, ...overrides.fonts },
|
26
|
+
maxWidth: overrides.maxWidth || `${baseLightTheme.maxWidth}px`,
|
27
|
+
fontSizes: { ...baseLightTheme.fontSizes, ...overrides.fontSizes },
|
28
|
+
fontWeights: { ...baseLightTheme.fontWeights, ...overrides.fontWeights },
|
29
|
+
lineHeights: { ...baseLightTheme.lineHeights, ...overrides.lineHeights },
|
30
|
+
shadows: { ...baseLightTheme.shadows, ...overrides.shadows },
|
31
|
+
section: { ...baseLightTheme.section, ...overrides.section },
|
32
|
+
header: { ...baseLightTheme.header, ...overrides.header },
|
33
|
+
footer: { ...baseLightTheme.footer, ...overrides.footer },
|
34
|
+
};
|
35
|
+
};
|
36
|
+
|
37
|
+
export const createCustomDarkTheme = (overrides: ThemeOverrides = {}) => {
|
38
|
+
return {
|
39
|
+
colors: { ...baseDarkTheme.colors, ...overrides.colors },
|
40
|
+
space: { ...baseDarkTheme.space, ...overrides.space },
|
41
|
+
radii: { ...baseDarkTheme.radii, ...overrides.radii },
|
42
|
+
fonts: { ...baseDarkTheme.fonts, ...overrides.fonts },
|
43
|
+
maxWidth: overrides.maxWidth || `${baseDarkTheme.maxWidth}px`,
|
44
|
+
fontSizes: { ...baseDarkTheme.fontSizes, ...overrides.fontSizes },
|
45
|
+
fontWeights: { ...baseDarkTheme.fontWeights, ...overrides.fontWeights },
|
46
|
+
lineHeights: { ...baseDarkTheme.lineHeights, ...overrides.lineHeights },
|
47
|
+
shadows: { ...baseDarkTheme.shadows, ...overrides.shadows },
|
48
|
+
section: { ...baseDarkTheme.section, ...overrides.section },
|
49
|
+
header: { ...baseDarkTheme.header, ...overrides.header },
|
50
|
+
footer: { ...baseDarkTheme.footer, ...overrides.footer },
|
51
|
+
};
|
52
|
+
};
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { createGlobalTheme, createThemeContract } from '@vanilla-extract/css';
|
2
|
+
import { themeContract } from '../theme/contract.css';
|
3
|
+
|
4
|
+
// Create a higher-specificity theme that can override the base themes
|
5
|
+
export const themeOverrides = createThemeContract(themeContract);
|
6
|
+
|
7
|
+
// Export a utility to create override themes with custom selectors
|
8
|
+
export const createThemeOverride = (selector: string, values: Partial<typeof themeContract>) => {
|
9
|
+
return createGlobalTheme(selector, themeContract, values as any);
|
10
|
+
};
|
@@ -1,15 +0,0 @@
|
|
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;
|
@@ -1,30 +0,0 @@
|
|
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
|
-
}
|
@@ -1,37 +0,0 @@
|
|
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;
|