diff --git a/docs/css/dottydoc.css b/docs/css/dottydoc.css index d2520888120a..c39c18991417 100644 --- a/docs/css/dottydoc.css +++ b/docs/css/dottydoc.css @@ -43,7 +43,7 @@ ul.post-list { /* headings anchors */ a.anchor { - color: white; + color: transparent; margin-left: -23px; padding-right: 3px; transition: color .4s ease-out; diff --git a/scaladoc/resources/dotty_res/scripts/theme.js b/scaladoc/resources/dotty_res/scripts/theme.js new file mode 100644 index 000000000000..b5cf46b1a221 --- /dev/null +++ b/scaladoc/resources/dotty_res/scripts/theme.js @@ -0,0 +1,48 @@ +;(function () { + const supportsLocalStorage = (() => { + try { + localStorage.setItem('test', 'test'); + localStorage.removeItem('test'); + return true; + } catch (e) { + return false; + } + })(); + + const settingKey = "use-dark-theme"; + + function toggleDarkTheme(isDark) { + currentlyDark = isDark + // this triggers the `:root.theme-dark` rule from scalastyle.css, + // which changes the values of a bunch of CSS color variables + document.documentElement.classList.toggle("theme-dark", isDark); + supportsLocalStorage && localStorage.setItem(settingKey, isDark); + } + + /* Infer a dark/light theme preference from the user's system */ + const colorSchemePrefMql = window.matchMedia("(prefers-color-scheme: dark)"); + + /* This needs to happen ASAP so we don't get a FOUC of bright colors before the dark theme is applied */ + const initiallyDark = (() => { + const storedSetting = supportsLocalStorage && localStorage.getItem(settingKey); + return (storedSetting === null) ? colorSchemePrefMql.matches : storedSetting === "true"; + })(); + let currentlyDark = initiallyDark; + toggleDarkTheme(initiallyDark); + + /* Wait for the DOM to be loaded before we try to attach event listeners to things in the DOM */ + window.addEventListener("DOMContentLoaded", () => { + const themeToggler = document.querySelector('#theme-toggle input'); + themeToggler.checked = !currentlyDark; + themeToggler.addEventListener("change", e => { + toggleDarkTheme(!e.target.checked); + }); + + /* Auto-swap the dark/light theme if the user changes it in their system */ + colorSchemePrefMql.addEventListener('change', e => { + const preferDark = e.matches; + themeToggler.checked = !preferDark; + toggleDarkTheme(preferDark); + }); + }); +})(); diff --git a/scaladoc/resources/dotty_res/scripts/ux.js b/scaladoc/resources/dotty_res/scripts/ux.js index 63d284d2d93e..b491d2f115fb 100644 --- a/scaladoc/resources/dotty_res/scripts/ux.js +++ b/scaladoc/resources/dotty_res/scripts/ux.js @@ -39,9 +39,14 @@ window.addEventListener("DOMContentLoaded", () => { }) if (location.hash) { - var selected = document.getElementById(location.hash.substring(1)); - if (selected){ - selected.classList.toggle("expand"); + var target = location.hash.substring(1); + // setting the 'expand' class on the top-level container causes undesireable styles + // to apply to the top-level docs, so we avoid this logic for that element. + if (target != 'container') { + var selected = document.getElementById(location.hash.substring(1)); + if (selected) { + selected.classList.toggle("expand"); + } } } diff --git a/scaladoc/resources/dotty_res/styles/filter-bar.css b/scaladoc/resources/dotty_res/styles/filter-bar.css index f8ed5eb9d94d..5a73a87e673d 100644 --- a/scaladoc/resources/dotty_res/styles/filter-bar.css +++ b/scaladoc/resources/dotty_res/styles/filter-bar.css @@ -1,6 +1,6 @@ .documentableFilter { padding: 24px 12px; - background-color: var(--leftbar-bg); + background-color: var(--code-bg); } .documentableFilter.active .filterToggleButton svg { @@ -26,13 +26,13 @@ } .filterToggleButton svg { - fill: var(--code-bg); + fill: var(--body-fg); transition: fill 0.1s ease-in, transform 0.1s ease-in-out; } .filterToggleButton:hover svg, .filterToggleButton:focus svg { - fill: var(--active-tab-color); + fill: var(--active-fg); } .filterableInput { @@ -41,7 +41,7 @@ outline: 0; border: 0; border-radius: 3px; - background-color: var(--code-bg); + background-color: var(--body-bg); } .filterLowerContainer { @@ -50,12 +50,11 @@ } .filterGroup { - display: flex; margin-bottom: 16px; } .filterList { - margin-left: 10px; + margin: 0.5em; } .filterButtonItem { @@ -66,12 +65,12 @@ outline: 0; border: 0; border-radius: 3px; - color: var(--leftbar-bg); - background-color: var(--code-bg); + color: var(--inactive-fg); + background-color: var(--inactive-bg); font-size: 12px; font-weight: 700; cursor: pointer; - border-bottom: 2px solid var(--inactive-fg); + border-bottom: 2px solid var(--inactive-bg-shadow); transition: all 0.1s ease-in; } @@ -81,9 +80,9 @@ } .filterButtonItem.active { - color: var(--code-bg); - border-bottom-color: var(--link-fg); - background-color: var(--active-tab-color); + color: var(--active-fg); + border-bottom-color: var(--active-bg-shadow); + background-color: var(--active-bg); } .filterButtonItem.visible { @@ -91,17 +90,19 @@ } .groupTitle { - min-width: 98px; - margin-top: 4px; + margin-bottom: 4px; font-weight: 700; - font-size: 14px; - color: var(--code-bg); + color: var(--body-fg); +} +.groupTitle > span { + display: inline-block; + vertical-align: baseline; } .groupButtonsContainer { - display: flex; - align-items: center; - margin-top: 4px; + display: inline-block; + vertical-align: baseline; + margin-left: 1em; } .selectAll { @@ -114,8 +115,8 @@ border: 0; background-color: transparent; padding: 0; - color: var(--code-bg); - font-size: 8px; + color: var(--active-fg); + font-size: 0.7em; cursor: pointer; transition: all 0.1s ease-in; } @@ -123,7 +124,7 @@ .selectAll { padding: 4px; border-radius: 2px; - background-color: var(--active-tab-color); + background-color: var(--active-bg); } .selectAll:hover, @@ -133,5 +134,5 @@ .deselectAll:hover, .deselectAll:focus { - color: var(--active-tab-color); + color: var(--active-bg); } diff --git a/scaladoc/resources/dotty_res/styles/nord-light.css b/scaladoc/resources/dotty_res/styles/nord-light.css index 71eac33c4930..9d1604dd0364 100644 --- a/scaladoc/resources/dotty_res/styles/nord-light.css +++ b/scaladoc/resources/dotty_res/styles/nord-light.css @@ -1,14 +1,37 @@ /* Theme inspired by nordtheme. The colors have been darkened to work on light backgrounds. */ +:root { + --hljs-bg: var(--code-bg); + --hljs-fg: var(--code-fg); + --hljs-comment: #90A1C1; + --hljs-doctag: #4B6B92; + --hljs-meta: hsl(40, 100%, 40%); + --hljs-subst: hsl(40, 100%, 40%); + --hljs-title: hsl(193, 60%, 42%); + --hljs-type: hsl(179, 61%, 30%); + --hljs-keyword: hsl(213, 60%, 45%); + --hljs-string: hsl(92, 46%, 43%); + --hljs-literal: hsl(311, 30%, 47%); +} +:root.theme-dark { + --hljs-meta: hsl(40, 100%, 49%); + --hljs-subst: hsl(40, 100%, 49%); + --hljs-title: hsl(193, 60%, 58%); + --hljs-keyword: hsl(213, 60%, 60%); + --hljs-type: hsl(179, 61%, 45%); + --hljs-string: hsl(92, 46%, 68%); + --hljs-literal: hsl(311, 30%, 62%); +} + pre, .hljs { - background: #F4F5FA; - color: #4C566A; + background: var(--hljs-bg); + color: var(--code-fg); } .hljs-comment { - color: #90A1C1; + color: var(--hljs-comment); } .hljs-doctag { - color: #4B6B92; + color: var(--hljs-doctag); font-weight: 500; } .hljs-emphasis { @@ -19,26 +42,26 @@ pre, .hljs { } .hljs-meta { - color: #F9A600; + color: var(--hljs-meta); font-weight: 500; } .hljs-subst { - color: #F9A600; + color: var(--hljs-subst); } .hljs-title { - color: #2B8FAC; + color: var(--hljs-title); font-weight: 500; } .hljs-type { - color: #1E7C7A; + color: var(--hljs-type); } .hljs-keyword { - color: #2E6BB8; + color: var(--hljs-keyword); font-weight: 500; } .hljs-string { - color: #6AA13B; + color: var(--hljs-string); } .hljs-built_in, .hljs-number, .hljs-literal { - color: #9D5490; + color: var(--hljs-literal); } diff --git a/scaladoc/resources/dotty_res/styles/scalastyle.css b/scaladoc/resources/dotty_res/styles/scalastyle.css index 25b4c26f7034..77efb2c7541d 100644 --- a/scaladoc/resources/dotty_res/styles/scalastyle.css +++ b/scaladoc/resources/dotty_res/styles/scalastyle.css @@ -5,28 +5,38 @@ --border-light: #DADFE6; --border-medium: #abc; - --body-bg: #f0f3f6; - --code-bg: #F4F5FA; - --documentable-bg: #FFFFFF; - --symbol-fg: #333; + --body-bg: #eee; + --body-fg: #333; + --title-fg: hsl(200, 80%, 30%); + + --active-bg: var(--leftbar-current-bg); + --active-bg-shadow: hsl(200, 38%, 50%); + --active-fg: var(--body-fg); + + --inactive-bg: #bbb; + --inactive-bg-shadow: var(--inactive-fg); + --inactive-fg: #777; + + --code-bg: hsl(200, 10%, 90%); + --code-fg: #4C566A; + --symbol-fg: var(--body-fg); + --documentable-bg: var(--code-bg); + --link-fg: #00607D; --link-hover-fg: #00A0D0; - --inactive-fg: #777; - --title-fg: #00485E; - --link-sig-fd: #2da0d1; - --link-sig-hover-fd: #7c99a5; + --link-sig-fg: var(--link-fg); - --leftbar-bg: #003048; - --leftbar-fg: #fff; - --leftbar-current-bg: #0090BB; + --leftbar-bg: hsl(200, 65%, 75%); + --leftbar-fg: #333; + --leftbar-current-bg: hsl(200, 80%, 65%); --leftbar-current-fg: #fff; - --leftbar-hover-bg: #00485E; - --leftbar-hover-fg: #fff; - --logo-fg: var(--leftbar-fg); + --leftbar-hover-bg: hsl(200, 65%, 65%); + --leftbar-hover-fg: #333; + + --footer-bg: #FFF; --icon-color: #00485E; - --active-tab-color: #00A0D0; --selected-fg: #00303E; --selected-bg: #BFE7F3; @@ -41,13 +51,56 @@ --content-padding: 24px 42px; --footer-height: 42px; } +:root.theme-dark { + /* Color Settings */ + --border-light: #DADFE6; + --border-medium: #abc; + + --body-bg: hsl(200, 100%, 3%); + --body-fg: #CCC; + --title-fg: hsl(200, 50%, 80%); + + --active-bg: hsl(200, 80%, 25%); + --active-bg-shadow: hsl(200, 38%, 50%); + --active-fg: var(--body-fg); + + --inactive-bg: #444; + --inactive-bg-shadow: var(--inactive-fg); + --inactive-fg: #777; + + --code-bg: hsl(200, 30%, 10%); + --code-fg: #bfbfbf; + --symbol-fg: var(--body-fg); + --link-fg: hsl(200, 100%, 70%); + --link-hover-fg: #00A0D0; + + --link-sig-fg: #2da0d1; + + --leftbar-bg: hsl(200, 100%, 14%); + --leftbar-fg: #CCC; + --leftbar-current-bg: hsl(200, 100%, 35%); + --leftbar-current-fg: #FFF; + --leftbar-hover-bg: hsl(200, 80%, 25%); + --leftbar-hover-fg: #CCC; + + --footer-bg: var(--body-bg); + --footer-fg: var(--body-fg); + + --icon-color: #00485E; + --selected-fg: #00303E; + --selected-bg: #BFE7F3; + +} body { margin: 0; padding: 0; font-family: "Lato", sans-serif; font-size: 16px; - background-color: var(--body-bg); + background: var(--body-bg); +} +body, button, input { + color: var(--body-fg); } /* Page layout */ @@ -271,6 +324,7 @@ th { #sideMenu2 a.selected { background: var(--leftbar-current-bg); + color: var(--leftbar-current-fg); font-weight: bold; } @@ -328,7 +382,7 @@ span.ar::before { .section-tab[data-active=""] { color: unset; font-weight: bold; - border-bottom: 2px solid var(--active-tab-color); + border-bottom: 2px solid var(--active-bg); } .tabs-section-body > :not([data-active]) { display: none; @@ -486,6 +540,8 @@ Same solution is already used in Dokka. /* Footer */ footer { + background: var(--footer-bg); + color: var(--footer-fg); display: flex; bottom: 0px; align-items: center; @@ -496,8 +552,12 @@ footer { min-height: var(--footer-height); border-top: 1px solid var(--border-light); } -footer span.go-to-top-icon { - background-color: white; +.theme-dark footer img { + /* "Poor man's dark mode" for images. + * This works great with black images, + * and just-okay with colored images. + */ + filter: invert(100%) hue-rotate(180deg); } footer > span:first-child { margin-left: 24px; @@ -517,6 +577,66 @@ footer .pull-right { margin-left: auto; } +/* Theme Toggle */ +.switch { + /* The switch - the box around the slider */ + position: relative; + display: inline-block; + width: 60px; + min-width: 60px; + height: 34px; + margin-bottom: 0; +} +.switch input { + /* Hide default HTML checkbox */ + opacity: 0; + width: 0; + height: 0; +} +.switch .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 34px; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} +.switch .slider:before { + position: absolute; + content: "🌘"; + height: 40px; + width: 40px; + line-height:40px; + font-size:20px; + text-align: center; + left: 0px; + bottom: 4px; + top: 0; + bottom: 0; + border-radius: 50%; + margin: auto 0; + -webkit-transition: 0.4s; + transition: 0.4s; + box-shadow: 0 0px 15px #2020203d; + background: #555; +} +.switch input:checked + .slider { + background-color: hsl(200, 80%, 65%); /* --active-bg, but not affected by the theme */ +} +.switch input:focus + .slider { + box-shadow: 0 0 1px #2196f3; +} +.switch input:checked + .slider:before { + content: "🌞"; + -webkit-transform: translateX(24px); + -ms-transform: translateX(24px); + transform: translateX(24px); + background: white; +} .documentableElement .modifiers { display: table-cell; @@ -544,7 +664,7 @@ footer .pull-right { } .other-modifiers a, .other-modifiers a:visited, .other-modifiers span[data-unresolved-link] { - color: var(--link-sig-fd); + color: var(--link-sig-fg); } .documentableElement.expand .modifiers { @@ -553,13 +673,13 @@ footer .pull-right { } .documentableElement .signature { - color: #5a5a5a; + color: var(--code-fg); display: table-cell; white-space: pre-wrap; } .signature a, .signature a:visited, .signature span[data-unresolved-link] { - color: var(--link-sig-fd); + color: var(--link-sig-fg); } .expand .signature { @@ -576,7 +696,8 @@ footer .pull-right { font-weight: 500; font-size: 12px; background: var(--documentable-bg); - border: 0.25em solid var(--body-bg); + border-left: 0.25em solid transparent; + margin: 0.25em; } .documentableElement>div { @@ -611,11 +732,11 @@ footer .pull-right { .documentableElement:hover { cursor: pointer; - border-left: 0.25em solid var(--leftbar-bg); + border-left-color: var(--active-bg); } .expand.documentableElement { - border-left: 0.25em solid var(--leftbar-bg); + border-left-color: var(--active-bg); } .documentableElement .annotations { color: gray; @@ -658,7 +779,7 @@ footer .pull-right { .tabs .names .tab.selected { color: unset; font-weight: bold; - border-bottom: 2px solid var(--active-tab-color); + border-bottom: 2px solid var(--active-bg); } .tabs .names { @@ -691,6 +812,9 @@ footer .pull-right { width: 5.7em; color:transparent; } +.theme-dark .micon { + filter: brightness(120%); +} .micon.cl { content: url("../images/class.svg") @@ -753,10 +877,6 @@ footer .pull-right { margin-right: 5%; } -footer { - color: grey; -} - footer .socials { margin-left: 10px; margin-right: 10px; @@ -861,10 +981,6 @@ footer .socials { } } -footer { - background-color: white; -} - /* The container