From 031abec4f78864cac157dc1698fea084c0346339 Mon Sep 17 00:00:00 2001 From: Sid Vishnoi <8426945+sidvishnoi@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:43:42 +0530 Subject: [PATCH] feat: dark mode (#3236) --- src/styles/algorithms.css.js | 22 +++++++++++- src/styles/caniuse.css.js | 3 +- src/styles/datatype.css.js | 4 +-- src/styles/dfn-panel.css.js | 9 +++++ src/styles/examples.css.js | 12 ------- src/styles/highlight.css.js | 58 +++++++++++++++++++++++++------- src/styles/issues-notes.css.js | 4 +++ src/styles/mdn-annotation.css.js | 11 +++++- src/styles/respec.css.js | 5 --- src/styles/ui.css.js | 37 +++++++++++--------- src/styles/var.css.js | 6 ++++ src/styles/webidl.css.js | 5 +++ src/type-helper.d.ts | 3 ++ src/w3c/style.js | 27 ++++++++++++++- tests/spec/w3c/style-spec.js | 43 ++++++++++++++++++++++- 15 files changed, 196 insertions(+), 53 deletions(-) diff --git a/src/styles/algorithms.css.js b/src/styles/algorithms.css.js index f33bd48f53..908380d598 100644 --- a/src/styles/algorithms.css.js +++ b/src/styles/algorithms.css.js @@ -4,9 +4,29 @@ const css = String.raw; // Prettier ignore only to keep code indented from level 0. // prettier-ignore export default css` +:root { + --assertion-border: #aaa; + --assertion-bg: #eee; + --assertion-text: black; +} + .assert { - background: #eee; border-left: 0.5em solid #aaa; padding: 0.3em; + border-color: #aaa; + border-color: var(--assertion-border); + background: #eee; + background: var(--assertion-bg); + color: black; + color: var(--assertion-text); +} + +/* There's no way to adapt this to "manual" theme toggle yet. */ +@media (prefers-color-scheme: dark) { + :root { + --assertion-border: #444; + --assertion-bg: var(--borderedblock-bg); + --assertion-text: var(--text); + } } `; diff --git a/src/styles/caniuse.css.js b/src/styles/caniuse.css.js index 3666ff670b..5296bf49c7 100644 --- a/src/styles/caniuse.css.js +++ b/src/styles/caniuse.css.js @@ -40,7 +40,7 @@ export default css` } .caniuse-type span { - background-color: white; + background-color: var(--bg, white); padding: 0 0.4em; } @@ -73,6 +73,7 @@ export default css` img.caniuse-browser { filter: drop-shadow(0px 0px .1cm #666666); + background: transparent; } .caniuse-cell span.browser-version { diff --git a/src/styles/datatype.css.js b/src/styles/datatype.css.js index 36186134c6..c6acc02678 100644 --- a/src/styles/datatype.css.js +++ b/src/styles/datatype.css.js @@ -25,14 +25,14 @@ var[data-type]::before { border-width: 4px 6px 0 6px; border-style: solid; border-color: transparent; - border-top-color: #000; + border-top-color: #222; } /* actual text */ var[data-type]::after { content: attr(data-type); transform: translateX(-50%) translateY(-100%); - background: #000; + background: #222; text-align: center; /* additional styling */ font-family: "Dank Mono", "Fira Code", monospace; diff --git a/src/styles/dfn-panel.css.js b/src/styles/dfn-panel.css.js index 7055557c95..f2beb9389e 100644 --- a/src/styles/dfn-panel.css.js +++ b/src/styles/dfn-panel.css.js @@ -22,9 +22,13 @@ dfn { font-family: "Helvetica Neue", sans-serif; font-size: small; background: #fff; + background: var(--indextable-hover-bg, #fff); color: black; + color: var(--text, black); box-shadow: 0 1em 3em -0.4em rgba(0, 0, 0, 0.3), 0 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1em 3em -0.4em var(--tocsidebar-shadow, rgba(0, 0, 0, 0.3)), + 0 0 1px 1px var(--tocsidebar-shadow, rgba(0, 0, 0, 0.05)); border-radius: 2px; } /* Triangle/caret */ @@ -39,10 +43,13 @@ dfn { border: 10px solid transparent; border-top: 0; border-bottom: 10px solid #fff; + border-bottom-color: var(--indextable-hover-bg, #fff); top: 0; } .dfn-panel:not(.docked) > .caret::before { border-bottom: 9px solid #a2a9b1; + /* TODO: need slightly darker shade */ + border-bottom-color: var(--indextable-hover-bg, #a2a9b1); } .dfn-panel * { @@ -52,11 +59,13 @@ dfn { .dfn-panel b { display: block; color: #000; + color: var(--text, #000); margin-top: 0.25em; } .dfn-panel ul a[href] { color: #333; + color: var(--text, #333); } .dfn-panel > div { diff --git a/src/styles/examples.css.js b/src/styles/examples.css.js index 07ecd2e444..0206f1498b 100644 --- a/src/styles/examples.css.js +++ b/src/styles/examples.css.js @@ -24,21 +24,9 @@ div.illegal-example p { color: black; } -:is(aside,div).example { - border-left-width: 0.5em; - border-left-style: solid; - border-color: #e0cb52; - background: #fcfaee; -} - aside.example div.example { border-left-width: 0.1em; border-color: #999; background: #fff; } - - -.example pre { - background-color: rgba(0, 0, 0, 0.03); -} `; diff --git a/src/styles/highlight.css.js b/src/styles/highlight.css.js index 2a5cb3c5e9..3f52eb7e71 100644 --- a/src/styles/highlight.css.js +++ b/src/styles/highlight.css.js @@ -1,18 +1,7 @@ /* -Adapted from Atom One Light by Daniel Gamage for ReSpec, with better color contrast +One Light for ReSpec, with better color contrast +Adapted from Atom One Light by Daniel Gamage (https://github.com/highlightjs/highlight.js/blob/c0b6ddbaaf7/src/styles/atom-one-light.css> Original One Light Syntax theme from https://github.com/atom/one-light-syntax -base: #fafafa -mono-1: #383a42 -mono-2: #686b77 -mono-3: #a0a1a7 -hue-1: #0184bb -hue-2: #4078f2 -hue-3: #a626a4 -hue-4: #50a14f -hue-5: #e45649 -hue-5-2: #c91243 -hue-6: #986801 -hue-6-2: #c18401 */ const css = String.raw; @@ -20,17 +9,53 @@ const css = String.raw; // Prettier ignore only to keep code indented from level 0. // prettier-ignore export default css` +.hljs { + --base: #fafafa; + --mono-1: #383a42; + --mono-2: #686b77; + --mono-3: #717277; + --hue-1: #0b76c5; + --hue-2: #336ae3; + --hue-3: #a626a4; + --hue-4: #42803c; + --hue-5: #ca4706; + --hue-5-2: #c91243; + --hue-6: #986801; + --hue-6-2: #9a6a01; +} + +/* There's no way to adapt this to "manual" theme toggle yet. */ +@media (prefers-color-scheme: dark) { + .hljs { + --base: #282c34; + --mono-1: #abb2bf; + --mono-2: #818896; + --mono-3: #5c6370; + --hue-1: #56b6c2; + --hue-2: #61aeee; + --hue-3: #c678dd; + --hue-4: #98c379; + --hue-5: #e06c75; + --hue-5-2: #be5046; + --hue-6: #d19a66; + --hue-6-2: #e6c07b; + } +} + .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #383a42; + color: var(--mono-1, #383a42); background: #fafafa; + background: var(--base, #fafafa); } .hljs-comment, .hljs-quote { color: #717277; + color: var(--mono-3, #717277); font-style: italic; } @@ -38,6 +63,7 @@ export default css` .hljs-keyword, .hljs-formula { color: #a626a4; + color: var(--hue-3, #a626a4); } .hljs-section, @@ -46,11 +72,13 @@ export default css` .hljs-deletion, .hljs-subst { color: #ca4706; + color: var(--hue-5, #ca4706); font-weight: bold; } .hljs-literal { color: #0b76c5; + color: var(--hue-1, #0b76c5); } .hljs-string, @@ -59,11 +87,13 @@ export default css` .hljs-attribute, .hljs-meta-string { color: #42803c; + color: var(--hue-4, #42803c); } .hljs-built_in, .hljs-class .hljs-title { color: #9a6a01; + color: var(--hue-6-2, #9a6a01); } .hljs-attr, @@ -75,6 +105,7 @@ export default css` .hljs-selector-pseudo, .hljs-number { color: #986801; + color: var(--hue-6, #986801); } .hljs-symbol, @@ -84,6 +115,7 @@ export default css` .hljs-selector-id, .hljs-title { color: #336ae3; + color: var(--hue-2, #336ae3); } .hljs-emphasis { diff --git a/src/styles/issues-notes.css.js b/src/styles/issues-notes.css.js index 5296c04288..179fd55635 100644 --- a/src/styles/issues-notes.css.js +++ b/src/styles/issues-notes.css.js @@ -31,9 +31,13 @@ span.warning { .warning { border-color: #f11; + border-color: var(--warning-border, #f11); border-width: 0.2em; border-style: solid; background: #fbe9e9; + background: var(--warning-bg, #fbe9e9); + color: black; + color: var(--text, black); } .warning-title:before { diff --git a/src/styles/mdn-annotation.css.js b/src/styles/mdn-annotation.css.js index 9052dd3b85..2b19a1db68 100644 --- a/src/styles/mdn-annotation.css.js +++ b/src/styles/mdn-annotation.css.js @@ -25,8 +25,15 @@ export default css` min-width: 25ch; max-width: 32ch; background: #fff; - box-shadow: 0 1em 3em -0.4em rgba(0, 0, 0, 0.3), + background: var(--indextable-hover-bg, #fff); + color: black; + color: var(--indextable-hover-text, black); + box-shadow: + 0 1em 3em -0.4em rgba(0, 0, 0, 0.3), 0 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: + 0 1em 3em -0.4em var(--tocsidebar-shadow, rgba(0, 0, 0, 0.3)), + 0 0 1px 1px var(--tocsidebar-shadow, rgba(0, 0, 0, 0.05)); border-radius: 2px; z-index: 11; margin-bottom: 0.4em; @@ -41,7 +48,9 @@ export default css` .mdn summary span { font-family: zillaslab, Palatino, "Palatino Linotype", serif; color: #fff; + color: var(--bg, #fff); background-color: #000; + background-color: var(--text, #000); display: inline-block; padding: 3px; } diff --git a/src/styles/respec.css.js b/src/styles/respec.css.js index 1b2337db7e..5db7128e31 100644 --- a/src/styles/respec.css.js +++ b/src/styles/respec.css.js @@ -86,11 +86,6 @@ a[href].orcid > svg { text-decoration: none; } -a .secno, -a .figno { - color: #000; -} - ul.tof, ol.tof { list-style: none outside none; diff --git a/src/styles/ui.css.js b/src/styles/ui.css.js index b3312b8b28..c7b2658c5e 100644 --- a/src/styles/ui.css.js +++ b/src/styles/ui.css.js @@ -30,9 +30,12 @@ export default css` .respec-info-button { height: 2.4em; background: #fff; + background: var(--bg, #fff); color: rgb(120, 120, 120); + color: var(--tocnav-normal-text, rgb(120, 120, 120)); border: 1px solid #ccc; box-shadow: 1px 1px 8px 0 rgba(100, 100, 100, 0.5); + box-shadow: 1px 1px 8px 0 var(--tocsidebar-shadow, rgba(100, 100, 100, 0.5)); padding: 0.2em 0em; } @@ -141,7 +144,8 @@ export default css` margin: 0; padding: 0; font-family: sans-serif; - background: #fff; + background: var(--bg, #fff); + color: var(--text, black); box-shadow: 1px 1px 8px 0 rgba(100, 100, 100, 0.5); width: 200px; display: none; @@ -169,8 +173,8 @@ export default css` .respec-save-button:link { padding-top: 16px; - color: rgb(240, 240, 240); - background: rgb(42, 90, 168); + color: var(--def-text, white); + background: var(--def-bg, rgb(42, 90, 168)); justify-self: stretch; height: 1cm; text-decoration: none; @@ -181,8 +185,8 @@ export default css` } .respec-save-button:link:hover { - color: white; - background: rgb(42, 90, 168); + color: var(--def-text, white); + background: var(--defrow-border, rgb(42, 90, 168)); padding: 0; margin: 0; border: 0; @@ -190,7 +194,8 @@ export default css` } .respec-save-button:link:focus { - background: #193766; + background: var(--tocnav-active-bg, #193766); + color: var(--tocnav-active-text, black); } #respec-ui button:focus, @@ -297,8 +302,10 @@ export default css` position: fixed; z-index: 11000; top: 10%; - background: #fff; + background: var(--bg, #fff); + color: var(--text, black); border: 5px solid #666; + border-color: var(--tocsidebar-shadow, #666); min-width: 20%; padding: 0; max-height: 80%; @@ -313,19 +320,16 @@ export default css` .respec-modal h3 { margin: 0; padding: 0.2em; + left: 0 !important; text-align: center; - color: black; - background: linear-gradient( - to bottom, - rgba(238, 238, 238, 1) 0%, - rgba(238, 238, 238, 1) 50%, - rgba(204, 204, 204, 1) 100% - ); + background: var(--tocsidebar-shadow, #ddd); + color: var(--text, black); font-size: 1em; } #respec-menu button.respec-option { - background: white; + background: var(--bg, white); + color: var(--text, black); border: none; width: 100%; text-align: left; @@ -334,7 +338,8 @@ export default css` } #respec-menu button.respec-option:hover { - background-color: #eeeeee; + background-color: var(--tocnav-hover-bg, #eee); + color: var(--tocnav-hover-text, black); } .respec-cmd-icon { diff --git a/src/styles/var.css.js b/src/styles/var.css.js index f3e8c90d94..71b838f922 100644 --- a/src/styles/var.css.js +++ b/src/styles/var.css.js @@ -14,6 +14,12 @@ var.respec-hl { box-shadow: 0 0 0px 2px var(--bg-color); } +@media (prefers-color-scheme: dark) { + var.respec-hl { + filter: saturate(0.9) brightness(0.9) + } +} + /* highlight colors https://github.com/w3c/tr-design/issues/152 */ diff --git a/src/styles/webidl.css.js b/src/styles/webidl.css.js index f1393e0602..f4db208983 100644 --- a/src/styles/webidl.css.js +++ b/src/styles/webidl.css.js @@ -11,6 +11,7 @@ pre.idl { pre.idl > code { color: black; + color: var(--text, black); } @media print { @@ -23,7 +24,10 @@ pre.idl > code { display: block; width: 150px; background: #8ccbf2; + background: var(--def-border, #8ccbf2); color: #fff; + /* TODO: need a better color here */ + color: var(--defrow-border, #fff); font-family: sans-serif; font-weight: bold; margin: -1em 0 1em -1em; @@ -35,6 +39,7 @@ pre.idl > code { margin-left: 0.3cm; text-decoration: none; border-bottom: none; + color: inherit; } .idlID { diff --git a/src/type-helper.d.ts b/src/type-helper.d.ts index 821a927d5f..dee96b79b7 100644 --- a/src/type-helper.d.ts +++ b/src/type-helper.d.ts @@ -134,6 +134,9 @@ interface Conf { noToc: boolean; /** Disables injecting ReSpec styles */ noReSpecCSS?: boolean; + /** Inject dark mode styles */ + darkMode?: boolean; + /** Indicates whether the document is a preview */ isPreview?: boolean; /** The pull request number, if applicable */ diff --git a/src/w3c/style.js b/src/w3c/style.js index 413c41badc..7407b49889 100644 --- a/src/w3c/style.js +++ b/src/w3c/style.js @@ -42,7 +42,12 @@ function createResourceHints() { }, { hint: "preload", // all specs include on base.css. - href: "https://www.w3.org/StyleSheets/TR/2021/base.css", + href: getStyleUrl("base.css").href, + as: "style", + }, + { + hint: "preload", + href: getStyleUrl("dark.css").href, as: "style", }, { @@ -108,6 +113,26 @@ export function run(conf) { ); // Make sure the W3C stylesheet is the last stylesheet, as required by W3C Pub Rules. sub("beforesave", styleMover(finalStyleURL)); + + if (conf.darkMode) { + const darkModeStyleUrl = getStyleUrl("dark.css"); + document.head.appendChild( + html`` + ); + document.head.appendChild( + html`` + ); + // As required by W3C Pub Rules. + sub("beforesave", styleMover(darkModeStyleUrl)); + } } /** @param {Conf} conf */ diff --git a/tests/spec/w3c/style-spec.js b/tests/spec/w3c/style-spec.js index eeb798597a..6f412b216a 100644 --- a/tests/spec/w3c/style-spec.js +++ b/tests/spec/w3c/style-spec.js @@ -1,6 +1,11 @@ "use strict"; -import { flushIframes, makeRSDoc, makeStandardOps } from "../SpecHelper.js"; +import { + flushIframes, + getExportedDoc, + makeRSDoc, + makeStandardOps, +} from "../SpecHelper.js"; const statuses = [ { @@ -191,6 +196,42 @@ describe("W3C - Style", () => { }); } + it("should add W3C stylesheet at the end", async () => { + const ops = makeStandardOps({}); + const doc = await getExportedDoc(await makeRSDoc(ops)); + const url = "https://www.w3.org/StyleSheets/TR/2021/base"; + const elem = doc.querySelector(`link[href^='${url}'][rel="stylesheet"]`); + expect(elem).toBeTruthy(); + expect(elem.nextElementSibling).toBeFalsy(); + }); + + it("should add dark mode stylesheet", async () => { + const ops = makeStandardOps({ darkMode: true }); + const doc = await makeRSDoc(ops); + const url = "https://www.w3.org/StyleSheets/TR/2021/dark.css"; + const elem = doc.querySelector(`link[href^='${url}'][rel="stylesheet"]`); + expect(elem).toBeTruthy(); + expect(elem.href).toBe(url); + expect(elem.getAttribute("media")).toBe("(prefers-color-scheme: dark)"); + }); + + it("should add W3C darkmode stylesheet at the end", async () => { + const ops = makeStandardOps({ darkMode: true }); + const doc = await getExportedDoc(await makeRSDoc(ops)); + const linkBase = doc.querySelector( + `link[href^='https://www.w3.org/StyleSheets/TR/2021/base'][rel="stylesheet"]` + ); + expect(linkBase).toBeTruthy(); + expect(linkBase.nextElementSibling).toBeTruthy(); + + const linkDarkMode = doc.querySelector( + `link[href^='https://www.w3.org/StyleSheets/TR/2021/dark.css'][rel="stylesheet"]` + ); + expect(linkDarkMode).toBeTruthy(); + expect(linkDarkMode.nextElementSibling).toBeFalsy(); + expect(linkBase.nextElementSibling).toBe(linkDarkMode); + }); + it("shouldn't include fixup.js when noToc is set", async () => { const ops = makeStandardOps(); const newProps = {