The Trans Directory
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

a11y(darkmode): use a button for the theme toggle (#1335)

* Use a `<button>` for theme toggle

* docs: Adds back Xinyang's cs garden to showcase (#1323)

adding back my garden which was deleted from the cleanup showcase

* feat(toc,explorer): add accessibility for toggle (#1327)

* Restore focus highlight on explorer toggle button.

Remove `unset: all` declaration causing `outline`
property to be unset. This allows the default
browser focus highlight to be shown.

* Fix semantics of expandable sections (explorer, toc).

This adds the appropriate aria attributes for the [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-image-description/#javascriptandcsssourcecode) and uses `visibility: hidden` to remove the hidden elements from the focus order without disrupting the animations. Further work is needed on the tree view nodes.

* Run prettier for SCSS files.

* feat: custom global latex macros (closes #1325)

* chore: ts fixes

* docs: recommend at least node 20 in gh

* fix: unmemoize explorer on rebuild (closes #1077)

* fix: pass buildId to worker

* Fix theme button DOM hierarchy and styles

* Restore functionality of theme button

* `aria-label` on theme svgs so their accessible labels are included in button content

---------

Co-authored-by: Xinyang Yu <47915643+xy-241@users.noreply.github.com>
Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>

authored by

Andrew
Xinyang Yu
Jacky Zhao
and committed by
GitHub
4c9e8601 46b63b68

+48 -56
+32 -35
quartz/components/Darkmode.tsx
··· 9 9 10 10 const Darkmode: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => { 11 11 return ( 12 - <div class={classNames(displayClass, "darkmode")}> 13 - <input class="toggle" id="darkmode-toggle" type="checkbox" tabIndex={-1} /> 14 - <label id="toggle-label-light" for="darkmode-toggle" tabIndex={-1}> 15 - <svg 16 - xmlns="http://www.w3.org/2000/svg" 17 - xmlnsXlink="http://www.w3.org/1999/xlink" 18 - version="1.1" 19 - id="dayIcon" 20 - x="0px" 21 - y="0px" 22 - viewBox="0 0 35 35" 23 - style="enable-background:new 0 0 35 35" 24 - xmlSpace="preserve" 25 - > 26 - <title>{i18n(cfg.locale).components.themeToggle.darkMode}</title> 27 - <path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path> 28 - </svg> 29 - </label> 30 - <label id="toggle-label-dark" for="darkmode-toggle" tabIndex={-1}> 31 - <svg 32 - xmlns="http://www.w3.org/2000/svg" 33 - xmlnsXlink="http://www.w3.org/1999/xlink" 34 - version="1.1" 35 - id="nightIcon" 36 - x="0px" 37 - y="0px" 38 - viewBox="0 0 100 100" 39 - style="enable-background:new 0 0 100 100" 40 - xmlSpace="preserve" 41 - > 42 - <title>{i18n(cfg.locale).components.themeToggle.lightMode}</title> 43 - <path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path> 44 - </svg> 45 - </label> 46 - </div> 12 + <button class={classNames(displayClass, "darkmode")} id="darkmode"> 13 + <svg 14 + xmlns="http://www.w3.org/2000/svg" 15 + xmlnsXlink="http://www.w3.org/1999/xlink" 16 + version="1.1" 17 + id="dayIcon" 18 + x="0px" 19 + y="0px" 20 + viewBox="0 0 35 35" 21 + style="enable-background:new 0 0 35 35" 22 + xmlSpace="preserve" 23 + aria-label={i18n(cfg.locale).components.themeToggle.darkMode} 24 + > 25 + <title>{i18n(cfg.locale).components.themeToggle.darkMode}</title> 26 + <path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path> 27 + </svg> 28 + <svg 29 + xmlns="http://www.w3.org/2000/svg" 30 + xmlnsXlink="http://www.w3.org/1999/xlink" 31 + version="1.1" 32 + id="nightIcon" 33 + x="0px" 34 + y="0px" 35 + viewBox="0 0 100 100" 36 + style="enable-background:new 0 0 100 100" 37 + xmlSpace="preserve" 38 + aria-label={i18n(cfg.locale).components.themeToggle.lightMode} 39 + > 40 + <title>{i18n(cfg.locale).components.themeToggle.lightMode}</title> 41 + <path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path> 42 + </svg> 43 + </button> 47 44 ) 48 45 } 49 46
+5 -8
quartz/components/scripts/darkmode.inline.ts
··· 11 11 12 12 document.addEventListener("nav", () => { 13 13 const switchTheme = (e: Event) => { 14 - const newTheme = (e.target as HTMLInputElement)?.checked ? "dark" : "light" 14 + const newTheme = 15 + document.documentElement.getAttribute("saved-theme") === "dark" ? "light" : "dark" 15 16 document.documentElement.setAttribute("saved-theme", newTheme) 16 17 localStorage.setItem("theme", newTheme) 17 18 emitThemeChangeEvent(newTheme) ··· 21 22 const newTheme = e.matches ? "dark" : "light" 22 23 document.documentElement.setAttribute("saved-theme", newTheme) 23 24 localStorage.setItem("theme", newTheme) 24 - toggleSwitch.checked = e.matches 25 25 emitThemeChangeEvent(newTheme) 26 26 } 27 27 28 28 // Darkmode toggle 29 - const toggleSwitch = document.querySelector("#darkmode-toggle") as HTMLInputElement 30 - toggleSwitch.addEventListener("change", switchTheme) 31 - window.addCleanup(() => toggleSwitch.removeEventListener("change", switchTheme)) 32 - if (currentTheme === "dark") { 33 - toggleSwitch.checked = true 34 - } 29 + const themeButton = document.querySelector("#darkmode") as HTMLButtonElement 30 + themeButton.addEventListener("click", switchTheme) 31 + window.addCleanup(() => themeButton.removeEventListener("click", switchTheme)) 35 32 36 33 // Listen for changes in prefers-color-scheme 37 34 const colorSchemeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
+11 -13
quartz/components/styles/darkmode.scss
··· 1 1 .darkmode { 2 + cursor: pointer; 3 + padding: 0; 2 4 position: relative; 5 + background: none; 6 + border: none; 3 7 width: 20px; 4 8 height: 20px; 5 9 margin: 0 10px; 6 - 7 - & > .toggle { 8 - display: none; 9 - box-sizing: border-box; 10 - } 10 + text-align: inherit; 11 11 12 12 & svg { 13 - cursor: pointer; 14 - opacity: 0; 15 13 position: absolute; 16 14 width: 20px; 17 15 height: 20px; ··· 29 27 color-scheme: light; 30 28 } 31 29 32 - :root[saved-theme="dark"] .toggle ~ label { 30 + :root[saved-theme="dark"] .darkmode { 33 31 & > #dayIcon { 34 - opacity: 0; 32 + display: none; 35 33 } 36 34 & > #nightIcon { 37 - opacity: 1; 35 + display: inline; 38 36 } 39 37 } 40 38 41 - :root .toggle ~ label { 39 + :root .darkmode { 42 40 & > #dayIcon { 43 - opacity: 1; 41 + display: inline; 44 42 } 45 43 & > #nightIcon { 46 - opacity: 0; 44 + display: none; 47 45 } 48 46 }