Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

combine light & dark themes #476

Merged
merged 2 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,29 @@ The path to the output root; defaults to `dist`.

## theme

The theme names, if any; defaults to `auto`. Themes affect the visual appearance of pages by specifying colors and fonts, or by augmenting default styles. The theme option is a convenient shorthand alternative to specifying a [custom stylesheet](#style).
The theme names, if any; defaults to `[light, dark]`. Themes affect the visual appearance of pages by specifying colors and fonts, or by augmenting default styles. The theme option is a convenient shorthand alternative to specifying a [custom stylesheet](#style).

The current built-in themes are:

- `auto` (default) - `light` or `dark` depending on [user preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)
- `auto-alt` - `light-alt` or `dark` depending on [user preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)
- `light` - a white background with dark text
- `light` (default) - a white background with dark text
- `light-alt` - a light gray background with dark text
- `dark` - a dark gray background with light text
- `dark` (default) - a dark gray background with light text
- `dark-alt` - a very dark gray background with light text
- `wide` - a full-width main column

You can combine themes like so:

```js run=false
theme: ["auto-alt", "wide"]
theme: ["light-alt", "dark-alt", "wide"]
```

When combining a light and dark theme, the dark theme will be applied depending on [user preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).

A theme can be applied to an individual page via the [front matter](./markdown.md#front-matter):

```yaml
---
theme: [auto-alt, wide]
theme: [light-alt, dark-alt, wide]
---
```

Expand All @@ -70,8 +70,6 @@ The custom stylesheet should typically import `observablehq:default.css` to buil
}
```

If you build on the `auto` or `auto-alt` themes, note that these themes’ colors are chosen according to the user’s [preferred color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).

The default styles are implemented using CSS custom properties. These properties are designed to be defined by themes or custom stylesheets. The following custom properties are supported:

- `--theme-foreground` - page foreground color, _e.g._ black
Expand Down
2 changes: 1 addition & 1 deletion docs/grid.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
toc: false
theme: [auto-alt, wide]
theme: [light-alt, dark-alt, wide]
---

```js
Expand Down
4 changes: 2 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface Config {
pager: boolean; // defaults to true
footer: string;
toc: TableOfContents;
style: null | Style; // defaults to {theme: ["auto"]}
style: null | Style; // defaults to {theme: ["light", "dark"]}
deploy: null | {workspace: string; project: string};
}

Expand Down Expand Up @@ -70,7 +70,7 @@ async function readPages(root: string): Promise<Page[]> {
const DEFAULT_FOOTER = 'Built with <a href="https://observablehq.com/" target=_blank>Observable</a>';

export async function normalizeConfig(spec: any = {}, defaultRoot = "docs"): Promise<Config> {
let {root = defaultRoot, output = "dist", style, theme = "auto", deploy, footer = DEFAULT_FOOTER} = spec;
let {root = defaultRoot, output = "dist", style, theme = ["light", "dark"], deploy, footer = DEFAULT_FOOTER} = spec;
root = String(root);
output = String(output);
if (style === null) style = null;
Expand Down
56 changes: 39 additions & 17 deletions src/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,56 @@ import {getObservableUiHost} from "./observableApiClient.js";
import {Sourcemap} from "./sourcemap.js";
import {relativeUrl} from "./url.js";

interface Theme {
name: string;
path: string;
light?: boolean;
dark?: boolean;
}

const THEMES: Theme[] = [
{name: "dark", path: getClientPath("./src/style/theme-dark.css"), dark: true},
{name: "dark-alt", path: getClientPath("./src/style/theme-dark-alt.css"), dark: true},
{name: "light", path: getClientPath("./src/style/theme-light.css"), light: true},
{name: "light-alt", path: getClientPath("./src/style/theme-light-alt.css"), light: true},
{name: "wide", path: getClientPath("./src/style/theme-wide.css")}
];

const STYLE_MODULES = {
"observablehq:default.css": getClientPath("./src/style/default.css"),
"observablehq:theme-auto.css": getClientPath("./src/style/theme-auto.css"),
"observablehq:theme-auto-alt.css": getClientPath("./src/style/theme-auto-alt.css"),
"observablehq:theme-dark.css": getClientPath("./src/style/theme-dark.css"),
"observablehq:theme-dark-alt.css": getClientPath("./src/style/theme-dark-alt.css"),
"observablehq:theme-light.css": getClientPath("./src/style/theme-light.css"),
"observablehq:theme-light-alt.css": getClientPath("./src/style/theme-light-alt.css"),
"observablehq:theme-wide.css": getClientPath("./src/style/theme-wide.css")
...Object.fromEntries(THEMES.map(({name, path}) => [`observablehq:theme-${name}.css`, path]))
};

function rewriteInputsNamespace(code: string) {
return code.replace(/\b__ns__\b/g, "inputs-3a86ea");
}

function renderTheme(names: string[]): string {
const lines = ['@import url("observablehq:default.css");'];
let hasLight = false;
let hasDark = false;
for (const name of names) {
const theme = THEMES.find((t) => t.name === name);
if (!theme) throw new Error(`invalid theme: ${theme}`);
lines.push(
`@import url(${JSON.stringify(`observablehq:theme-${theme.name}.css`)})${
theme.dark && !theme.light && hasLight // a dark-only theme preceded by a light theme
? " (prefers-color-scheme: dark)"
: theme.light && !theme.dark && hasDark // a light-only theme preceded by a dark theme
? " (prefers-color-scheme: light)"
: ""
};`
);
if (theme.light) hasLight = true;
if (theme.dark) hasDark = true;
}
return lines.join("\n");
}

export async function bundleStyles({path, theme}: {path?: string; theme?: string[]}): Promise<string> {
const result = await build({
bundle: true,
...(path
? {entryPoints: [path]}
: {
stdin: {
contents: `@import url("observablehq:default.css");\n${theme!
.map((t) => `@import url(${JSON.stringify(`observablehq:theme-${t}.css`)});\n`)
.join("")}`,
loader: "css"
}
}),
...(path ? {entryPoints: [path]} : {stdin: {contents: renderTheme(theme!), loader: "css"}}),
write: false,
alias: STYLE_MODULES
});
Expand Down
2 changes: 0 additions & 2 deletions src/style/theme-auto-alt.css

This file was deleted.

2 changes: 0 additions & 2 deletions src/style/theme-auto.css

This file was deleted.

4 changes: 2 additions & 2 deletions test/config-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("readConfig(undefined, root)", () => {
assert.deepStrictEqual(await readConfig(undefined, "test/input/build/config"), {
root: "test/input/build/config",
output: "dist",
style: {theme: ["auto"]},
style: {theme: ["light", "dark"]},
pages: [
{path: "/index", name: "Index"},
{path: "/one", name: "One<Two"},
Expand All @@ -29,7 +29,7 @@ describe("readConfig(undefined, root)", () => {
assert.deepStrictEqual(await readConfig(undefined, "test/input/build/simple"), {
root: "test/input/build/simple",
output: "dist",
style: {theme: ["auto"]},
style: {theme: ["light", "dark"]},
pages: [{name: "Build test case", path: "/simple"}],
title: undefined,
toc: {label: "Contents", show: true},
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/404/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Page not found</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_observablehq/client.js">
<link rel="modulepreload" href="./_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/archives/tar.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Tar</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_observablehq/client.js">
<link rel="modulepreload" href="./_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/archives/zip.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Zip</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_observablehq/client.js">
<link rel="modulepreload" href="./_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/config/closed/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>A page…</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="../_observablehq/theme-auto.css">
<link rel="preload" as="style" href="../_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="../_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="../_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="../_observablehq/client.js">
<link rel="modulepreload" href="../_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/config/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Index</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_observablehq/client.js">
<link rel="modulepreload" href="./_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/config/one.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>One</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_observablehq/client.js">
<link rel="modulepreload" href="./_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/config/sub/two.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Two</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="../_observablehq/theme-auto.css">
<link rel="preload" as="style" href="../_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="../_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="../_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="../_observablehq/client.js">
<link rel="modulepreload" href="../_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/config/toc-override.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>H1: Section</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_observablehq/client.js">
<link rel="modulepreload" href="./_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/config/toc.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>H1: Section</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_observablehq/client.js">
<link rel="modulepreload" href="./_observablehq/runtime.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/fetches/foo.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Top</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_import/foo/foo.js?sha=ddc538dfc10d83a59458d5893c89191ef3b2c9b1c02ef6da055423f37388ecf4">
<link rel="modulepreload" href="./_observablehq/client.js">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/fetches/top.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Top</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_import/foo/foo.js?sha=ddc538dfc10d83a59458d5893c89191ef3b2c9b1c02ef6da055423f37388ecf4">
<link rel="modulepreload" href="./_import/top.js?sha=a713c9f9e5b3c1e456a16cd100388f7eacfe48ee5fcaf7772f405abe53541cd0">
Expand Down
4 changes: 2 additions & 2 deletions test/output/build/files/files.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="./_observablehq/theme-auto.css">
<link rel="preload" as="style" href="./_observablehq/theme-light,dark.css">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-auto.css">
<link rel="stylesheet" type="text/css" href="./_observablehq/theme-light,dark.css">
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&amp;display=swap" crossorigin>
<link rel="modulepreload" href="./_observablehq/client.js">
<link rel="modulepreload" href="./_observablehq/runtime.js">
Expand Down
Loading