Skip to content

Commit b1f2a98

Browse files
www: docs: feat: add dark mode (#2820)
- Added CSS vars for dark/light colors. - Added dark/light theme toggle as Island Fixes #2648 --------- Co-authored-by: Marvin Hagemeister <marvin@deno.com>
1 parent adf8b87 commit b1f2a98

File tree

16 files changed

+320
-46
lines changed

16 files changed

+320
-46
lines changed

www/components/DocsSidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function SidebarCategory(props: {
1212
<li class="my-2 block">
1313
<a
1414
href={href}
15-
class="text-gray-900 hover:text-gray-600 aria-[current]:text-green-700 aria-[current]:hover:underline font-bold"
15+
class="text-foreground-secondary hover:text-gray-600 aria-[current]:text-fresh-green aria-[current]:hover:underline font-bold"
1616
>
1717
{title}
1818
</a>
@@ -36,7 +36,7 @@ export function SidebarEntry(props: {
3636
<li class="py-[1px]">
3737
<a
3838
href={href}
39-
class="aria-[current]:text-green-700 aria-[current]:border-green-600 aria-[current]:bg-green-50 border-l-4 border-transparent px-4 py-0.5 transition-colors hover:text-green-500 font-normal block"
39+
class="aria-[current]:text-fresh-green aria-[current]:border-green-600 aria-[current]:bg-fresh-green/5 border-l-4 border-transparent px-4 py-0.5 transition-colors hover:text-fresh-green/80 font-normal block"
4040
>
4141
{title}
4242
</a>

www/components/Footer.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@ const LINKS = [
1818
export default function Footer(props: JSX.HTMLAttributes<HTMLElement>) {
1919
return (
2020
<footer
21-
class={`border-t-2 border-gray-200 md:h-16 flex mt-16 justify-center md:mx-16 ${props.class}`}
21+
class={`border-t-2 border-foreground-secondary/20 md:h-16 flex mt-16 justify-center md:mx-16 ${props.class}`}
2222
>
2323
<div class="flex flex-col sm:flex-row gap-4 justify-between items-center max-w-screen-xl mx-auto w-full sm:px-6 md:px-8 p-4">
24-
<div class="text-gray-600 text-center">
24+
<div class="text-foreground-secondary text-center">
2525
<span>© {new Date().getFullYear()} the Fresh authors</span>
2626
</div>
2727

2828
<div class="flex items-center gap-8">
2929
{LINKS.map((link) => (
30-
<a href={link.href} class="text-gray-600 hover:underline">
30+
<a
31+
href={link.href}
32+
class="text-foreground-secondary hover:underline"
33+
>
3134
{link.title}
3235
</a>
3336
))}

www/components/Header.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ import NavigationBar from "./NavigationBar.tsx";
33
export default function Header(props: { title: string; active: string }) {
44
const isHome = props.active == "/";
55
const isDocs = props.active == "/docs";
6+
const isShowcase = props.active == "/showcase";
67

78
return (
89
<header
910
class={[
1011
"mx-auto flex gap-3 items-center",
11-
isHome ? "justify-end" : "justify-between",
12+
isHome ? "justify-end h-20 max-w-screen-xl" : "justify-between",
1213
isDocs
13-
? "h-20 max-w-screen-2xl w-full sticky top-0 bg-white/75 z-50 backdrop-blur-sm"
14-
: "h-20 max-w-screen-xl",
14+
? "h-20 max-w-screen-2xl w-full sticky top-0 bg-background-primary z-50 backdrop-blur-sm"
15+
: "",
16+
isShowcase ? "max-w-screen-xl w-full" : "",
1517
].join(" ")}
1618
f-client-nav={false}
1719
>
@@ -39,12 +41,12 @@ export function Logo() {
3941
viewBox="0 0 250 75"
4042
fill="none"
4143
xmlns="http://www.w3.org/2000/svg"
42-
class="h-5 ml-2.5 shrink-0 hidden sm:inline-block"
44+
class="h-5 ml-2.5 shrink-0 hidden sm:inline-block fill-[#0A140C] dark:fill-[#f5ebf3]"
4345
aria-label="Fresh logo"
4446
>
4547
<path
4648
d="M14.0805 0.761269V70.0893H0V0.761269H14.0805ZM35.6322 30.2257V41.7803H10.3448V30.2257H35.6322ZM38.5057 0.761269V12.3159H10.3448V0.761269H38.5057ZM46.5517 0.761269H68.9655C73.5632 0.761269 77.5862 1.62785 80.7471 3.36105C83.908 4.80538 86.4943 7.11632 88.2184 10.2939C90.1641 13.8269 91.1548 17.8107 91.092 21.8485C91.092 25.6038 90.5172 28.7813 89.3678 31.3811C88.5057 34.2698 87.069 36.2919 85.0575 38.0251C83.046 39.7583 81.0345 40.9137 78.4483 42.3581L74.1379 44.669H56.0345V33.1143H68.3908C70.0971 33.2247 71.7975 32.8225 73.2759 31.9589C74.4253 31.0923 75.2874 29.9368 76.1494 28.4925C76.8253 26.6458 77.1186 24.6798 77.0115 22.7151C77.0115 20.6931 76.7241 18.9599 76.1494 17.2267C75.5747 15.7823 74.7126 14.338 73.2759 13.7603C72.4138 12.8937 70.6897 12.3159 68.9655 12.3159H60.3448V70.0893H46.5517V0.761269V0.761269ZM78.1609 70.0893L65.5172 39.1805H79.8851L93.1035 69.5115V70.0893H78.1609ZM140.517 58.5346V70.0893H110.345V58.5346H140.517ZM114.655 0.761269V70.0893H100.575V0.761269H114.943H114.655ZM136.494 29.0702V40.0471H110.345V29.0702H136.207H136.494ZM140.517 0.761269V12.3159H110.345V0.761269H140.517ZM178.161 51.8907C178.161 50.7352 178.161 49.5797 177.586 48.7131C177.586 47.5577 177.012 46.6911 176.149 45.8245L173.276 42.9358L167.816 40.6249L160.345 37.1585L154.023 32.8255C152.021 31.2537 150.359 29.2878 149.138 27.0481C148.066 24.5949 147.574 21.9252 147.701 19.2487C147.701 16.3601 147.989 13.4714 149.138 11.4493C150.166 9.00506 151.739 6.83084 153.736 5.09427C155.747 3.64994 158.046 2.20559 160.632 1.33899C167.362 -0.855543 174.677 -0.337717 181.034 2.78333C184.483 4.51653 186.782 7.11632 188.793 10.2939C190.517 13.1825 191.667 17.2267 191.667 21.2708H177.299C177.424 19.5169 177.229 17.7548 176.724 16.0712C176.149 14.6269 175.287 13.1825 173.851 12.6048C172.701 11.7382 170.977 11.1605 169.253 11.1605C167.529 11.1605 166.092 11.7382 164.943 12.3159C163.793 12.8937 162.931 14.0491 162.069 15.2046L161.494 19.2487C161.494 20.4042 161.782 21.5597 162.356 22.4263L164.655 24.7372C167.235 26.2818 169.924 27.6335 172.701 28.7813L181.034 32.8255C183.199 34.2594 185.136 36.0121 186.782 38.0251C188.793 39.7583 189.655 41.7803 190.805 43.8024C192.605 48.9619 192.503 54.5998 190.517 59.6901C189.368 62.001 187.931 64.0231 186.207 65.7563C184.115 67.5878 181.669 68.9647 179.023 69.8004C173.276 71.2447 176.149 75 170.402 75C164.655 75 166.667 70.667 161.207 69.8004C158.333 68.9338 155.46 67.4895 153.448 65.7563C151.19 63.7856 149.419 61.313 148.276 58.5346C147.126 55.6459 146.552 52.1795 146.552 48.4243H160.632C160.632 50.4463 160.632 52.1795 161.207 53.6239C161.494 55.0682 162.069 56.5125 162.931 57.3791C163.793 57.9569 164.943 58.8235 166.092 59.1123C167.529 59.6901 168.966 59.6901 170.402 59.6901C172.414 59.6901 173.851 59.6901 175 58.8235C176.149 57.9569 176.724 57.0903 177.299 55.9348C177.874 54.7793 178.161 53.335 178.161 51.8907V51.8907ZM239.943 28.7813V40.336H211.207V28.7813H239.943ZM215.23 0.761269V70.0893H201.437V0.761269H215.23ZM250 0.761269V70.0893H236.207V0.761269H250Z"
47-
fill="#0A140C"
49+
fill="currentColor"
4850
/>
4951
</svg>
5052
</a>

www/components/NavigationBar.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ThemeToggle from "../islands/ThemeToggle.tsx";
12
import * as Icons from "./Icons.tsx";
23

34
export default function NavigationBar(
@@ -18,6 +19,7 @@ export default function NavigationBar(
1819
},
1920
];
2021
const isHome = props.active == "/";
22+
const isDocs = props.active == "/docs";
2123
return (
2224
<nav class={"flex " + (props.class ?? "")} f-client-nav={false}>
2325
<ul class="flex items-center gap-x-2 sm:gap-4 mx-4 my-2 sm:my-6 flex-wrap lg:mx-8 2xl:mr-0">
@@ -26,7 +28,11 @@ export default function NavigationBar(
2628
<a
2729
href={item.href}
2830
class={`p-1 sm:p-2 ${
29-
isHome ? "text-green-900" : "text-gray-600"
31+
isHome
32+
? "text-green-900"
33+
: isDocs
34+
? "text-foreground-secondary"
35+
: "text-gray-600"
3036
} hover:underline aria-[current]:font-bold`}
3137
>
3238
{item.name}
@@ -52,6 +58,11 @@ export default function NavigationBar(
5258
<Icons.Discord />
5359
</a>
5460
</li>
61+
{isDocs && (
62+
<li class="flex items-center">
63+
<ThemeToggle />
64+
</li>
65+
)}
5566
</ul>
5667
</nav>
5768
);

www/islands/CopyArea.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ export default function CopyArea(props: { code: string }) {
4848
<button
4949
aria-label="Copy to Clipboard"
5050
disabled={!IS_BROWSER}
51-
class={`rounded p-1.5 border border-gray-300 hover:bg-gray-700 ${
52-
copied.value ? "text-green-500" : ""
51+
class={`rounded p-1.5 border border-foreground-secondary/30 hover:bg-foreground-secondary/70 ${
52+
copied.value ? "text-fresh-green/80" : ""
5353
} relative`}
5454
onClick={handleClick}
5555
>

www/islands/TableOfContents.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export function TableOfContents({ headings }: TableOfContentsProps) {
105105
<button
106106
id="toc-outline-btn"
107107
onClick={() => setIsOpen((v) => !v)}
108-
class="bg-gray-100 py-2 px-4 rounded border border-gray-300 flex items-center hover:border-green-600 transition-colors text-sm"
108+
class="bg-background-primary py-2 px-4 rounded border border-foreground-secondary/30 flex items-center hover:border-fresh-green/80 transition-colors text-sm"
109109
>
110110
On this page
111111
<svg
@@ -117,7 +117,7 @@ export function TableOfContents({ headings }: TableOfContentsProps) {
117117
</svg>
118118
</button>
119119
{isOpen && (
120-
<div class="mt-2 pl-4 border-l border-gray-250 text-[13px] leading-7">
120+
<div class="mt-2 pl-4 border-l border-foreground-primary/20 text-[13px] leading-7">
121121
<nav aria-labelledby="toc-outline-btn">
122122
<ul>
123123
{headings.map((heading) => {
@@ -142,7 +142,7 @@ export function TableOfContents({ headings }: TableOfContentsProps) {
142142
ref={refMarker}
143143
class="marker w-[2px] bg-green-400 h-5 absolute top-0 opacity-0 transition-all"
144144
/>
145-
<div class="pl-4 border-l border-gray-250 text-[13px] leading-7">
145+
<div class="pl-4 border-l border-foreground-secondary/20 text-[13px] leading-7">
146146
<div role="heading" aria-level={2} class="font-semibold">
147147
On this page
148148
</div>

www/islands/ThemeToggle.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { useEffect, useState } from "preact/hooks";
2+
import { IS_BROWSER } from "fresh/runtime";
3+
4+
export default function ThemeToggle() {
5+
const getPreferredTheme = () => {
6+
if (!IS_BROWSER) return "light";
7+
const storedTheme = localStorage.getItem("theme");
8+
if (storedTheme) return storedTheme;
9+
return window.matchMedia("(prefers-color-scheme: dark)").matches
10+
? "dark"
11+
: "light";
12+
};
13+
14+
const [theme, setTheme] = useState(getPreferredTheme);
15+
16+
useEffect(() => {
17+
document.documentElement.classList.remove("light", "dark");
18+
document.documentElement.classList.add(theme);
19+
document.documentElement.setAttribute("data-theme", theme);
20+
localStorage.setItem("theme", theme);
21+
}, [theme]);
22+
23+
const toggleTheme = () => {
24+
setTheme((prev) => (prev === "light" ? "dark" : "light"));
25+
};
26+
27+
return (
28+
<button
29+
type="button"
30+
onClick={toggleTheme}
31+
class="dark-mode-toggle button p-1 -m-1"
32+
aria-label="Toggle Theme"
33+
>
34+
{theme === "light"
35+
? (
36+
<svg
37+
class="fill-foreground-primary hover:fill-fresh w-6 h-6"
38+
viewBox="0 0 20 20"
39+
xmlns="http://www.w3.org/2000/svg"
40+
>
41+
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z">
42+
</path>
43+
</svg>
44+
)
45+
: (
46+
<svg
47+
class="fill-foreground-primary hover:fill-fresh w-6 h-6"
48+
viewBox="0 0 20 20"
49+
xmlns="http://www.w3.org/2000/svg"
50+
>
51+
<path
52+
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1
53+
0 100-2H3a1 1 0 000 2h1z"
54+
fill-rule="evenodd"
55+
clip-rule="evenodd"
56+
>
57+
</path>
58+
</svg>
59+
)}
60+
</button>
61+
);
62+
}

www/islands/VersionSelect.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function VersionSelect(
2020
</label>
2121
{selectedIsLatest && (
2222
<div class="flex absolute pointer-events-none select-none w-full h-full items-center justify-end pr-8">
23-
<div class="rounded-full px-2 py-1 text-xs tag-label bg-[#056CF025] text-blue-700">
23+
<div class="rounded-full px-2 py-1 text-xs tag-label bg-[#056CF025] dark:bg-[#2182ff45] text-blue-700 dark:text-blue-400">
2424
Latest
2525
</div>
2626
</div>
@@ -34,9 +34,9 @@ export default function VersionSelect(
3434
)}
3535
<select
3636
id="version"
37-
class={`rounded-md block border border-gray-300 appearance-none bg-white form-select-bg font-semibold ${
37+
class={`rounded-md block border border-foreground-primary/20 appearance-none bg-background-primary form-select-bg font-semibold ${
3838
selectedIsLatest ? "pr-22" : "pr-10"
39-
} py-2 pl-3 w-full h-full leading-none sm:(text-sm leading-5) focus:(outline-none border-blue-300) hover:bg-gray-100`}
39+
} py-2 pl-3 w-full h-full leading-none sm:(text-sm leading-5) focus:(outline-none border-blue-300) hover:bg-background-secondary`}
4040
value={selectedVersion}
4141
onChange={(e) => {
4242
if (e.currentTarget.value !== selectedVersion) {

www/routes/_app.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { define } from "../utils/state.ts";
33

44
export default define.page(function App({ Component, state, url }) {
55
return (
6-
<html lang="en">
6+
<html lang="en" class="dark" data-theme="dark">
77
<head>
88
<meta charset="utf-8" />
99
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -42,6 +42,8 @@ export default define.page(function App({ Component, state, url }) {
4242
</>
4343
)
4444
: null}
45+
<script src="/theme.client.js"></script>
46+
<script type="module" src="/theme-toggle.client.js"></script>
4547
</head>
4648
<body>
4749
<Component />

www/routes/docs/[...slug].tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ export default define.page<typeof handler>(function DocsPage(props) {
152152
<Header title="docs" active="/docs" />
153153
<div f-client-nav={true}>
154154
<MobileSidebar page={page} />
155-
<div class="flex mx-auto max-w-screen-2xl px-0 md:px-4 md:py-0 justify-start bg-gray-100">
155+
<div class="flex mx-auto max-w-screen-2xl px-0 md:px-4 md:py-0 justify-start bg-background-secondary">
156156
<label
157157
for="docs_sidebar"
158-
class="px-4 py-3 lg:hidden flex items-center hover:bg-gray-100 rounded gap-2 cursor-pointer"
158+
class="px-4 py-3 lg:hidden flex items-center rounded gap-2 cursor-pointer"
159159
>
160160
<svg
161161
class="h-6 w-6"
@@ -178,7 +178,7 @@ export default define.page<typeof handler>(function DocsPage(props) {
178178
<div class="fixed top-24 w-[17rem] flex overflow-hidden">
179179
<div class="flex-1 h-[calc(100vh_-_6rem)] overflow-y-auto pb-8">
180180
<SearchButton class="mr-4 sm:mr-0" />
181-
<div class="mb-4">
181+
<div class="mb-4 px-1">
182182
<VersionSelect
183183
selectedVersion={page.version}
184184
versions={page.versionLinks}
@@ -199,7 +199,7 @@ export default define.page<typeof handler>(function DocsPage(props) {
199199
<TableOfContents headings={headings} />
200200

201201
<div class="lg:order-1 min-w-0 max-w-3xl w-full">
202-
<h1 class="text-4xl text-gray-900 tracking-tight font-bold md:mt-0 px-4 md:px-0 mb-4">
202+
<h1 class="text-4xl text-foreground-primary tracking-tight font-bold md:mt-0 px-4 md:px-0 mb-4">
203203
{page.title}
204204
</h1>
205205
<div
@@ -256,7 +256,7 @@ function MobileSidebar({ page }: { page: Page }) {
256256
class="absolute inset-0 bg-gray-600 opacity-75"
257257
for="docs_sidebar"
258258
/>
259-
<div class="relative flex-1 flex flex-col w-[18rem] h-full bg-white border-r-2 border-gray-100">
259+
<div class="relative flex-1 flex flex-col w-[18rem] h-full bg-background-primary border-r-2 border-foreground-secondary">
260260
<nav class="pt-0 pb-16 overflow-x-auto">
261261
<div class="flex-1 h-screen overflow-y-auto pt-4 px-4">
262262
<SearchButton class="mr-4 sm:mr-0" />
@@ -293,9 +293,9 @@ function ForwardBackButtons(props: {
293293
? (
294294
<a
295295
href={prev.href}
296-
class="px-4 py-2 text-left rounded border border-gray-200 grid border-solid w-full hover:border-green-600 transition-colors"
296+
class="px-4 py-2 text-left rounded border border-foreground-secondary/20 grid border-solid w-full hover:border-green-600 transition-colors"
297297
>
298-
<span class="text-sm text-gray-600">
298+
<span class="text-sm text-gray-600 dark:text-gray-500">
299299
Previous page
300300
</span>
301301
<span class="text-green-600 font-medium">
@@ -308,9 +308,9 @@ function ForwardBackButtons(props: {
308308
? (
309309
<a
310310
href={next.href}
311-
class="px-4 py-2 text-right rounded border border-gray-200 border-solid grid w-full hover:border-green-600 transition-colors"
311+
class="px-4 py-2 text-left rounded border border-foreground-secondary/20 grid border-solid w-full hover:border-green-600 transition-colors"
312312
>
313-
<span class="text-sm text-gray-600">
313+
<span class="text-sm text-gray-600 dark:text-gray-500">
314314
Next page
315315
</span>
316316
<span class="text-green-600 font-medium">

0 commit comments

Comments
 (0)