Skip to content

Commit 3efa9f4

Browse files
authored
Try to read Jupyter CSS properties even if body attribute is not set. (#71)
* Try to read Jupyter CSS properties even if body attribute is not set. * Simplify the story about theme mode from CDN
1 parent 4735668 commit 3efa9f4

File tree

5 files changed

+77
-23
lines changed

5 files changed

+77
-23
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ jupyter_ui_demo/labextension
1212
# Version file is handled by hatchling
1313
jupyter_ui_demo/_version.py
1414

15+
examples/cdn/toolkit.min.js
16+
1517
# Created by https://www.gitignore.io/api/python
1618
# Edit at https://www.gitignore.io/?templates=python
1719

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ A pre-bundled script that contains all APIs needed to use Jupyter UI Toolkit is
8686

8787
The above CDN location points to the latest release of `@jupyter/web-components`. It is advised that when you deploy your site or app, you import the specific version you have developed and tested with.
8888

89+
See the [example](./examples/cdn/index.html) folder for more hints about theming.
90+
8991
## Documentation
9092

9193
Further documentation can be found in the following places:

examples/cdn/index.html

+39-10
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,52 @@
1717
--jp-ui-font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
1818
--jp-ui-font-size1: 13px;
1919
}
20+
21+
body {
22+
background-color: var(--neutral-layer-1);
23+
}
24+
25+
#theme-mode>span {
26+
display: inline-block;
27+
width: 24px;
28+
height: 24px;
29+
background-image: url('https://unpkg.com/@mdi/svg/svg/brightness-6.svg');
30+
background-color: var(--neutral-foreground-rest);
31+
mix-blend-mode: difference;
32+
}
2033
</style>
2134
</head>
22-
<!--
23-
The toolkit supports theming from JupyterLab theme
24-
For that you need to set `data-jp-theme-name` with a theme _name_
25-
and `data-jp-theme-light` with `true` (ligth theme) or `false` (dark theme)
26-
-->
27-
28-
<body data-jp-theme-name="default" data-jp-theme-light="true">
2935

36+
<body data-theme-mode="dark">
3037
<jp-button appearance="accent" onclick="alert('Accent button pressed')">Click me!</jp-button>
3138
<jp-button onclick="alert('Neutral button pressed')">Click me!</jp-button>
39+
<jp-button id="theme-mode">
40+
<span></span>
41+
</jp-button>
3242

3343
<script type="module">
34-
// Inject JupyterLab theme listener
35-
import { addJupyterLabThemeChangeListener } from 'https://unpkg.com/@jupyter/web-components/dist/toolkit.min.js';
36-
addJupyterLabThemeChangeListener();
44+
// Apply JupyterLab theme
45+
import { applyJupyterTheme } from 'https://unpkg.com/@jupyter/web-components/dist/toolkit.min.js';
46+
47+
function switchThemeMode(th) {
48+
const mode = document.body.getAttribute('data-theme-mode');
49+
document.body.setAttribute(
50+
'data-theme-mode',
51+
mode == 'light' ? 'dark' : 'light'
52+
);
53+
document.body.style.removeProperty('--jp-layout-color1');
54+
document.body.style.setProperty(
55+
'--jp-layout-color1',
56+
// Apparent opposite condition as we don't update the mode variable
57+
mode == 'dark' ? '#808080' : 'black'
58+
);
59+
applyJupyterTheme();
60+
}
61+
switchThemeMode();
62+
63+
document.querySelector('#theme-mode').addEventListener('click', () => {
64+
switchThemeMode();
65+
});
3766
</script>
3867
</body>
3968

packages/components/src/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Copyright (c) Jupyter Development Team.
22
// Distributed under the terms of the Modified BSD License.
33

4-
export { addJupyterLabThemeChangeListener } from './utilities/theme/applyTheme';
4+
export {
5+
addJupyterLabThemeChangeListener,
6+
applyJupyterTheme
7+
} from './utilities/theme/applyTheme';
58

69
export * from './color';
710
export * from './design-tokens';

packages/components/src/utilities/theme/applyTheme.ts

+30-12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
parseColor,
88
rgbToHSL
99
} from '@microsoft/fast-colors';
10+
import { isDark } from '@microsoft/fast-components';
1011
import { DesignToken } from '@microsoft/fast-foundation';
1112
import {
1213
Palette,
@@ -28,6 +29,8 @@ import {
2829

2930
const THEME_NAME_BODY_ATTRIBUTE = 'data-jp-theme-name';
3031
const THEME_MODE_BODY_ATTRIBUTE = 'data-jp-theme-light';
32+
// Use to determine the neutral color and possibly if theme is dark
33+
const BASE_LAYOUT_COLOR = '--jp-layout-color1';
3134

3235
/**
3336
* Flag to initialized only one listener
@@ -48,7 +51,7 @@ export function addJupyterLabThemeChangeListener(): void {
4851
function initThemeChangeListener(): void {
4952
const addObserver = () => {
5053
const observer = new MutationObserver(() => {
51-
applyCurrentTheme();
54+
applyJupyterTheme();
5255
});
5356
observer.observe(document.body, {
5457
attributes: true,
@@ -57,7 +60,7 @@ function initThemeChangeListener(): void {
5760
characterData: false
5861
});
5962

60-
applyCurrentTheme();
63+
applyJupyterTheme();
6164
};
6265

6366
if (document.readyState === 'complete') {
@@ -115,7 +118,7 @@ const tokenMappings: { [key: string]: IConverter<any> } = {
115118
converter: intConverter,
116119
token: controlCornerRadius
117120
},
118-
'--jp-layout-color1': {
121+
[BASE_LAYOUT_COLOR]: {
119122
converter: (value: string, isDark: boolean): Palette<Swatch> | null => {
120123
const parsedColor = parseColor(value);
121124
if (parsedColor) {
@@ -173,22 +176,37 @@ const tokenMappings: { [key: string]: IConverter<any> } = {
173176
/**
174177
* Applies the current Jupyter theme to the toolkit components.
175178
*/
176-
function applyCurrentTheme() {
177-
if (!document.body.getAttribute(THEME_NAME_BODY_ATTRIBUTE)) {
178-
return;
179-
}
180-
179+
export function applyJupyterTheme(): void {
181180
// Get all the styles applied to the <body> tag in the webview HTML
182181
// Importantly this includes all the CSS variables associated with the
183182
// current Jupyter theme
184183
const styles = getComputedStyle(document.body);
185184

186185
// Set mode
187-
const isDark =
188-
document.body.getAttribute(THEME_MODE_BODY_ATTRIBUTE) === 'false';
186+
// It will look at the body attribute or try to extrapolate
187+
const themeMode = document.body.getAttribute(THEME_MODE_BODY_ATTRIBUTE);
188+
let isDark_ = false;
189+
if (themeMode) {
190+
isDark_ = themeMode === 'false';
191+
} else {
192+
const layoutColor = styles.getPropertyValue(BASE_LAYOUT_COLOR).toString();
193+
if (layoutColor) {
194+
const parsedColor = parseColor(layoutColor);
195+
if (parsedColor) {
196+
isDark_ = isDark(
197+
SwatchRGB.create(parsedColor.r, parsedColor.g, parsedColor.b)
198+
);
199+
console.debug(
200+
`Theme is ${
201+
isDark_ ? 'dark' : 'light'
202+
} based on '${BASE_LAYOUT_COLOR}' value: ${layoutColor}.`
203+
);
204+
}
205+
}
206+
}
189207
baseLayerLuminance.setValueFor(
190208
document.body,
191-
isDark ? StandardLuminance.DarkMode : StandardLuminance.LightMode
209+
isDark_ ? StandardLuminance.DarkMode : StandardLuminance.LightMode
192210
);
193211

194212
for (const jpTokenName in tokenMappings) {
@@ -198,7 +216,7 @@ function applyCurrentTheme() {
198216
if (document.body && value !== '') {
199217
const parsedValue = (toolkitTokenName.converter ?? ((v: string) => v))(
200218
value.trim(),
201-
isDark
219+
isDark_
202220
);
203221
if (parsedValue !== null) {
204222
toolkitTokenName.token.setValueFor(document.body, parsedValue);

0 commit comments

Comments
 (0)