Skip to content

Commit 9f9163e

Browse files
docs(theming): add guide for dark mode to theming (#940)
closes ionic-team/ionic-framework#18713
1 parent 227f18b commit 9f9163e

File tree

4 files changed

+279
-3
lines changed

4 files changed

+279
-3
lines changed

src/assets/locales/en/messages.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@
5151
"menu-theming-css-variables": "CSS Variables",
5252
"menu-theming-colors": "Colors",
5353
"menu-theming-themes": "Themes",
54-
"menu-theming-color-generator": "Color Generator",
54+
"menu-theming-dark-mode": "Dark Mode",
5555
"menu-theming-advanced": "Advanced",
56+
"menu-theming-color-generator": "Color Generator",
5657

5758
"menu-angular": "Angular",
5859
"menu-angular-overview": "Overview",

src/components/menu/templates/main.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const items = {
4444
'menu-theming-css-variables': '/docs/theming/css-variables',
4545
'menu-theming-colors': '/docs/theming/colors',
4646
'menu-theming-themes': '/docs/theming/themes',
47+
'menu-theming-dark-mode': '/docs/theming/dark-mode',
4748
'menu-theming-advanced': '/docs/theming/advanced',
4849
'menu-theming-color-generator': '/docs/theming/color-generator',
4950
},

src/pages/theming/dark-mode.md

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
---
2+
initialTab: 'preview'
3+
inlineHtmlPreviews: true
4+
previousText: 'Themes'
5+
previousUrl: '/docs/theming/themes'
6+
nextText: 'Advanced Theming'
7+
nextUrl: '/docs/theming/advanced'
8+
---
9+
10+
# Dark Mode
11+
12+
Ionic makes it easy to change the themes of your app, including supporting dark color schemes. With growing support for dark mode in native apps, developers are now looking to add it to their apps to support user preferences.
13+
14+
## Using Media Queries
15+
16+
The first way to enable dark mode is by using the [CSS media query for the user's preferred color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). This media query will hook into the system setting of the user's device and apply the theme if a dark mode is enabled.
17+
18+
```css
19+
@media (prefers-color-scheme: dark) {
20+
:root {
21+
/* dark mode variables go here */
22+
}
23+
}
24+
```
25+
26+
Currently, the `prefers-color-scheme` media query has [limited browser support](https://caniuse.com/#feat=prefers-color-scheme), so users will not be able to benefit from having the dark mode applied using this media query in certain browsers. However, the dark mode can still be applied by using a [CSS class fallback](#css-class-fallback).
27+
28+
29+
## CSS Class Fallback
30+
31+
As a fallback method for devices that don't support the media query, the dark mode can be applied by styling a CSS selector and applying the class to the document body.
32+
33+
```css
34+
@media (prefers-color-scheme: dark) {
35+
:root {
36+
/* Dark mode variables go here */
37+
}
38+
}
39+
40+
/* Fallback for older browsers or manual mode */
41+
body.dark {
42+
/* Dark mode variables go here */
43+
}
44+
```
45+
46+
With the variables targeting the `body.dark` selector, all that is needed now is to add the class to the `<body>` in the app. This can be done in a variety of ways depending on the framework your app is built with.
47+
48+
Notice that the variables should be in both places in this example. We can [use JavaScript](#combining-with-javascript) in order to avoid setting the variables in two places.
49+
50+
51+
## Combining with JavaScript
52+
53+
In order to keep the CSS variables written once and avoid having to update them in multiple places, the fallback and class can be combined by using JavaScript to check the value of the `prefers-color-scheme` media query and adding the `dark` class if the preference is `dark`. Here's what the CSS would look like:
54+
55+
```css
56+
body.dark {
57+
/* Dark mode variables go here */
58+
}
59+
```
60+
61+
Notice that the variables above are only in the `body.dark` selector now, and the `prefers-color-scheme` media query has been removed. Now, in the JavaScript, the `dark` class can be added to the `<body>` by checking if the document matches the media query using [matchMedia()](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia). The `loadApp()` function should be called on the load of an application, based on the framework being used:
62+
63+
```javascript
64+
// Use matchMedia to check the user preference
65+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
66+
67+
// Listen for changes to the prefers-color-scheme media query
68+
prefersDark.addListener((mediaQuery) => toggleDarkTheme(mediaQuery.matches));
69+
70+
// Called when the app loads
71+
function loadApp() {
72+
toggleDarkTheme(prefersDark.matches);
73+
}
74+
75+
// Add or remove the "dark" class based on if the media query matches
76+
function toggleDarkTheme(shouldAdd) {
77+
document.body.classList.toggle('dark', shouldAdd);
78+
}
79+
```
80+
81+
<!-- Codepen https://codepen.io/ionic/pen/jONzJpG -->
82+
<code-pen user="ionic" slug="jONzJpG"></code-pen>
83+
84+
In addition to calling `toggleDarkTheme()` from `loadApp()` and when the media query changes, the `toggleDarkTheme()` function could be called by the app, such as when a user changes a toggle, to switch between the light and dark themes:
85+
86+
```javascript
87+
// Query for the toggle that is used to change between themes
88+
const toggle = document.querySelector('#themeToggle');
89+
90+
// Listen for the toggle check/uncheck to toggle the dark class on the <body>
91+
toggle.addEventListener('ionChange', (ev) => {
92+
document.body.classList.toggle('dark', ev.detail.checked);
93+
});
94+
95+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
96+
97+
// Listen for changes to the prefers-color-scheme media query
98+
prefersDark.addListener((e) => checkToggle(e.matches));
99+
100+
// Called when the app loads
101+
function loadApp() {
102+
checkToggle(prefersDark.matches);
103+
}
104+
105+
// Called by the media query to check/uncheck the toggle
106+
function checkToggle(shouldCheck) {
107+
toggle.checked = shouldCheck;
108+
}
109+
```
110+
111+
<!-- Codepen https://codepen.io/ionic/pen/zYOpQLj -->
112+
<code-pen user="ionic" slug="zYOpQLj"></code-pen>
113+
114+
115+
## Ionic Dark Theme
116+
117+
Ionic has a recommended theme for variables to use in order to get a dark mode based on the device running the app. It can be broken down into the following parts:
118+
119+
1. Changing the default [Ionic colors](/docs/theming/colors) for all [modes](/docs/theming/platform-styles#ionic-modes) to complement the dark background in the `body.dark` selector.
120+
1. Setting variables for the dark theme on `ios` devices.
121+
1. Setting variables for the dark theme on `md` devices.
122+
123+
The following code can be copied and pasted into an app to get Ionic's dark theme. We add the `dark` class to the document body using JavaScript as mentioned in the [combining with JavaScript](#combining-with-javascript) section. The dark mode will not be enabled until the `dark` class is added to the document body.
124+
125+
> For more information on the variables that are being changed, including other variables that can be added to further customize, see [Themes](/docs/theming/themes).
126+
127+
128+
```css
129+
/*
130+
* Dark Colors
131+
* -------------------------------------------
132+
*/
133+
134+
body.dark {
135+
--ion-color-primary: #428cff;
136+
--ion-color-primary-rgb: 66,140,255;
137+
--ion-color-primary-contrast: #ffffff;
138+
--ion-color-primary-contrast-rgb: 255,255,255;
139+
--ion-color-primary-shade: #3a7be0;
140+
--ion-color-primary-tint: #5598ff;
141+
142+
--ion-color-secondary: #50c8ff;
143+
--ion-color-secondary-rgb: 80,200,255;
144+
--ion-color-secondary-contrast: #ffffff;
145+
--ion-color-secondary-contrast-rgb: 255,255,255;
146+
--ion-color-secondary-shade: #46b0e0;
147+
--ion-color-secondary-tint: #62ceff;
148+
149+
--ion-color-tertiary: #6a64ff;
150+
--ion-color-tertiary-rgb: 106,100,255;
151+
--ion-color-tertiary-contrast: #ffffff;
152+
--ion-color-tertiary-contrast-rgb: 255,255,255;
153+
--ion-color-tertiary-shade: #5d58e0;
154+
--ion-color-tertiary-tint: #7974ff;
155+
156+
--ion-color-success: #2fdf75;
157+
--ion-color-success-rgb: 47,223,117;
158+
--ion-color-success-contrast: #000000;
159+
--ion-color-success-contrast-rgb: 0,0,0;
160+
--ion-color-success-shade: #29c467;
161+
--ion-color-success-tint: #44e283;
162+
163+
--ion-color-warning: #ffd534;
164+
--ion-color-warning-rgb: 255,213,52;
165+
--ion-color-warning-contrast: #000000;
166+
--ion-color-warning-contrast-rgb: 0,0,0;
167+
--ion-color-warning-shade: #e0bb2e;
168+
--ion-color-warning-tint: #ffd948;
169+
170+
--ion-color-danger: #ff4961;
171+
--ion-color-danger-rgb: 255,73,97;
172+
--ion-color-danger-contrast: #ffffff;
173+
--ion-color-danger-contrast-rgb: 255,255,255;
174+
--ion-color-danger-shade: #e04055;
175+
--ion-color-danger-tint: #ff5b71;
176+
177+
--ion-color-dark: #f4f5f8;
178+
--ion-color-dark-rgb: 244,245,248;
179+
--ion-color-dark-contrast: #000000;
180+
--ion-color-dark-contrast-rgb: 0,0,0;
181+
--ion-color-dark-shade: #d7d8da;
182+
--ion-color-dark-tint: #f5f6f9;
183+
184+
--ion-color-medium: #989aa2;
185+
--ion-color-medium-rgb: 152,154,162;
186+
--ion-color-medium-contrast: #000000;
187+
--ion-color-medium-contrast-rgb: 0,0,0;
188+
--ion-color-medium-shade: #86888f;
189+
--ion-color-medium-tint: #a2a4ab;
190+
191+
--ion-color-light: #222428;
192+
--ion-color-light-rgb: 34,36,40;
193+
--ion-color-light-contrast: #ffffff;
194+
--ion-color-light-contrast-rgb: 255,255,255;
195+
--ion-color-light-shade: #1e2023;
196+
--ion-color-light-tint: #383a3e;
197+
}
198+
199+
/*
200+
* iOS Dark Theme
201+
* -------------------------------------------
202+
*/
203+
204+
.ios body.dark {
205+
--ion-background-color: #000000;
206+
--ion-background-color-rgb: 0,0,0;
207+
208+
--ion-text-color: #ffffff;
209+
--ion-text-color-rgb: 255,255,255;
210+
211+
--ion-color-step-50: #0d0d0d;
212+
--ion-color-step-100: #1a1a1a;
213+
--ion-color-step-150: #262626;
214+
--ion-color-step-200: #333333;
215+
--ion-color-step-250: #404040;
216+
--ion-color-step-300: #4d4d4d;
217+
--ion-color-step-350: #595959;
218+
--ion-color-step-400: #666666;
219+
--ion-color-step-450: #737373;
220+
--ion-color-step-500: #808080;
221+
--ion-color-step-550: #8c8c8c;
222+
--ion-color-step-600: #999999;
223+
--ion-color-step-650: #a6a6a6;
224+
--ion-color-step-700: #b3b3b3;
225+
--ion-color-step-750: #bfbfbf;
226+
--ion-color-step-800: #cccccc;
227+
--ion-color-step-850: #d9d9d9;
228+
--ion-color-step-900: #e6e6e6;
229+
--ion-color-step-950: #f2f2f2;
230+
231+
--ion-toolbar-background: #0d0d0d;
232+
233+
--ion-item-background: #1c1c1c;
234+
--ion-item-background-activated: #313131;
235+
}
236+
237+
238+
/*
239+
* Material Design Dark Theme
240+
* -------------------------------------------
241+
*/
242+
243+
.md body.dark {
244+
--ion-background-color: #121212;
245+
--ion-background-color-rgb: 18,18,18;
246+
247+
--ion-text-color: #ffffff;
248+
--ion-text-color-rgb: 255,255,255;
249+
250+
--ion-border-color: #222222;
251+
252+
--ion-color-step-50: #1e1e1e;
253+
--ion-color-step-100: #2a2a2a;
254+
--ion-color-step-150: #363636;
255+
--ion-color-step-200: #414141;
256+
--ion-color-step-250: #4d4d4d;
257+
--ion-color-step-300: #595959;
258+
--ion-color-step-350: #656565;
259+
--ion-color-step-400: #717171;
260+
--ion-color-step-450: #7d7d7d;
261+
--ion-color-step-500: #898989;
262+
--ion-color-step-550: #949494;
263+
--ion-color-step-600: #a0a0a0;
264+
--ion-color-step-650: #acacac;
265+
--ion-color-step-700: #b8b8b8;
266+
--ion-color-step-750: #c4c4c4;
267+
--ion-color-step-800: #d0d0d0;
268+
--ion-color-step-850: #dbdbdb;
269+
--ion-color-step-900: #e7e7e7;
270+
--ion-color-step-950: #f3f3f3;
271+
272+
--ion-item-background: #1A1B1E;
273+
}
274+
```

src/pages/theming/themes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ initialTab: 'preview'
33
inlineHtmlPreviews: true
44
previousText: 'Colors'
55
previousUrl: '/docs/theming/colors'
6-
nextText: 'Advanced Theming'
7-
nextUrl: '/docs/theming/advanced'
6+
nextText: 'Dark Mode'
7+
nextUrl: '/docs/theming/dark-mode'
88
---
99

1010
# Themes

0 commit comments

Comments
 (0)