@crystin001/theme-settings-lib 1.0.0 → 1.1.0
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 +71 -38
- package/dist/components/ThemeShowcase.d.ts +10 -0
- package/dist/components/ThemeShowcase.d.ts.map +1 -0
- package/dist/components/ThemeShowcase.js +357 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/stories/ThemeStories.d.ts +6 -0
- package/dist/stories/ThemeStories.d.ts.map +1 -0
- package/dist/stories/ThemeStories.js +285 -0
- package/dist/stories/index.d.ts +2 -0
- package/dist/stories/index.d.ts.map +1 -0
- package/dist/stories/index.js +4 -0
- package/dist/theme-validation.d.ts +27 -0
- package/dist/theme-validation.d.ts.map +1 -0
- package/dist/theme-validation.js +114 -0
- package/dist/theme.d.ts +15 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +263 -7
- package/package.json +26 -14
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.IndividualThemes = exports.DarkThemes = exports.LightThemes = exports.AllThemes = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const react_native_1 = require("react-native");
|
|
9
|
+
const theme_settings_lib_1 = require("@crystin001/theme-settings-lib");
|
|
10
|
+
// Color Swatch Component
|
|
11
|
+
function ColorSwatch({ label, color, textColor }) {
|
|
12
|
+
return (<react_native_1.View style={[styles.swatch, { backgroundColor: color }]}>
|
|
13
|
+
<react_native_1.Text style={[styles.swatchLabel, { color: textColor || '#000' }]} numberOfLines={1}>
|
|
14
|
+
{label}
|
|
15
|
+
</react_native_1.Text>
|
|
16
|
+
<react_native_1.Text style={[styles.swatchValue, { color: textColor || '#000' }]} numberOfLines={1}>
|
|
17
|
+
{color}
|
|
18
|
+
</react_native_1.Text>
|
|
19
|
+
</react_native_1.View>);
|
|
20
|
+
}
|
|
21
|
+
// Theme Preview Component
|
|
22
|
+
function ThemePreview({ themeName }) {
|
|
23
|
+
const colors = theme_settings_lib_1.Themes[themeName];
|
|
24
|
+
const { setThemeFamily } = (0, theme_settings_lib_1.useSettings)();
|
|
25
|
+
const displayName = themeName.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
|
|
26
|
+
const handleSelect = () => {
|
|
27
|
+
const family = themeName.replace(/-light$|-dark$/, '');
|
|
28
|
+
setThemeFamily(family);
|
|
29
|
+
};
|
|
30
|
+
return (<react_native_1.View style={[styles.themeCard, { backgroundColor: colors.card, borderColor: colors.border }]}>
|
|
31
|
+
<react_native_1.View style={[styles.themeHeader, { backgroundColor: colors.background }]}>
|
|
32
|
+
<react_native_1.Text style={[styles.themeTitle, { color: colors.text }]}>{displayName}</react_native_1.Text>
|
|
33
|
+
</react_native_1.View>
|
|
34
|
+
|
|
35
|
+
<react_native_1.View style={styles.colorGrid}>
|
|
36
|
+
<ColorSwatch label="Background" color={colors.background} textColor={colors.text}/>
|
|
37
|
+
<ColorSwatch label="Text" color={colors.text}/>
|
|
38
|
+
<ColorSwatch label="Primary" color={colors.primary}/>
|
|
39
|
+
<ColorSwatch label="Secondary" color={colors.secondary}/>
|
|
40
|
+
<ColorSwatch label="Tint" color={colors.tint}/>
|
|
41
|
+
<ColorSwatch label="Card" color={colors.card} textColor={colors.cardText}/>
|
|
42
|
+
</react_native_1.View>
|
|
43
|
+
|
|
44
|
+
<react_native_1.View style={styles.sampleSection}>
|
|
45
|
+
<react_native_1.Text style={[styles.sampleLabel, { color: colors.text }]}>Sample UI:</react_native_1.Text>
|
|
46
|
+
|
|
47
|
+
<react_native_1.View style={[styles.sampleCard, { backgroundColor: colors.card, borderColor: colors.border }]}>
|
|
48
|
+
<react_native_1.Text style={[styles.sampleText, { color: colors.cardText }]}>
|
|
49
|
+
Sample card content
|
|
50
|
+
</react_native_1.Text>
|
|
51
|
+
</react_native_1.View>
|
|
52
|
+
|
|
53
|
+
<react_native_1.View style={styles.buttonRow}>
|
|
54
|
+
<react_native_1.Pressable style={[styles.sampleButton, { backgroundColor: colors.button }]} onPress={handleSelect}>
|
|
55
|
+
<react_native_1.Text style={[styles.sampleButtonText, { color: colors.buttonText }]}>
|
|
56
|
+
Apply Theme
|
|
57
|
+
</react_native_1.Text>
|
|
58
|
+
</react_native_1.Pressable>
|
|
59
|
+
<react_native_1.Pressable style={[styles.sampleButton, styles.sampleButtonSecondary, { backgroundColor: colors.buttonSecondary }]} onPress={() => { }}>
|
|
60
|
+
<react_native_1.Text style={[styles.sampleButtonText, { color: colors.buttonSecondaryText }]}>
|
|
61
|
+
Secondary
|
|
62
|
+
</react_native_1.Text>
|
|
63
|
+
</react_native_1.Pressable>
|
|
64
|
+
</react_native_1.View>
|
|
65
|
+
|
|
66
|
+
<react_native_1.View style={styles.statusRow}>
|
|
67
|
+
<react_native_1.View style={[styles.statusBadge, { backgroundColor: colors.success }]}>
|
|
68
|
+
<react_native_1.Text style={[styles.statusText, { color: '#FFF' }]}>Success</react_native_1.Text>
|
|
69
|
+
</react_native_1.View>
|
|
70
|
+
<react_native_1.View style={[styles.statusBadge, { backgroundColor: colors.warning }]}>
|
|
71
|
+
<react_native_1.Text style={[styles.statusText, { color: '#000' }]}>Warning</react_native_1.Text>
|
|
72
|
+
</react_native_1.View>
|
|
73
|
+
<react_native_1.View style={[styles.statusBadge, { backgroundColor: colors.error }]}>
|
|
74
|
+
<react_native_1.Text style={[styles.statusText, { color: '#FFF' }]}>Error</react_native_1.Text>
|
|
75
|
+
</react_native_1.View>
|
|
76
|
+
<react_native_1.View style={[styles.statusBadge, { backgroundColor: colors.info }]}>
|
|
77
|
+
<react_native_1.Text style={[styles.statusText, { color: '#FFF' }]}>Info</react_native_1.Text>
|
|
78
|
+
</react_native_1.View>
|
|
79
|
+
</react_native_1.View>
|
|
80
|
+
|
|
81
|
+
<react_native_1.View style={[styles.sampleInput, { backgroundColor: colors.input, borderColor: colors.inputBorder }]}>
|
|
82
|
+
<react_native_1.Text style={[styles.sampleInputText, { color: colors.inputText }]}>
|
|
83
|
+
Sample input field
|
|
84
|
+
</react_native_1.Text>
|
|
85
|
+
</react_native_1.View>
|
|
86
|
+
</react_native_1.View>
|
|
87
|
+
</react_native_1.View>);
|
|
88
|
+
}
|
|
89
|
+
// All Themes Story
|
|
90
|
+
const AllThemes = () => {
|
|
91
|
+
const themeEntries = Object.entries(theme_settings_lib_1.Themes);
|
|
92
|
+
return (<theme_settings_lib_1.SettingsProvider>
|
|
93
|
+
<react_native_1.ScrollView style={styles.container} contentContainerStyle={styles.scrollContent}>
|
|
94
|
+
<react_native_1.View style={styles.header}>
|
|
95
|
+
<react_native_1.Text style={styles.headerTitle}>All Themes</react_native_1.Text>
|
|
96
|
+
<react_native_1.Text style={styles.headerSubtitle}>Tap "Apply Theme" to switch themes</react_native_1.Text>
|
|
97
|
+
</react_native_1.View>
|
|
98
|
+
{themeEntries.map(([themeName]) => (<ThemePreview key={themeName} themeName={themeName}/>))}
|
|
99
|
+
</react_native_1.ScrollView>
|
|
100
|
+
</theme_settings_lib_1.SettingsProvider>);
|
|
101
|
+
};
|
|
102
|
+
exports.AllThemes = AllThemes;
|
|
103
|
+
// Light Themes Story
|
|
104
|
+
const LightThemes = () => {
|
|
105
|
+
const lightThemes = Object.keys(theme_settings_lib_1.Themes).filter(name => name.endsWith('-light'));
|
|
106
|
+
return (<theme_settings_lib_1.SettingsProvider>
|
|
107
|
+
<react_native_1.ScrollView style={styles.container} contentContainerStyle={styles.scrollContent}>
|
|
108
|
+
<react_native_1.View style={styles.header}>
|
|
109
|
+
<react_native_1.Text style={styles.headerTitle}>Light Themes</react_native_1.Text>
|
|
110
|
+
</react_native_1.View>
|
|
111
|
+
{lightThemes.map((themeName) => (<ThemePreview key={themeName} themeName={themeName}/>))}
|
|
112
|
+
</react_native_1.ScrollView>
|
|
113
|
+
</theme_settings_lib_1.SettingsProvider>);
|
|
114
|
+
};
|
|
115
|
+
exports.LightThemes = LightThemes;
|
|
116
|
+
// Dark Themes Story
|
|
117
|
+
const DarkThemes = () => {
|
|
118
|
+
const darkThemes = Object.keys(theme_settings_lib_1.Themes).filter(name => name.endsWith('-dark'));
|
|
119
|
+
return (<theme_settings_lib_1.SettingsProvider>
|
|
120
|
+
<react_native_1.ScrollView style={styles.container} contentContainerStyle={styles.scrollContent}>
|
|
121
|
+
<react_native_1.View style={styles.header}>
|
|
122
|
+
<react_native_1.Text style={styles.headerTitle}>Dark Themes</react_native_1.Text>
|
|
123
|
+
</react_native_1.View>
|
|
124
|
+
{darkThemes.map((themeName) => (<ThemePreview key={themeName} themeName={themeName}/>))}
|
|
125
|
+
</react_native_1.ScrollView>
|
|
126
|
+
</theme_settings_lib_1.SettingsProvider>);
|
|
127
|
+
};
|
|
128
|
+
exports.DarkThemes = DarkThemes;
|
|
129
|
+
// Individual Theme Stories - shows all themes in one view
|
|
130
|
+
const IndividualThemes = () => {
|
|
131
|
+
const themeEntries = Object.entries(theme_settings_lib_1.Themes);
|
|
132
|
+
return (<theme_settings_lib_1.SettingsProvider>
|
|
133
|
+
<react_native_1.ScrollView style={styles.container} contentContainerStyle={styles.scrollContent}>
|
|
134
|
+
<react_native_1.View style={styles.header}>
|
|
135
|
+
<react_native_1.Text style={styles.headerTitle}>Individual Themes</react_native_1.Text>
|
|
136
|
+
<react_native_1.Text style={styles.headerSubtitle}>Scroll to see all themes</react_native_1.Text>
|
|
137
|
+
</react_native_1.View>
|
|
138
|
+
{themeEntries.map(([themeName]) => {
|
|
139
|
+
const displayName = themeName.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
|
|
140
|
+
return (<react_native_1.View key={themeName} style={styles.themeSection}>
|
|
141
|
+
<react_native_1.Text style={styles.sectionTitle}>{displayName}</react_native_1.Text>
|
|
142
|
+
<ThemePreview themeName={themeName}/>
|
|
143
|
+
</react_native_1.View>);
|
|
144
|
+
})}
|
|
145
|
+
</react_native_1.ScrollView>
|
|
146
|
+
</theme_settings_lib_1.SettingsProvider>);
|
|
147
|
+
};
|
|
148
|
+
exports.IndividualThemes = IndividualThemes;
|
|
149
|
+
const styles = react_native_1.StyleSheet.create({
|
|
150
|
+
container: {
|
|
151
|
+
flex: 1,
|
|
152
|
+
backgroundColor: '#F5F5F5',
|
|
153
|
+
},
|
|
154
|
+
scrollContent: {
|
|
155
|
+
padding: 20,
|
|
156
|
+
gap: 20,
|
|
157
|
+
},
|
|
158
|
+
header: {
|
|
159
|
+
padding: 20,
|
|
160
|
+
backgroundColor: '#FFF',
|
|
161
|
+
borderRadius: 8,
|
|
162
|
+
marginBottom: 8,
|
|
163
|
+
},
|
|
164
|
+
headerTitle: {
|
|
165
|
+
fontSize: 24,
|
|
166
|
+
fontWeight: 'bold',
|
|
167
|
+
color: '#000',
|
|
168
|
+
marginBottom: 4,
|
|
169
|
+
},
|
|
170
|
+
headerSubtitle: {
|
|
171
|
+
fontSize: 14,
|
|
172
|
+
color: '#666',
|
|
173
|
+
},
|
|
174
|
+
themeCard: {
|
|
175
|
+
borderRadius: 12,
|
|
176
|
+
padding: 16,
|
|
177
|
+
marginBottom: 16,
|
|
178
|
+
borderWidth: 1,
|
|
179
|
+
shadowColor: '#000',
|
|
180
|
+
shadowOffset: { width: 0, height: 2 },
|
|
181
|
+
shadowOpacity: 0.1,
|
|
182
|
+
shadowRadius: 4,
|
|
183
|
+
elevation: 3,
|
|
184
|
+
},
|
|
185
|
+
themeHeader: {
|
|
186
|
+
marginBottom: 16,
|
|
187
|
+
padding: 12,
|
|
188
|
+
borderRadius: 8,
|
|
189
|
+
},
|
|
190
|
+
themeTitle: {
|
|
191
|
+
fontSize: 18,
|
|
192
|
+
fontWeight: 'bold',
|
|
193
|
+
},
|
|
194
|
+
colorGrid: {
|
|
195
|
+
flexDirection: 'row',
|
|
196
|
+
flexWrap: 'wrap',
|
|
197
|
+
gap: 8,
|
|
198
|
+
marginBottom: 16,
|
|
199
|
+
},
|
|
200
|
+
swatch: {
|
|
201
|
+
width: '30%',
|
|
202
|
+
height: 60,
|
|
203
|
+
borderRadius: 6,
|
|
204
|
+
padding: 6,
|
|
205
|
+
justifyContent: 'flex-end',
|
|
206
|
+
},
|
|
207
|
+
swatchLabel: {
|
|
208
|
+
fontSize: 10,
|
|
209
|
+
fontWeight: '600',
|
|
210
|
+
marginBottom: 2,
|
|
211
|
+
},
|
|
212
|
+
swatchValue: {
|
|
213
|
+
fontSize: 9,
|
|
214
|
+
opacity: 0.8,
|
|
215
|
+
},
|
|
216
|
+
sampleSection: {
|
|
217
|
+
marginTop: 8,
|
|
218
|
+
},
|
|
219
|
+
sampleLabel: {
|
|
220
|
+
fontSize: 14,
|
|
221
|
+
fontWeight: '600',
|
|
222
|
+
marginBottom: 8,
|
|
223
|
+
},
|
|
224
|
+
sampleCard: {
|
|
225
|
+
padding: 12,
|
|
226
|
+
borderRadius: 8,
|
|
227
|
+
borderWidth: 1,
|
|
228
|
+
marginBottom: 12,
|
|
229
|
+
},
|
|
230
|
+
sampleText: {
|
|
231
|
+
fontSize: 14,
|
|
232
|
+
},
|
|
233
|
+
buttonRow: {
|
|
234
|
+
flexDirection: 'row',
|
|
235
|
+
gap: 8,
|
|
236
|
+
marginBottom: 12,
|
|
237
|
+
},
|
|
238
|
+
sampleButton: {
|
|
239
|
+
flex: 1,
|
|
240
|
+
paddingVertical: 10,
|
|
241
|
+
paddingHorizontal: 16,
|
|
242
|
+
borderRadius: 6,
|
|
243
|
+
alignItems: 'center',
|
|
244
|
+
},
|
|
245
|
+
sampleButtonSecondary: {
|
|
246
|
+
// Additional styles if needed
|
|
247
|
+
},
|
|
248
|
+
sampleButtonText: {
|
|
249
|
+
fontSize: 14,
|
|
250
|
+
fontWeight: '600',
|
|
251
|
+
},
|
|
252
|
+
statusRow: {
|
|
253
|
+
flexDirection: 'row',
|
|
254
|
+
flexWrap: 'wrap',
|
|
255
|
+
gap: 8,
|
|
256
|
+
marginBottom: 12,
|
|
257
|
+
},
|
|
258
|
+
statusBadge: {
|
|
259
|
+
paddingHorizontal: 10,
|
|
260
|
+
paddingVertical: 6,
|
|
261
|
+
borderRadius: 12,
|
|
262
|
+
},
|
|
263
|
+
statusText: {
|
|
264
|
+
fontSize: 11,
|
|
265
|
+
fontWeight: '600',
|
|
266
|
+
},
|
|
267
|
+
sampleInput: {
|
|
268
|
+
padding: 12,
|
|
269
|
+
borderRadius: 6,
|
|
270
|
+
borderWidth: 1,
|
|
271
|
+
},
|
|
272
|
+
sampleInputText: {
|
|
273
|
+
fontSize: 14,
|
|
274
|
+
},
|
|
275
|
+
themeSection: {
|
|
276
|
+
marginBottom: 24,
|
|
277
|
+
},
|
|
278
|
+
sectionTitle: {
|
|
279
|
+
fontSize: 20,
|
|
280
|
+
fontWeight: 'bold',
|
|
281
|
+
color: '#000',
|
|
282
|
+
marginBottom: 12,
|
|
283
|
+
paddingHorizontal: 4,
|
|
284
|
+
},
|
|
285
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stories/index.ts"],"names":[],"mappings":"AACA,OAAO,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme validation utilities for checking contrast and accessibility
|
|
3
|
+
*/
|
|
4
|
+
import { ThemeColors } from './theme';
|
|
5
|
+
export interface ContrastIssue {
|
|
6
|
+
property: string;
|
|
7
|
+
foreground: string;
|
|
8
|
+
background: string;
|
|
9
|
+
contrastRatio: number;
|
|
10
|
+
meetsAA: boolean;
|
|
11
|
+
meetsAAA: boolean;
|
|
12
|
+
suggestion?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ThemeValidationResult {
|
|
15
|
+
themeName: string;
|
|
16
|
+
issues: ContrastIssue[];
|
|
17
|
+
hasIssues: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Validate contrast for all text/background combinations in a theme
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateThemeContrast(themeName: string, colors: ThemeColors): ThemeValidationResult;
|
|
23
|
+
/**
|
|
24
|
+
* Validate all themes and return results
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateAllThemes(themes: Record<string, ThemeColors>): ThemeValidationResult[];
|
|
27
|
+
//# sourceMappingURL=theme-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-validation.d.ts","sourceRoot":"","sources":["../src/theme-validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGtC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,GAClB,qBAAqB,CAgHvB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,qBAAqB,EAAE,CAE9F"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Theme validation utilities for checking contrast and accessibility
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.validateThemeContrast = validateThemeContrast;
|
|
7
|
+
exports.validateAllThemes = validateAllThemes;
|
|
8
|
+
const color_utils_1 = require("./color-utils");
|
|
9
|
+
/**
|
|
10
|
+
* Validate contrast for all text/background combinations in a theme
|
|
11
|
+
*/
|
|
12
|
+
function validateThemeContrast(themeName, colors) {
|
|
13
|
+
const issues = [];
|
|
14
|
+
// Check text on background
|
|
15
|
+
const textContrast = (0, color_utils_1.getContrastRatio)(colors.text, colors.background);
|
|
16
|
+
if (!(0, color_utils_1.meetsContrastRequirement)(colors.text, colors.background, 'AA')) {
|
|
17
|
+
issues.push({
|
|
18
|
+
property: 'text on background',
|
|
19
|
+
foreground: colors.text,
|
|
20
|
+
background: colors.background,
|
|
21
|
+
contrastRatio: textContrast,
|
|
22
|
+
meetsAA: false,
|
|
23
|
+
meetsAAA: (0, color_utils_1.meetsContrastRequirement)(colors.text, colors.background, 'AAA'),
|
|
24
|
+
suggestion: (0, color_utils_1.getContrastTextColor)(colors.background),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Check cardText on card
|
|
28
|
+
const cardTextContrast = (0, color_utils_1.getContrastRatio)(colors.cardText, colors.card);
|
|
29
|
+
if (!(0, color_utils_1.meetsContrastRequirement)(colors.cardText, colors.card, 'AA')) {
|
|
30
|
+
issues.push({
|
|
31
|
+
property: 'cardText on card',
|
|
32
|
+
foreground: colors.cardText,
|
|
33
|
+
background: colors.card,
|
|
34
|
+
contrastRatio: cardTextContrast,
|
|
35
|
+
meetsAA: false,
|
|
36
|
+
meetsAAA: (0, color_utils_1.meetsContrastRequirement)(colors.cardText, colors.card, 'AAA'),
|
|
37
|
+
suggestion: (0, color_utils_1.getContrastTextColor)(colors.card),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Check buttonText on button
|
|
41
|
+
const buttonTextContrast = (0, color_utils_1.getContrastRatio)(colors.buttonText, colors.button);
|
|
42
|
+
if (!(0, color_utils_1.meetsContrastRequirement)(colors.buttonText, colors.button, 'AA')) {
|
|
43
|
+
issues.push({
|
|
44
|
+
property: 'buttonText on button',
|
|
45
|
+
foreground: colors.buttonText,
|
|
46
|
+
background: colors.button,
|
|
47
|
+
contrastRatio: buttonTextContrast,
|
|
48
|
+
meetsAA: false,
|
|
49
|
+
meetsAAA: (0, color_utils_1.meetsContrastRequirement)(colors.buttonText, colors.button, 'AAA'),
|
|
50
|
+
suggestion: (0, color_utils_1.getContrastTextColor)(colors.button),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Check buttonSecondaryText on buttonSecondary
|
|
54
|
+
const buttonSecondaryTextContrast = (0, color_utils_1.getContrastRatio)(colors.buttonSecondaryText, colors.buttonSecondary);
|
|
55
|
+
if (!(0, color_utils_1.meetsContrastRequirement)(colors.buttonSecondaryText, colors.buttonSecondary, 'AA')) {
|
|
56
|
+
issues.push({
|
|
57
|
+
property: 'buttonSecondaryText on buttonSecondary',
|
|
58
|
+
foreground: colors.buttonSecondaryText,
|
|
59
|
+
background: colors.buttonSecondary,
|
|
60
|
+
contrastRatio: buttonSecondaryTextContrast,
|
|
61
|
+
meetsAA: false,
|
|
62
|
+
meetsAAA: (0, color_utils_1.meetsContrastRequirement)(colors.buttonSecondaryText, colors.buttonSecondary, 'AAA'),
|
|
63
|
+
suggestion: (0, color_utils_1.getContrastTextColor)(colors.buttonSecondary),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Check inputText on input
|
|
67
|
+
const inputTextContrast = (0, color_utils_1.getContrastRatio)(colors.inputText, colors.input);
|
|
68
|
+
if (!(0, color_utils_1.meetsContrastRequirement)(colors.inputText, colors.input, 'AA')) {
|
|
69
|
+
issues.push({
|
|
70
|
+
property: 'inputText on input',
|
|
71
|
+
foreground: colors.inputText,
|
|
72
|
+
background: colors.input,
|
|
73
|
+
contrastRatio: inputTextContrast,
|
|
74
|
+
meetsAA: false,
|
|
75
|
+
meetsAAA: (0, color_utils_1.meetsContrastRequirement)(colors.inputText, colors.input, 'AAA'),
|
|
76
|
+
suggestion: (0, color_utils_1.getContrastTextColor)(colors.input),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// Check status colors with white text (common pattern)
|
|
80
|
+
const statusColors = [
|
|
81
|
+
{ name: 'success', color: colors.success },
|
|
82
|
+
{ name: 'warning', color: colors.warning },
|
|
83
|
+
{ name: 'error', color: colors.error },
|
|
84
|
+
{ name: 'info', color: colors.info },
|
|
85
|
+
];
|
|
86
|
+
statusColors.forEach(({ name, color }) => {
|
|
87
|
+
const whiteContrast = (0, color_utils_1.getContrastRatio)('#FFFFFF', color);
|
|
88
|
+
const blackContrast = (0, color_utils_1.getContrastRatio)('#000000', color);
|
|
89
|
+
const bestText = whiteContrast > blackContrast ? '#FFFFFF' : '#000000';
|
|
90
|
+
const bestContrast = Math.max(whiteContrast, blackContrast);
|
|
91
|
+
if (!(0, color_utils_1.meetsContrastRequirement)(bestText, color, 'AA')) {
|
|
92
|
+
issues.push({
|
|
93
|
+
property: `text on ${name}`,
|
|
94
|
+
foreground: bestText,
|
|
95
|
+
background: color,
|
|
96
|
+
contrastRatio: bestContrast,
|
|
97
|
+
meetsAA: false,
|
|
98
|
+
meetsAAA: (0, color_utils_1.meetsContrastRequirement)(bestText, color, 'AAA'),
|
|
99
|
+
suggestion: (0, color_utils_1.getContrastTextColor)(color),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
themeName,
|
|
105
|
+
issues,
|
|
106
|
+
hasIssues: issues.length > 0,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Validate all themes and return results
|
|
111
|
+
*/
|
|
112
|
+
function validateAllThemes(themes) {
|
|
113
|
+
return Object.entries(themes).map(([name, colors]) => validateThemeContrast(name, colors));
|
|
114
|
+
}
|
package/dist/theme.d.ts
CHANGED
|
@@ -30,6 +30,21 @@ export interface ThemeColors {
|
|
|
30
30
|
primary: string;
|
|
31
31
|
secondary: string;
|
|
32
32
|
muted: string;
|
|
33
|
+
accent1: string;
|
|
34
|
+
accent1Light: string;
|
|
35
|
+
accent1Dark: string;
|
|
36
|
+
accent2: string;
|
|
37
|
+
accent2Light: string;
|
|
38
|
+
accent2Dark: string;
|
|
39
|
+
accent3: string;
|
|
40
|
+
accent3Light: string;
|
|
41
|
+
accent3Dark: string;
|
|
42
|
+
accent4: string;
|
|
43
|
+
accent4Light: string;
|
|
44
|
+
accent4Dark: string;
|
|
45
|
+
accent5: string;
|
|
46
|
+
accent5Light: string;
|
|
47
|
+
accent5Dark: string;
|
|
33
48
|
}
|
|
34
49
|
export declare const Themes: Record<ActualThemeName, ThemeColors>;
|
|
35
50
|
export declare const Colors: {
|
package/dist/theme.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,WAAW,GACX,aAAa,GACb,QAAQ,GACR,QAAQ,GACR,eAAe,GACf,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5C,MAAM,MAAM,SAAS,GAAG,GAAG,WAAW,IAAI,YAAY,EAAE,GAAG,QAAQ,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3D,MAAM,WAAW,WAAW;IAE1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IAExB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,WAAW,GACX,aAAa,GACb,QAAQ,GACR,QAAQ,GACR,eAAe,GACf,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5C,MAAM,MAAM,SAAS,GAAG,GAAG,WAAW,IAAI,YAAY,EAAE,GAAG,QAAQ,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3D,MAAM,WAAW,WAAW;IAE1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IAExB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IAEd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,WAAW,CAypBvD,CAAC;AAGF,eAAO,MAAM,MAAM;;;CAGlB,CAAC;AAEF,eAAO,MAAM,KAAK;IAEd,gDAAgD;;IAEhD,8CAA8C;;IAE9C,gDAAgD;;IAEhD,mDAAmD;;CAerD,CAAC"}
|