@principal-ade/industry-theme 0.1.2
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/dist/cjs/ThemeProvider.d.ts +19 -0
- package/dist/cjs/ThemeProvider.d.ts.map +1 -0
- package/dist/cjs/ThemeShowcase.d.ts +24 -0
- package/dist/cjs/ThemeShowcase.d.ts.map +1 -0
- package/dist/cjs/defaultThemes.d.ts +8 -0
- package/dist/cjs/defaultThemes.d.ts.map +1 -0
- package/dist/cjs/glassmorphismTheme.d.ts +7 -0
- package/dist/cjs/glassmorphismTheme.d.ts.map +1 -0
- package/dist/cjs/index.d.ts +130 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +1798 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/themeHelpers.d.ts +30 -0
- package/dist/cjs/themeHelpers.d.ts.map +1 -0
- package/dist/cjs/themes.d.ts +12 -0
- package/dist/cjs/themes.d.ts.map +1 -0
- package/dist/cjs/utils.d.ts +32 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/esm/ThemeProvider.d.ts +19 -0
- package/dist/esm/ThemeProvider.d.ts.map +1 -0
- package/dist/esm/ThemeShowcase.d.ts +24 -0
- package/dist/esm/ThemeShowcase.d.ts.map +1 -0
- package/dist/esm/defaultThemes.d.ts +8 -0
- package/dist/esm/defaultThemes.d.ts.map +1 -0
- package/dist/esm/glassmorphismTheme.d.ts +7 -0
- package/dist/esm/glassmorphismTheme.d.ts.map +1 -0
- package/dist/esm/index.d.ts +130 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +1753 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/themeHelpers.d.ts +30 -0
- package/dist/esm/themeHelpers.d.ts.map +1 -0
- package/dist/esm/themes.d.ts +12 -0
- package/dist/esm/themes.d.ts.map +1 -0
- package/dist/esm/utils.d.ts +32 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/package.json +64 -0
- package/src/README.md +134 -0
- package/src/ThemeProvider.tsx +117 -0
- package/src/ThemeShowcase.tsx +442 -0
- package/src/defaultThemes.ts +369 -0
- package/src/glassmorphismTheme.ts +213 -0
- package/src/index.ts +230 -0
- package/src/themeHelpers.ts +106 -0
- package/src/themes.ts +746 -0
- package/src/utils.ts +135 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Theme } from './index';
|
|
2
|
+
/**
|
|
3
|
+
* Override colors in a theme with type checking
|
|
4
|
+
*/
|
|
5
|
+
export declare function overrideColors<T extends Theme>(theme: T, colors: Partial<T['colors']>): T;
|
|
6
|
+
/**
|
|
7
|
+
* Create a new theme by extending a base theme
|
|
8
|
+
*/
|
|
9
|
+
export declare function makeTheme<T extends Theme>(baseTheme: T, overrides: Partial<{
|
|
10
|
+
colors: Partial<T['colors']>;
|
|
11
|
+
fonts: Partial<T['fonts']>;
|
|
12
|
+
fontSizes: T['fontSizes'];
|
|
13
|
+
space: T['space'];
|
|
14
|
+
radii: T['radii'];
|
|
15
|
+
}>): T;
|
|
16
|
+
/**
|
|
17
|
+
* Add a new mode to a theme
|
|
18
|
+
* @param theme - The theme to add the mode to
|
|
19
|
+
* @param modeName - The name of the new mode (e.g., 'dark', 'light', 'high-contrast')
|
|
20
|
+
* @param colors - Partial colors to override for this mode
|
|
21
|
+
* @param baseMode - Optional existing mode to extend from (defaults to base colors)
|
|
22
|
+
*/
|
|
23
|
+
export declare function addMode<T extends Theme>(theme: T, modeName: string, colors: Partial<T['colors']>, baseMode?: string): T;
|
|
24
|
+
/**
|
|
25
|
+
* Get colors for a specific mode
|
|
26
|
+
* @param theme - The theme to get colors from
|
|
27
|
+
* @param mode - The mode name (returns base colors if mode doesn't exist)
|
|
28
|
+
*/
|
|
29
|
+
export declare function getMode<T extends Theme>(theme: T, mode?: string): T['colors'];
|
|
30
|
+
//# sourceMappingURL=themeHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeHelpers.d.ts","sourceRoot":"","sources":["../../src/themeHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,KAAK,EAC5C,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAC3B,CAAC,CAQH;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,KAAK,EACvC,SAAS,EAAE,CAAC,EACZ,SAAS,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3B,SAAS,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IAClB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;CACnB,CAAC,GACD,CAAC,CAaH;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,KAAK,EACrC,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAC5B,QAAQ,CAAC,EAAE,MAAM,GAChB,CAAC,CA6BH;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,KAAK,EACrC,KAAK,EAAE,CAAC,EACR,IAAI,CAAC,EAAE,MAAM,GACZ,CAAC,CAAC,QAAQ,CAAC,CASb"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection of available themes for PrincipleMD
|
|
3
|
+
*/
|
|
4
|
+
import { Theme } from './index';
|
|
5
|
+
export { glassmorphismTheme } from './glassmorphismTheme';
|
|
6
|
+
export { defaultMarkdownTheme, defaultEditorTheme, defaultTerminalTheme, } from './defaultThemes';
|
|
7
|
+
export declare const regalTheme: Theme;
|
|
8
|
+
export declare const terminalTheme: Theme;
|
|
9
|
+
export declare const matrixTheme: Theme;
|
|
10
|
+
export declare const matrixMinimalTheme: Theme;
|
|
11
|
+
export declare const slateTheme: Theme;
|
|
12
|
+
//# sourceMappingURL=themes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../../src/themes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAGzB,eAAO,MAAM,UAAU,EAAE,KAsIxB,CAAC;AAGF,eAAO,MAAM,aAAa,EAAE,KA6K3B,CAAC;AAGF,eAAO,MAAM,WAAW,EAAE,KA0IzB,CAAC;AAGF,eAAO,MAAM,kBAAkB,EAAE,KA0IhC,CAAC;AAGF,eAAO,MAAM,UAAU,EAAE,KAyIxB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Theme } from './index';
|
|
2
|
+
type StyleValue = string | number | StyleValue[] | {
|
|
3
|
+
[key: string]: StyleValue;
|
|
4
|
+
};
|
|
5
|
+
type StyleObject = Record<string, StyleValue>;
|
|
6
|
+
/**
|
|
7
|
+
* Utility functions for working with the Theme UI spec-compliant theme
|
|
8
|
+
*/
|
|
9
|
+
export declare const getColor: (theme: Theme, colorKey: string) => string;
|
|
10
|
+
export declare const getSpace: (theme: Theme, index: number) => number;
|
|
11
|
+
export declare const getFontSize: (theme: Theme, index: number) => number;
|
|
12
|
+
export declare const getRadius: (theme: Theme, index: number) => number;
|
|
13
|
+
export declare const getShadow: (theme: Theme, index: number) => string;
|
|
14
|
+
export declare const getZIndex: (theme: Theme, index: number) => number;
|
|
15
|
+
export declare const responsive: (values: (string | number)[]) => object;
|
|
16
|
+
export declare const sx: (styles: StyleObject) => StyleObject;
|
|
17
|
+
export declare const createStyle: (theme: Theme, styleObj: StyleObject) => StyleObject;
|
|
18
|
+
export declare const mergeThemes: (baseTheme: Theme, ...overrides: Partial<Theme>[]) => Theme;
|
|
19
|
+
declare const _default: {
|
|
20
|
+
getColor: (theme: Theme, colorKey: string) => string;
|
|
21
|
+
getSpace: (theme: Theme, index: number) => number;
|
|
22
|
+
getFontSize: (theme: Theme, index: number) => number;
|
|
23
|
+
getRadius: (theme: Theme, index: number) => number;
|
|
24
|
+
getShadow: (theme: Theme, index: number) => string;
|
|
25
|
+
getZIndex: (theme: Theme, index: number) => number;
|
|
26
|
+
responsive: (values: (string | number)[]) => object;
|
|
27
|
+
sx: (styles: StyleObject) => StyleObject;
|
|
28
|
+
createStyle: (theme: Theme, styleObj: StyleObject) => StyleObject;
|
|
29
|
+
mergeThemes: (baseTheme: Theme, ...overrides: Partial<Theme>[]) => Theme;
|
|
30
|
+
};
|
|
31
|
+
export default _default;
|
|
32
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGhC,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC;AACjF,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE9C;;GAEG;AAGH,eAAO,MAAM,QAAQ,GAAI,OAAO,KAAK,EAAE,UAAU,MAAM,KAAG,MAKzD,CAAC;AAGF,eAAO,MAAM,QAAQ,GAAI,OAAO,KAAK,EAAE,OAAO,MAAM,KAAG,MAEtD,CAAC;AAGF,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,EAAE,OAAO,MAAM,KAAG,MAEzD,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,OAAO,KAAK,EAAE,OAAO,MAAM,KAAG,MAEvD,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,OAAO,KAAK,EAAE,OAAO,MAAM,KAAG,MAEvD,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,OAAO,KAAK,EAAE,OAAO,MAAM,KAAG,MAEvD,CAAC;AAGF,eAAO,MAAM,UAAU,GAAI,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KAAG,MAUxD,CAAC;AAGF,eAAO,MAAM,EAAE,GAAI,QAAQ,WAAW,gBAAW,CAAC;AAGlD,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,EAAE,UAAU,WAAW,KAAG,WAmCjE,CAAC;AAGF,eAAO,MAAM,WAAW,GAAI,WAAW,KAAK,EAAE,GAAG,WAAW,OAAO,CAAC,KAAK,CAAC,EAAE,KAAG,KAuB9E,CAAC;;sBA9G8B,KAAK,YAAY,MAAM,KAAG,MAAM;sBAQhC,KAAK,SAAS,MAAM,KAAG,MAAM;yBAK1B,KAAK,SAAS,MAAM,KAAG,MAAM;uBAK/B,KAAK,SAAS,MAAM,KAAG,MAAM;uBAK7B,KAAK,SAAS,MAAM,KAAG,MAAM;uBAK7B,KAAK,SAAS,MAAM,KAAG,MAAM;yBAK3B,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KAAG,MAAM;iBAapC,WAAW;yBAGH,KAAK,YAAY,WAAW,KAAG,WAAW;6BAsCtC,KAAK,gBAAgB,OAAO,CAAC,KAAK,CAAC,EAAE,KAAG,KAAK;;AAyBpF,wBAWE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@principal-ade/industry-theme",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Theme components and styles for industry-themed markdown",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/esm/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/esm/index.d.ts",
|
|
12
|
+
"import": "./dist/esm/index.js",
|
|
13
|
+
"require": "./dist/cjs/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "npm run build:clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:copy-package-files",
|
|
18
|
+
"build:clean": "rm -rf dist",
|
|
19
|
+
"build:esm": "bun build ./src/index.ts --outfile ./dist/esm/index.js --format esm --external react",
|
|
20
|
+
"build:cjs": "bun build ./src/index.ts --outfile ./dist/cjs/index.js --format cjs --external react",
|
|
21
|
+
"build:types": "tsc --emitDeclarationOnly --declaration --declarationMap --outDir ./dist/esm",
|
|
22
|
+
"build:copy-package-files": "echo '{\"type\":\"module\"}' > dist/esm/package.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json && cp -r dist/esm/*.d.ts dist/esm/*.d.ts.map dist/esm/types dist/esm/utils dist/cjs/ 2>/dev/null || true",
|
|
23
|
+
"dev": "npm run build --watch",
|
|
24
|
+
"test": "bun test",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"lint": "eslint . --ext .ts,.tsx",
|
|
27
|
+
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
|
28
|
+
"format": "prettier --write .",
|
|
29
|
+
"format:check": "prettier --check .",
|
|
30
|
+
"clean": "rm -rf dist"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"markdown",
|
|
34
|
+
"theme",
|
|
35
|
+
"industry"
|
|
36
|
+
],
|
|
37
|
+
"author": "Principal ADE Team",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@eslint/js": "^9.32.0",
|
|
41
|
+
"@types/bun": "latest",
|
|
42
|
+
"@types/react": "^19.1.12",
|
|
43
|
+
"eslint": "^9.32.0",
|
|
44
|
+
"eslint-config-prettier": "^10.1.8",
|
|
45
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
46
|
+
"eslint-plugin-import": "^2.32.0",
|
|
47
|
+
"prettier": "^3.6.2",
|
|
48
|
+
"react": ">=19.0.0",
|
|
49
|
+
"typescript": "^5.0.4",
|
|
50
|
+
"typescript-eslint": "^8.38.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"react": ">=19.0.0"
|
|
54
|
+
},
|
|
55
|
+
"files": [
|
|
56
|
+
"dist",
|
|
57
|
+
"src",
|
|
58
|
+
"README.md"
|
|
59
|
+
],
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "git+https://github.com/principal-ade/industry-theme.git"
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Theme UI Spec-Compliant Theme System
|
|
2
|
+
|
|
3
|
+
This directory contains a new Theme UI specification-compliant theme system for the PrincipleMD electron-react project.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
- **`index.ts`** - Main theme configuration following Theme UI spec
|
|
8
|
+
- **`ThemeProvider.tsx`** - React context provider and useTheme hook
|
|
9
|
+
- **`utils.ts`** - Utility functions for working with theme values
|
|
10
|
+
- **`README.md`** - This documentation
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
### 1. Wrap your app with the ThemeProvider
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { ThemeProvider } from './theme/ThemeProvider';
|
|
18
|
+
import { theme } from './theme';
|
|
19
|
+
|
|
20
|
+
function App() {
|
|
21
|
+
return (
|
|
22
|
+
<ThemeProvider theme={theme} initialColorMode="light">
|
|
23
|
+
<YourApp />
|
|
24
|
+
</ThemeProvider>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2. Use the theme in components
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { useTheme } from './theme/ThemeProvider';
|
|
33
|
+
import { getSpace, getFontSize } from './theme/utils';
|
|
34
|
+
|
|
35
|
+
function MyComponent() {
|
|
36
|
+
const { theme, colorMode, toggleColorMode } = useTheme();
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div
|
|
40
|
+
style={{
|
|
41
|
+
backgroundColor: theme.colors.background,
|
|
42
|
+
color: theme.colors.text,
|
|
43
|
+
padding: getSpace(theme, 4), // theme.space[4]
|
|
44
|
+
fontSize: getFontSize(theme, 2), // theme.fontSizes[2]
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
Current mode: {colorMode}
|
|
48
|
+
<button onClick={toggleColorMode}>Toggle Mode</button>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Available theme properties
|
|
55
|
+
|
|
56
|
+
#### Colors
|
|
57
|
+
|
|
58
|
+
- `theme.colors.text` - Primary text color
|
|
59
|
+
- `theme.colors.background` - Primary background color
|
|
60
|
+
- `theme.colors.primary` - Primary brand color
|
|
61
|
+
- `theme.colors.secondary` - Secondary brand color
|
|
62
|
+
- `theme.colors.success` - Success state color
|
|
63
|
+
- `theme.colors.warning` - Warning state color
|
|
64
|
+
- `theme.colors.error` - Error state color
|
|
65
|
+
- `theme.colors.border` - Border color
|
|
66
|
+
- `theme.colors.backgroundSecondary` - Secondary background
|
|
67
|
+
- `theme.colors.textSecondary` - Secondary text color
|
|
68
|
+
|
|
69
|
+
#### Typography
|
|
70
|
+
|
|
71
|
+
- `theme.fonts.body` - Body font family
|
|
72
|
+
- `theme.fonts.heading` - Heading font family
|
|
73
|
+
- `theme.fonts.monospace` - Monospace font family
|
|
74
|
+
- `theme.fontSizes[0-9]` - Font size scale (12px to 96px)
|
|
75
|
+
- `theme.fontWeights.body` - Body font weight
|
|
76
|
+
- `theme.fontWeights.heading` - Heading font weight
|
|
77
|
+
|
|
78
|
+
#### Spacing & Layout
|
|
79
|
+
|
|
80
|
+
- `theme.space[0-8]` - Spacing scale (0px to 512px)
|
|
81
|
+
- `theme.radii[0-7]` - Border radius scale (0px to 24px)
|
|
82
|
+
- `theme.shadows[0-5]` - Box shadow scale
|
|
83
|
+
- `theme.breakpoints` - Responsive breakpoints
|
|
84
|
+
|
|
85
|
+
### 4. Utility functions
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { getColor, getSpace, getFontSize, getRadius } from './theme/utils';
|
|
89
|
+
|
|
90
|
+
// Get theme values safely
|
|
91
|
+
const backgroundColor = getColor(theme, 'background');
|
|
92
|
+
const padding = getSpace(theme, 3); // theme.space[3]
|
|
93
|
+
const fontSize = getFontSize(theme, 2); // theme.fontSizes[2]
|
|
94
|
+
const borderRadius = getRadius(theme, 1); // theme.radii[1]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Color Modes
|
|
98
|
+
|
|
99
|
+
The theme system supports automatic light/dark mode switching:
|
|
100
|
+
|
|
101
|
+
- Automatically detects system preference on first load
|
|
102
|
+
- Saves user preference to localStorage
|
|
103
|
+
- Provides `toggleColorMode()` function
|
|
104
|
+
- Dark mode colors are defined in `theme.colors.modes.dark`
|
|
105
|
+
|
|
106
|
+
## Migration from old theme
|
|
107
|
+
|
|
108
|
+
The new theme maintains backward compatibility with existing color property names:
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
// Old usage (still works)
|
|
112
|
+
theme.colors.background;
|
|
113
|
+
theme.colors.primary;
|
|
114
|
+
theme.colors.textSecondary;
|
|
115
|
+
|
|
116
|
+
// New Theme UI spec additions
|
|
117
|
+
theme.fontSizes[2]; // instead of hardcoded values
|
|
118
|
+
theme.space[4]; // instead of hardcoded spacing
|
|
119
|
+
theme.radii[2]; // instead of hardcoded border radius
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Theme UI Specification Compliance
|
|
123
|
+
|
|
124
|
+
This theme follows the [Theme UI theme specification](https://theme-ui.com/theme-spec) which provides:
|
|
125
|
+
|
|
126
|
+
- Consistent scales for spacing, typography, and colors
|
|
127
|
+
- Responsive design support
|
|
128
|
+
- Component variant support
|
|
129
|
+
- Color mode handling
|
|
130
|
+
- Standard naming conventions
|
|
131
|
+
|
|
132
|
+
## Example Component
|
|
133
|
+
|
|
134
|
+
See `ThemeExample.tsx` for a comprehensive example showing all theme features.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { createContext, useContext, ReactNode, useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
import { getMode } from './themeHelpers';
|
|
4
|
+
|
|
5
|
+
import { Theme, theme as defaultTheme } from './index';
|
|
6
|
+
|
|
7
|
+
// Theme context
|
|
8
|
+
interface ThemeContextValue {
|
|
9
|
+
theme: Theme;
|
|
10
|
+
mode?: string;
|
|
11
|
+
setMode: (mode: string) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Create a singleton context instance that persists across module boundaries
|
|
15
|
+
let ThemeContext: React.Context<ThemeContextValue | undefined>;
|
|
16
|
+
|
|
17
|
+
// Use a global variable to ensure single instance across all imports
|
|
18
|
+
const getThemeContext = () => {
|
|
19
|
+
if (typeof window !== 'undefined') {
|
|
20
|
+
// Store on window to ensure true singleton in browser
|
|
21
|
+
const globalWindow = window as Window & {
|
|
22
|
+
__principlemd_theme_context__?: React.Context<ThemeContextValue | undefined>;
|
|
23
|
+
};
|
|
24
|
+
if (!globalWindow.__principlemd_theme_context__) {
|
|
25
|
+
globalWindow.__principlemd_theme_context__ = createContext<ThemeContextValue | undefined>(
|
|
26
|
+
undefined,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return globalWindow.__principlemd_theme_context__;
|
|
30
|
+
} else {
|
|
31
|
+
// Server-side rendering or Node environment
|
|
32
|
+
if (!ThemeContext) {
|
|
33
|
+
ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
|
|
34
|
+
}
|
|
35
|
+
return ThemeContext;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Get the singleton context
|
|
40
|
+
const ThemeContextSingleton = getThemeContext();
|
|
41
|
+
|
|
42
|
+
// Hook to use theme
|
|
43
|
+
export const useTheme = (): ThemeContextValue => {
|
|
44
|
+
const context = useContext(ThemeContextSingleton);
|
|
45
|
+
|
|
46
|
+
if (!context) {
|
|
47
|
+
throw new Error('useTheme must be used within a ThemeProvider');
|
|
48
|
+
}
|
|
49
|
+
return context as ThemeContextValue;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Theme provider component
|
|
53
|
+
interface ThemeProviderProps {
|
|
54
|
+
children: ReactNode;
|
|
55
|
+
theme?: Theme;
|
|
56
|
+
initialMode?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
60
|
+
children,
|
|
61
|
+
theme: customTheme = defaultTheme,
|
|
62
|
+
initialMode,
|
|
63
|
+
}) => {
|
|
64
|
+
const [mode, setMode] = useState<string | undefined>(initialMode);
|
|
65
|
+
|
|
66
|
+
// Get the theme with the current mode applied
|
|
67
|
+
const activeTheme = React.useMemo(() => {
|
|
68
|
+
if (!mode || !customTheme.modes || !customTheme.modes[mode]) {
|
|
69
|
+
return customTheme;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Apply the mode colors to the theme
|
|
73
|
+
return {
|
|
74
|
+
...customTheme,
|
|
75
|
+
colors: getMode(customTheme, mode),
|
|
76
|
+
};
|
|
77
|
+
}, [customTheme, mode]);
|
|
78
|
+
|
|
79
|
+
// Load saved mode from localStorage on mount
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (!initialMode) {
|
|
82
|
+
const savedMode = localStorage.getItem('principlemd-theme-mode');
|
|
83
|
+
if (savedMode) {
|
|
84
|
+
setMode(savedMode);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}, [initialMode]);
|
|
88
|
+
|
|
89
|
+
// Save mode to localStorage when it changes
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (mode) {
|
|
92
|
+
localStorage.setItem('principlemd-theme-mode', mode);
|
|
93
|
+
} else {
|
|
94
|
+
localStorage.removeItem('principlemd-theme-mode');
|
|
95
|
+
}
|
|
96
|
+
}, [mode]);
|
|
97
|
+
|
|
98
|
+
const value: ThemeContextValue = {
|
|
99
|
+
theme: activeTheme,
|
|
100
|
+
mode,
|
|
101
|
+
setMode,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return <ThemeContextSingleton.Provider value={value}>{children}</ThemeContextSingleton.Provider>;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Higher-order component for theme access
|
|
108
|
+
export const withTheme = <P extends object>(
|
|
109
|
+
Component: React.ComponentType<P & { theme: Theme }>,
|
|
110
|
+
) => {
|
|
111
|
+
return (props: P) => {
|
|
112
|
+
const { theme } = useTheme();
|
|
113
|
+
return <Component {...props} theme={theme} />;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default ThemeProvider;
|