Skip to content

Commit c3a4b3c

Browse files
authored
feat: primer color schemes (#1752)
* feat: primer color schemes Signed-off-by: Adam Setch <[email protected]> * feat: primer color schemes Signed-off-by: Adam Setch <[email protected]> * feat: primer color schemes Signed-off-by: Adam Setch <[email protected]> --------- Signed-off-by: Adam Setch <[email protected]>
1 parent bfbf323 commit c3a4b3c

File tree

11 files changed

+272
-112
lines changed

11 files changed

+272
-112
lines changed

src/main/main.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,15 @@ app.whenReady().then(async () => {
9494

9595
nativeTheme.on('updated', () => {
9696
if (nativeTheme.shouldUseDarkColors) {
97-
mb.window.webContents.send(namespacedEvent('update-theme'), 'DARK');
97+
mb.window.webContents.send(
98+
namespacedEvent('update-theme'),
99+
'DARK_DEFAULT',
100+
);
98101
} else {
99-
mb.window.webContents.send(namespacedEvent('update-theme'), 'LIGHT');
102+
mb.window.webContents.send(
103+
namespacedEvent('update-theme'),
104+
'LIGHT_DEFAULT',
105+
);
100106
}
101107
});
102108

src/renderer/App.css

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@
99
@import "@primer/primitives/dist/css/functional/typography/typography.css";
1010

1111
/* Themes and Colors */
12-
/* TODO Add support for setting color modes (dark_dimmed) - see #1748 */
1312
@import "@primer/primitives/dist/css/functional/themes/light.css";
13+
@import "@primer/primitives/dist/css/functional/themes/light-tritanopia.css";
14+
@import "@primer/primitives/dist/css/functional/themes/light-high-contrast.css";
15+
@import "@primer/primitives/dist/css/functional/themes/light-colorblind.css";
1416
@import "@primer/primitives/dist/css/functional/themes/dark.css";
17+
@import "@primer/primitives/dist/css/functional/themes/dark-colorblind.css";
18+
@import "@primer/primitives/dist/css/functional/themes/dark-dimmed.css";
19+
@import "@primer/primitives/dist/css/functional/themes/dark-high-contrast.css";
20+
@import "@primer/primitives/dist/css/functional/themes/dark-tritanopia.css";
1521

1622
html,
1723
body,

src/renderer/App.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import { NotificationsRoute } from './routes/Notifications';
2121
import { SettingsRoute } from './routes/Settings';
2222

2323
import './App.css';
24+
import {
25+
DEFAULT_DAY_COLOR_SCHEME,
26+
DEFAULT_NIGHT_COLOR_SCHEME,
27+
} from './utils/theme';
2428

2529
function RequireAuth({ children }) {
2630
const { isLoggedIn } = useContext(AppContext);
@@ -35,8 +39,11 @@ function RequireAuth({ children }) {
3539

3640
export const App = () => {
3741
return (
38-
// TODO Add support for setting color modes (dark_dimmed) - see #1748
39-
<ThemeProvider dayScheme="light" nightScheme="dark">
42+
<ThemeProvider
43+
colorMode="auto"
44+
dayScheme={DEFAULT_DAY_COLOR_SCHEME}
45+
nightScheme={DEFAULT_NIGHT_COLOR_SCHEME}
46+
>
4047
<BaseStyles>
4148
<AppProvider>
4249
<Router>

src/renderer/components/settings/AppearanceSettings.test.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe('renderer/routes/components/settings/AppearanceSettings.tsx', () => {
1717
jest.clearAllMocks();
1818
});
1919

20-
it('should change the theme radio group', async () => {
20+
it('should change the theme mode dropdown', async () => {
2121
await act(async () => {
2222
render(
2323
<AppContext.Provider
@@ -34,7 +34,8 @@ describe('renderer/routes/components/settings/AppearanceSettings.tsx', () => {
3434
);
3535
});
3636

37-
fireEvent.click(screen.getByLabelText('Light'));
37+
const select = screen.getByTestId('settings-theme') as HTMLSelectElement;
38+
fireEvent.change(select, { target: { value: 'LIGHT' } });
3839

3940
expect(updateSetting).toHaveBeenCalledTimes(1);
4041
expect(updateSetting).toHaveBeenCalledWith('theme', 'LIGHT');

src/renderer/components/settings/AppearanceSettings.tsx

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,34 @@ import {
1313
TagIcon,
1414
XCircleIcon,
1515
} from '@primer/octicons-react';
16-
import { Button, ButtonGroup, IconButton, useTheme } from '@primer/react';
16+
import {
17+
Button,
18+
ButtonGroup,
19+
IconButton,
20+
Select,
21+
useTheme,
22+
} from '@primer/react';
1723

1824
import { namespacedEvent } from '../../../shared/events';
1925
import { AppContext } from '../../context/App';
2026
import { Size, Theme } from '../../types';
2127
import { hasMultipleAccounts } from '../../utils/auth/utils';
22-
import { getColorModeFromTheme, setTheme } from '../../utils/theme';
28+
import {
29+
DEFAULT_DAY_COLOR_SCHEME,
30+
DEFAULT_NIGHT_COLOR_SCHEME,
31+
isDayScheme,
32+
setScrollbarTheme,
33+
} from '../../utils/theme';
2334
import { zoomLevelToPercentage, zoomPercentageToLevel } from '../../utils/zoom';
2435
import { Checkbox } from '../fields/Checkbox';
2536
import { FieldLabel } from '../fields/FieldLabel';
26-
import { RadioGroup } from '../fields/RadioGroup';
2737
import { Title } from '../primitives/Title';
2838

2939
let timeout: NodeJS.Timeout;
3040
const DELAY = 200;
3141

3242
export const AppearanceSettings: FC = () => {
33-
const { setColorMode } = useTheme();
43+
const { setColorMode, setDayScheme, setNightScheme } = useTheme();
3444
const { auth, settings, updateSetting } = useContext(AppContext);
3545
const [zoomPercentage, setZoomPercentage] = useState(
3646
zoomLevelToPercentage(webFrame.getZoomLevel()),
@@ -41,14 +51,15 @@ export const AppearanceSettings: FC = () => {
4151
namespacedEvent('update-theme'),
4252
(_, updatedTheme: Theme) => {
4353
if (settings.theme === Theme.SYSTEM) {
44-
const mode = getColorModeFromTheme(updatedTheme);
45-
46-
setTheme(updatedTheme);
47-
setColorMode(mode);
54+
const mode = isDayScheme(updatedTheme) ? 'day' : 'night';
55+
setColorMode('auto');
56+
setDayScheme(DEFAULT_DAY_COLOR_SCHEME);
57+
setNightScheme(DEFAULT_NIGHT_COLOR_SCHEME);
58+
setScrollbarTheme(mode);
4859
}
4960
},
5061
);
51-
}, [settings.theme, setColorMode]);
62+
}, [settings.theme, setColorMode, setDayScheme, setNightScheme]);
5263

5364
window.addEventListener('resize', () => {
5465
// clear the timeout
@@ -65,20 +76,48 @@ export const AppearanceSettings: FC = () => {
6576
<fieldset>
6677
<Title icon={PaintbrushIcon}>Appearance</Title>
6778

68-
<RadioGroup
69-
name="theme"
70-
label="Theme:"
71-
value={settings.theme}
72-
options={[
73-
{ label: 'System', value: Theme.SYSTEM },
74-
{ label: 'Light', value: Theme.LIGHT },
75-
{ label: 'Dark', value: Theme.DARK },
76-
]}
77-
onChange={(evt) => updateSetting('theme', evt.target.value as Theme)}
78-
/>
79+
<div className="flex items-center mt-3 mb-2 text-sm">
80+
<FieldLabel name="theme" label="Theme:" />
81+
82+
<Select
83+
id="theme"
84+
value={settings.theme}
85+
onChange={(evt) => updateSetting('theme', evt.target.value as Theme)}
86+
data-testid="settings-theme"
87+
>
88+
<Select.OptGroup label="System">
89+
<Select.Option value={Theme.SYSTEM}>System</Select.Option>
90+
</Select.OptGroup>
91+
<Select.OptGroup label="Light">
92+
<Select.Option value={Theme.LIGHT}>Light default</Select.Option>
93+
<Select.Option value={Theme.LIGHT_HIGH_CONTRAST}>
94+
Light high contrast
95+
</Select.Option>
96+
<Select.Option value={Theme.LIGHT_COLORBLIND}>
97+
Light Protanopia & Deuteranopia
98+
</Select.Option>
99+
<Select.Option value={Theme.LIGHT_TRITANOPIA}>
100+
Light Tritanopia
101+
</Select.Option>
102+
</Select.OptGroup>
103+
<Select.OptGroup label="Dark">
104+
<Select.Option value={Theme.DARK}>Dark default</Select.Option>
105+
<Select.Option value={Theme.DARK_HIGH_CONTRAST}>
106+
Dark high contrast
107+
</Select.Option>
108+
<Select.Option value={Theme.DARK_COLORBLIND}>
109+
Dark Protanopia & Deuteranopia
110+
</Select.Option>
111+
<Select.Option value={Theme.DARK_TRITANOPIA}>
112+
Dark Tritanopia
113+
</Select.Option>
114+
<Select.Option value={Theme.DARK_DIMMED}>Dark dimmed</Select.Option>
115+
</Select.OptGroup>
116+
</Select>
117+
</div>
79118

80119
<div className="flex items-center mt-3 mb-2 text-sm">
81-
<FieldLabel name="zoom" label="Zoom" />
120+
<FieldLabel name="zoom" label="Zoom:" />
82121

83122
<ButtonGroup>
84123
<IconButton

src/renderer/context/App.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,13 @@ import {
4949
import { Constants } from '../utils/constants';
5050
import { getNotificationCount } from '../utils/notifications/notifications';
5151
import { clearState, loadState, saveState } from '../utils/storage';
52-
import { getColorModeFromTheme, setTheme } from '../utils/theme';
52+
import {
53+
DEFAULT_DAY_COLOR_SCHEME,
54+
DEFAULT_NIGHT_COLOR_SCHEME,
55+
isDayScheme,
56+
mapThemeModeToColorScheme,
57+
setScrollbarTheme,
58+
} from '../utils/theme';
5359
import { zoomPercentageToLevel } from '../utils/zoom';
5460

5561
export const defaultAuth: AuthState = {
@@ -125,7 +131,7 @@ interface AppContextState {
125131
export const AppContext = createContext<Partial<AppContextState>>({});
126132

127133
export const AppProvider = ({ children }: { children: ReactNode }) => {
128-
const { setColorMode } = useTheme();
134+
const { setColorMode, setDayScheme, setNightScheme } = useTheme();
129135
const [auth, setAuth] = useState<AuthState>(defaultAuth);
130136
const [settings, setSettings] = useState<SettingsState>(defaultSettings);
131137
const {
@@ -144,11 +150,18 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
144150
}, []);
145151

146152
useEffect(() => {
147-
const mode = getColorModeFromTheme(settings.theme);
148-
setColorMode(mode);
153+
const colorScheme = mapThemeModeToColorScheme(settings.theme);
149154

150-
setTheme(settings.theme); // TODO - Replace fully with Primer design tokens and components
151-
}, [settings.theme, setColorMode]);
155+
if (isDayScheme(settings.theme)) {
156+
setDayScheme(colorScheme ?? DEFAULT_DAY_COLOR_SCHEME);
157+
setColorMode('day');
158+
setScrollbarTheme('day');
159+
} else {
160+
setNightScheme(colorScheme ?? DEFAULT_NIGHT_COLOR_SCHEME);
161+
setColorMode('night');
162+
setScrollbarTheme('night');
163+
}
164+
}, [settings.theme, setColorMode, setDayScheme, setNightScheme]);
152165

153166
// biome-ignore lint/correctness/useExhaustiveDependencies: We only want fetchNotifications to be called for account changes
154167
useEffect(() => {

0 commit comments

Comments
 (0)