@xuefx/ui-theme 1.0.1 → 1.0.4

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/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # @xuefx/ui-theme
2
+
3
+ Design tokens, Tailwind preset, and theming tools for [@xuefx/ui](https://www.npmjs.com/package/@xuefx/ui).
4
+
5
+ ## Usage
6
+
7
+ ```js
8
+ // 1. Import design tokens (CSS custom properties)
9
+ import '@xuefx/ui-theme/styles.css';
10
+
11
+ // 2. Build your own theme
12
+ import { ThemeProvider, createTheme, themes } from '@xuefx/ui-theme';
13
+
14
+ const myTheme = createTheme({
15
+ colors: { primary: '142 76% 36%', 'primary-foreground': '0 0% 100%' },
16
+ });
17
+
18
+ // 3. Or use a preset
19
+ <ThemeProvider theme={themes.green}>
20
+ <App />
21
+ </ThemeProvider>
22
+ ```
23
+
24
+ ## Presets
25
+
26
+ | Preset | |
27
+ |---|---|
28
+ | `themes.green` | Emerald green |
29
+ | `themes.cyan` | Sky blue |
30
+ | `themes.red` | Warm red |
31
+ | `themes.purple` | Purple |
32
+ | `themes.orange` | Orange |
33
+ | `themes.dark` | Dark mode |
@@ -1,40 +1,31 @@
1
- import React, { createContext, useContext, useMemo } from 'react';
1
+ import { type ReactNode, createContext, useContext, useMemo } from 'react';
2
+ import type { ThemeConfig } from './create-theme.js';
2
3
 
3
- const ThemeContext = createContext(null);
4
+ const ThemeContext = createContext<ThemeConfig | null>(null);
4
5
 
5
6
  /**
6
7
  * Access the current theme object provided by the nearest ThemeProvider.
7
8
  * Returns `null` when no ThemeProvider is mounted (i.e. using default :root styles).
8
9
  */
9
- export function useTheme() {
10
+ export function useTheme(): ThemeConfig | null {
10
11
  return useContext(ThemeContext);
11
12
  }
12
13
 
14
+ interface ThemeProviderProps {
15
+ theme: ThemeConfig | null;
16
+ children: ReactNode;
17
+ }
18
+
13
19
  /**
14
20
  * Inject a custom theme into a component subtree.
15
21
  *
16
22
  * CSS custom properties are set as inline styles on a wrapper <div>,
17
23
  * scoping them to the subtree. When no ThemeProvider is used, the global
18
24
  * `:root` variables from `theme/styles.css` serve as the default.
19
- *
20
- * @example
21
- * import { ThemeProvider, createTheme } from 'theme';
22
- *
23
- * const brandTheme = createTheme({
24
- * colors: { primary: '142 76% 36%', 'primary-foreground': '0 0% 100%' },
25
- * });
26
- *
27
- * function App() {
28
- * return (
29
- * <ThemeProvider theme={brandTheme}>
30
- * <Button>Brand Button</Button>
31
- * </ThemeProvider>
32
- * );
33
- * }
34
25
  */
35
- export function ThemeProvider({ theme, children }) {
26
+ export function ThemeProvider({ theme, children }: ThemeProviderProps) {
36
27
  const style = useMemo(() => {
37
- const vars = {};
28
+ const vars: Record<string, string> = {};
38
29
  if (!theme) return vars;
39
30
  if (theme.colors) {
40
31
  for (const [key, value] of Object.entries(theme.colors)) {
@@ -1,30 +1,31 @@
1
1
  import { colors, borderRadius, spacing, fontSize } from './tokens.js';
2
+ import type { TokenColors } from './tokens.js';
2
3
 
3
- const defaultTokens = { colors, borderRadius, spacing, fontSize, radius: '0.5rem' };
4
+ export interface ThemeConfig {
5
+ colors: Partial<TokenColors>;
6
+ borderRadius: Record<string, string>;
7
+ spacing: Record<string, string>;
8
+ fontSize: Record<string, [string, { lineHeight: string }]>;
9
+ radius: string;
10
+ }
11
+
12
+ const defaultTokens: ThemeConfig = { colors, borderRadius, spacing, fontSize, radius: '0.5rem' };
4
13
 
5
14
  /**
6
15
  * Create a theme object by merging user overrides with the default design tokens.
7
16
  *
8
- * @param {Object} userConfig
9
- * @param {Object} [userConfig.colors] - Partial color token overrides (HSL strings)
10
- * @param {Object} [userConfig.borderRadius]
11
- * @param {Object} [userConfig.spacing]
12
- * @param {Object} [userConfig.fontSize]
13
- * @param {string} [userConfig.radius] - Base border radius (e.g. '0.75rem')
14
- * @returns {Object} A complete theme object
15
- *
16
17
  * @example
17
18
  * const brandTheme = createTheme({
18
19
  * colors: { primary: '142 76% 36%', 'primary-foreground': '0 0% 100%' },
19
20
  * radius: '0.75rem',
20
21
  * });
21
22
  */
22
- export function createTheme(userConfig = {}) {
23
+ export function createTheme(userConfig: Partial<ThemeConfig> = {}): ThemeConfig {
23
24
  return {
24
25
  colors: { ...defaultTokens.colors, ...userConfig.colors },
25
26
  borderRadius: { ...defaultTokens.borderRadius, ...userConfig.borderRadius },
26
27
  spacing: { ...defaultTokens.spacing, ...userConfig.spacing },
27
28
  fontSize: { ...defaultTokens.fontSize, ...userConfig.fontSize },
28
- radius: userConfig.radius || defaultTokens.radius,
29
+ radius: userConfig.radius ?? defaultTokens.radius,
29
30
  };
30
31
  }
@@ -1,5 +1,6 @@
1
1
  export * from './tokens.js';
2
2
  export { default as tailwindPreset } from './tailwind-preset.js';
3
3
  export { createTheme } from './create-theme.js';
4
+ export type { ThemeConfig } from './create-theme.js';
4
5
  export { themes } from './themes.js';
5
- export { ThemeProvider, useTheme } from './ThemeProvider.jsx';
6
+ export { ThemeProvider, useTheme } from './ThemeProvider.js';
package/package.json CHANGED
@@ -1,29 +1,48 @@
1
1
  {
2
2
  "name": "@xuefx/ui-theme",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "description": "Design tokens, Tailwind preset, and theming tools for @xuefx/ui",
5
5
  "type": "module",
6
- "main": "./index.js",
6
+ "main": "./index.ts",
7
7
  "files": [
8
- "*.js",
9
- "*.jsx",
8
+ "*.ts",
9
+ "*.tsx",
10
10
  "*.css"
11
11
  ],
12
12
  "exports": {
13
- ".": "./index.js",
14
- "./tokens": "./tokens.js",
15
- "./tailwind-preset": "./tailwind-preset.js",
13
+ ".": {
14
+ "types": "./index.ts",
15
+ "default": "./index.ts"
16
+ },
17
+ "./tokens": {
18
+ "types": "./tokens.ts",
19
+ "default": "./tokens.ts"
20
+ },
21
+ "./tailwind-preset": {
22
+ "types": "./tailwind-preset.ts",
23
+ "default": "./tailwind-preset.ts"
24
+ },
16
25
  "./styles.css": "./styles.css",
17
- "./create-theme": "./create-theme.js",
18
- "./ThemeProvider": "./ThemeProvider.jsx",
19
- "./themes": "./themes.js"
26
+ "./create-theme": {
27
+ "types": "./create-theme.ts",
28
+ "default": "./create-theme.ts"
29
+ },
30
+ "./ThemeProvider": {
31
+ "types": "./ThemeProvider.tsx",
32
+ "default": "./ThemeProvider.tsx"
33
+ },
34
+ "./themes": {
35
+ "types": "./themes.ts",
36
+ "default": "./themes.ts"
37
+ }
20
38
  },
21
39
  "peerDependencies": {
22
40
  "react": ">=19.0.0"
23
41
  },
24
42
  "devDependencies": {
25
43
  "react": "^19.1.0",
26
- "react-dom": "^19.1.0"
44
+ "react-dom": "^19.1.0",
45
+ "typescript": "^6.0.3"
27
46
  },
28
47
  "license": "ISC"
29
48
  }
@@ -1,13 +1,16 @@
1
+ import { borderRadius } from './tokens.js';
2
+ import type { Config } from 'tailwindcss';
3
+
1
4
  /**
2
5
  * Tailwind CSS preset that maps CSS custom properties from styles.css
3
6
  * into Tailwind's theme namespace.
4
7
  *
5
8
  * Usage in tailwind.config.js:
6
- * import { tailwindPreset } from 'theme';
9
+ * import tailwindPreset from '@xuefx/ui-theme/tailwind-preset';
7
10
  * export default { presets: [tailwindPreset], ... }
8
11
  */
9
12
 
10
- export default {
13
+ const preset: Partial<Config> = {
11
14
  theme: {
12
15
  extend: {
13
16
  colors: {
@@ -45,11 +48,9 @@ export default {
45
48
  input: 'hsl(var(--input))',
46
49
  ring: 'hsl(var(--ring))',
47
50
  },
48
- borderRadius: {
49
- lg: 'var(--radius)',
50
- md: 'calc(var(--radius) - 2px)',
51
- sm: 'calc(var(--radius) - 4px)',
52
- },
51
+ borderRadius,
53
52
  },
54
53
  },
55
54
  };
55
+
56
+ export default preset;
@@ -1,3 +1,6 @@
1
+ import { createTheme } from './create-theme.js';
2
+ import type { ThemeConfig } from './create-theme.js';
3
+
1
4
  /**
2
5
  * Pre-built theme presets.
3
6
  *
@@ -7,9 +10,10 @@
7
10
  * <App />
8
11
  * </ThemeProvider>
9
12
  */
10
- import { createTheme } from './create-theme.js';
13
+ export const themes: Record<string, ThemeConfig> = {
14
+ /** Default navy — matches :root variables, explicitly wraps with ThemeProvider */
15
+ default: createTheme({}),
11
16
 
12
- export const themes = {
13
17
  /** Sky blue brand — 清新科技感 */
14
18
  cyan: createTheme({
15
19
  colors: {
@@ -6,7 +6,30 @@
6
6
  * Color tokens use HSL channels as space-separated strings — the format
7
7
  * Tailwind's `hsl(var(--name))` expects. No `hsl()` wrapper here.
8
8
  */
9
- export const colors = {
9
+
10
+ export interface TokenColors {
11
+ background: string;
12
+ foreground: string;
13
+ card: string;
14
+ 'card-foreground': string;
15
+ popover: string;
16
+ 'popover-foreground': string;
17
+ primary: string;
18
+ 'primary-foreground': string;
19
+ secondary: string;
20
+ 'secondary-foreground': string;
21
+ muted: string;
22
+ 'muted-foreground': string;
23
+ accent: string;
24
+ 'accent-foreground': string;
25
+ destructive: string;
26
+ 'destructive-foreground': string;
27
+ border: string;
28
+ input: string;
29
+ ring: string;
30
+ }
31
+
32
+ export const colors: TokenColors = {
10
33
  background: '0 0% 100%',
11
34
  foreground: '222.2 84% 4.9%',
12
35
  card: '0 0% 100%',
@@ -28,13 +51,13 @@ export const colors = {
28
51
  ring: '222.2 84% 4.9%',
29
52
  };
30
53
 
31
- export const borderRadius = {
54
+ export const borderRadius: Record<string, string> = {
32
55
  lg: 'var(--radius)',
33
56
  md: 'calc(var(--radius) - 2px)',
34
57
  sm: 'calc(var(--radius) - 4px)',
35
58
  };
36
59
 
37
- export const spacing = {
60
+ export const spacing: Record<string, string> = {
38
61
  0: '0px',
39
62
  1: '0.25rem',
40
63
  2: '0.5rem',
@@ -58,7 +81,7 @@ export const spacing = {
58
81
  96: '24rem',
59
82
  };
60
83
 
61
- export const fontSize = {
84
+ export const fontSize: Record<string, [string, { lineHeight: string }]> = {
62
85
  xs: ['0.75rem', { lineHeight: '1rem' }],
63
86
  sm: ['0.875rem', { lineHeight: '1.25rem' }],
64
87
  base: ['1rem', { lineHeight: '1.5rem' }],