Skip to content

Commit 39b0a3b

Browse files
authored
Support light/dark colors in theme (#958)
* Support light/dark colors in theme * Add example for light/dark
1 parent cbd9252 commit 39b0a3b

File tree

5 files changed

+234
-41
lines changed

5 files changed

+234
-41
lines changed

example/src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ const BaseTheme = createTheme({
163163
tablet: "yellow",
164164
laptop: "red",
165165
},
166+
lightDarkTest: {
167+
light: "white",
168+
dark: "black",
169+
},
166170
},
167171
},
168172
typography: {},

example/src/ThemeExample.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ const VideoPlayerExample: React.FC = () => {
5858
}}
5959
/>
6060
</Section>
61+
<Section style={{}} title="Light/Dark Color">
62+
<View
63+
style={{
64+
width: 100,
65+
height: 100,
66+
borderWidth: 1,
67+
backgroundColor: theme.colors.background.lightDarkTest,
68+
}}
69+
/>
70+
</Section>
6171
</Container>
6272
);
6373
};

packages/theme/src/Provider.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from "react";
2-
import { Dimensions, Platform } from "react-native";
2+
import { Dimensions, Platform, useColorScheme } from "react-native";
33
import AsyncStorage from "@react-native-async-storage/async-storage";
44
import createThemeValuesProxy from "./createThemeValuesProxy";
55
import DefaultTheme from "./DefaultTheme";
@@ -41,6 +41,8 @@ const Provider: React.FC<React.PropsWithChildren<ProviderProps>> = ({
4141
const [deviceWidth, setDeviceWidth] = React.useState(
4242
Dimensions.get("window").width
4343
);
44+
const colorScheme = useColorScheme();
45+
const lightDarkSelection = colorScheme ?? "light";
4446

4547
const changeTheme = React.useCallback(
4648
(themeName: string, options?: ChangeThemeOptions) => {
@@ -72,35 +74,40 @@ const Provider: React.FC<React.PropsWithChildren<ProviderProps>> = ({
7274
currentTheme.colors.branding,
7375
breakpoints,
7476
deviceWidth,
75-
Platform.OS
77+
Platform.OS,
78+
lightDarkSelection
7679
),
7780
text: createThemeValuesProxy(
7881
currentTheme.colors.text,
7982
breakpoints,
8083
deviceWidth,
81-
Platform.OS
84+
Platform.OS,
85+
lightDarkSelection
8286
),
8387
background: createThemeValuesProxy(
8488
currentTheme.colors.background,
8589
breakpoints,
8690
deviceWidth,
87-
Platform.OS
91+
Platform.OS,
92+
lightDarkSelection
8893
),
8994
foreground: createThemeValuesProxy(
9095
currentTheme.colors.foreground,
9196
breakpoints,
9297
deviceWidth,
93-
Platform.OS
98+
Platform.OS,
99+
lightDarkSelection
94100
),
95101
border: createThemeValuesProxy(
96102
currentTheme.colors.border,
97103
breakpoints,
98104
deviceWidth,
99-
Platform.OS
105+
Platform.OS,
106+
lightDarkSelection
100107
),
101108
},
102109
}),
103-
[currentTheme, deviceWidth, breakpoints]
110+
[currentTheme, deviceWidth, breakpoints, lightDarkSelection]
104111
);
105112

106113
React.useEffect(() => {

packages/theme/src/__tests__/createThemeValuesProxy.test.ts

Lines changed: 128 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,22 @@ const value: any = {
4747
nested: "nestedMedium",
4848
},
4949
},
50+
lightDark: {
51+
default: "defaultLightDark",
52+
light: "lightValue",
53+
dark: "darkValue",
54+
},
55+
lightDarkNested: {
56+
light: {
57+
nested: "nestedLight",
58+
},
59+
dark: {
60+
nested: "nestedDark",
61+
},
62+
},
5063
};
5164

52-
const proxied = createThemeValuesProxy(value, {}, 400, "android")!;
65+
const proxied = createThemeValuesProxy(value, {}, 400, "android", "light")!;
5366

5467
describe("createThemeValuesProxy tests", () => {
5568
describe("Value Returned Directly", () => {
@@ -78,29 +91,59 @@ describe("createThemeValuesProxy tests", () => {
7891

7992
describe("Platform Value", () => {
8093
test("returns android value when platform is android", () => {
81-
const androidProxied = createThemeValuesProxy(value, {}, 400, "android")!;
94+
const androidProxied = createThemeValuesProxy(
95+
value,
96+
{},
97+
400,
98+
"android",
99+
"light"
100+
)!;
82101
expect(androidProxied.platform).toEqual(value.platform.android);
83102
});
84103

85104
test("returns ios value when platform is ios", () => {
86-
const iosProxied = createThemeValuesProxy(value, {}, 400, "ios")!;
105+
const iosProxied = createThemeValuesProxy(
106+
value,
107+
{},
108+
400,
109+
"ios",
110+
"light"
111+
)!;
87112
expect(iosProxied.platform).toEqual(value.platform.ios);
88113
});
89114

90115
test("returns default platform value when platform is not in keys", () => {
91-
const windowsProxied = createThemeValuesProxy(value, {}, 400, "windows")!;
116+
const windowsProxied = createThemeValuesProxy(
117+
value,
118+
{},
119+
400,
120+
"windows",
121+
"light"
122+
)!;
92123
expect(windowsProxied.platform).toEqual(value.platform.default);
93124
});
94125

95126
test("returns nested android value when platform is android", () => {
96-
const androidProxied = createThemeValuesProxy(value, {}, 400, "android")!;
127+
const androidProxied = createThemeValuesProxy(
128+
value,
129+
{},
130+
400,
131+
"android",
132+
"light"
133+
)!;
97134
expect(androidProxied.platformNested.nested).toEqual(
98135
value.platformNested.android.nested
99136
);
100137
});
101138

102139
test("returns nested ios value when platform is ios", () => {
103-
const iosProxied = createThemeValuesProxy(value, {}, 400, "ios")!;
140+
const iosProxied = createThemeValuesProxy(
141+
value,
142+
{},
143+
400,
144+
"ios",
145+
"light"
146+
)!;
104147
expect(iosProxied.platformNested.nested).toEqual(
105148
value.platformNested.ios.nested
106149
);
@@ -113,7 +156,8 @@ describe("createThemeValuesProxy tests", () => {
113156
value,
114157
breakpoints,
115158
breakpoints.small,
116-
"android"
159+
"android",
160+
"light"
117161
)!;
118162
expect(smallProxied.breakpoint).toEqual(value.breakpoint.small);
119163
});
@@ -123,7 +167,8 @@ describe("createThemeValuesProxy tests", () => {
123167
value,
124168
breakpoints,
125169
breakpoints.small + 50,
126-
"android"
170+
"android",
171+
"light"
127172
)!;
128173
expect(smallProxied.breakpoint).toEqual(value.breakpoint.small);
129174
});
@@ -133,7 +178,8 @@ describe("createThemeValuesProxy tests", () => {
133178
value,
134179
breakpoints,
135180
breakpoints.medium,
136-
"android"
181+
"android",
182+
"light"
137183
)!;
138184
expect(mediumProxied.breakpoint).toEqual(value.breakpoint.medium);
139185
});
@@ -143,7 +189,8 @@ describe("createThemeValuesProxy tests", () => {
143189
value,
144190
breakpoints,
145191
breakpoints.medium + 50,
146-
"android"
192+
"android",
193+
"light"
147194
)!;
148195
expect(mediumProxied.breakpoint).toEqual(value.breakpoint.medium);
149196
});
@@ -153,7 +200,8 @@ describe("createThemeValuesProxy tests", () => {
153200
value,
154201
breakpoints,
155202
breakpoints.large,
156-
"android"
203+
"android",
204+
"light"
157205
)!;
158206
expect(largeProxied.breakpoint).toEqual(value.breakpoint.large);
159207
});
@@ -163,7 +211,8 @@ describe("createThemeValuesProxy tests", () => {
163211
value,
164212
breakpoints,
165213
breakpoints.large + 400,
166-
"android"
214+
"android",
215+
"light"
167216
)!;
168217
expect(largeProxied.breakpoint).toEqual(value.breakpoint.large);
169218
});
@@ -173,7 +222,8 @@ describe("createThemeValuesProxy tests", () => {
173222
value,
174223
breakpoints,
175224
50,
176-
"android"
225+
"android",
226+
"light"
177227
)!;
178228
expect(verySmallProxied.breakpoint).toEqual(value.breakpoint.default);
179229
});
@@ -183,7 +233,8 @@ describe("createThemeValuesProxy tests", () => {
183233
value,
184234
breakpoints,
185235
breakpoints.small,
186-
"android"
236+
"android",
237+
"light"
187238
)!;
188239
expect(smallProxied.breakpointNested.nested).toEqual(
189240
value.breakpointNested.small.nested
@@ -195,11 +246,73 @@ describe("createThemeValuesProxy tests", () => {
195246
value,
196247
breakpoints,
197248
breakpoints.medium,
198-
"android"
249+
"android",
250+
"light"
199251
)!;
200252
expect(mediumProxied.breakpointNested.nested).toEqual(
201253
value.breakpointNested.medium.nested
202254
);
203255
});
204256
});
257+
258+
describe("Light Dark Value", () => {
259+
test("returns light value when key is light", () => {
260+
const lightProxied = createThemeValuesProxy(
261+
value,
262+
{},
263+
400,
264+
"android",
265+
"light"
266+
)!;
267+
expect(lightProxied.lightDark).toEqual(value.lightDark.light);
268+
});
269+
270+
test("returns dark value when key is dark", () => {
271+
const darkProxied = createThemeValuesProxy(
272+
value,
273+
{},
274+
400,
275+
"android",
276+
"dark"
277+
)!;
278+
expect(darkProxied.lightDark).toEqual(value.lightDark.dark);
279+
});
280+
281+
test("returns default value when key is not light or dark", () => {
282+
const otherProxied = createThemeValuesProxy(
283+
value,
284+
{},
285+
400,
286+
"android",
287+
"other" as any
288+
)!;
289+
expect(otherProxied.lightDark).toEqual(value.lightDark.default);
290+
});
291+
292+
test("returns nested light value when key is light", () => {
293+
const lightProxied = createThemeValuesProxy(
294+
value,
295+
{},
296+
400,
297+
"android",
298+
"light"
299+
)!;
300+
expect(lightProxied.lightDarkNested.nested).toEqual(
301+
value.lightDarkNested.light.nested
302+
);
303+
});
304+
305+
test("returns nested dark value when key is dark", () => {
306+
const darkProxied = createThemeValuesProxy(
307+
value,
308+
{},
309+
400,
310+
"android",
311+
"dark"
312+
)!;
313+
expect(darkProxied.lightDarkNested.nested).toEqual(
314+
value.lightDarkNested.dark.nested
315+
);
316+
});
317+
});
205318
});

0 commit comments

Comments
 (0)