})
diff --git a/components/TechCard.tsx b/components/TechCard.tsx
index 808526b..c614585 100644
--- a/components/TechCard.tsx
+++ b/components/TechCard.tsx
@@ -12,15 +12,15 @@ function TechCard({
children: ComponentChildren;
}) {
return (
-
+
);
}
diff --git a/deno.json b/deno.json
index 0d61268..28a0460 100644
--- a/deno.json
+++ b/deno.json
@@ -1,11 +1,12 @@
{
- "version": "1.0.0",
"lock": false,
"tasks": {
- "start": "deno run -Ar main.ts",
- "dev": "deno run -A --watch=components/,islands/,routes/,static/,utils/,deno.json,twind.config.ts,fresh.config.ts dev.ts --no-clear-screen",
+ "start": "deno run -A main.ts",
+ "dev": "deno run -A --watch=components/,islands/,routes/,static/,utils/,deno.json,main.ts,dev.ts,fresh.config.ts,tailwind.config.ts dev.ts --no-clear-screen",
"test": "deno test --allow-all --no-check --doc --allow-none",
"build": "deno run -A dev.ts build",
+ "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
+ "update": "deno run -A -r https://fresh.deno.dev/update .",
"preview": "deno run -A main.ts"
},
"lint": {
@@ -18,8 +19,7 @@
"ban-untagged-todo"
],
"exclude": [
- "no-unused-vars",
- "no-cache-npm"
+ "no-unused-vars"
]
},
"exclude": [
@@ -60,11 +60,8 @@
"$testing/": "https://deno.land/std@0.150.0/testing/",
"preact": "https://esm.sh/preact@10.19.2",
"preact/": "https://esm.sh/preact@10.19.2/",
- "preact-render-to-string": "https://esm.sh/*preact-render-to-string@6.2.0",
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.0",
"@preact/signals": "https://esm.sh/*@preact/signals@1.2.1",
- "twind": "https://esm.sh/twind@0.16.19",
- "twind/": "https://esm.sh/twind@0.16.19/",
"fresh_validation": "https://deno.land/x/fresh_validation@0.1.1/mod.ts",
"fresh_session": "https://deno.land/x/fresh_session@0.1.7/mod.ts",
"fresh_seo": "https://deno.land/x/fresh_seo@0.1.2/mod.ts",
@@ -72,8 +69,9 @@
"gfm": "https://deno.land/x/gfm@0.1.26/mod.ts",
"speed_highlight_js/": "https://unpkg.com/@speed-highlight/core@1.1.11/dist/",
"tailwindcss": "npm:tailwindcss@3.4.1",
- "tailwindcss/colors": "npm:tailwindcss@3.4.1/colors.js",
- "tailwindcss/plugin": "npm:tailwindcss@3.4.1/plugin.js"
+ "tailwindcss/": "npm:/tailwindcss@3.4.1/",
+ "tailwindcss/plugin": "npm:/tailwindcss@3.4.1/plugin.js",
+ "tailwindcss/colors": "npm:/tailwindcss@3.4.1/colors.js"
},
"exclude": [
"**/_fresh/*"
diff --git a/fresh.config.ts b/fresh.config.ts
index 047a011..c1fbd3f 100644
--- a/fresh.config.ts
+++ b/fresh.config.ts
@@ -1,7 +1,9 @@
import { defineConfig } from '$fresh/server.ts';
-import tailwindPlugin from '$fresh/plugins/tailwind.ts';
+import tailwind from '$fresh/plugins/tailwind.ts';
import tailwindConfig from '@/tailwind.config.ts';
export default defineConfig({
- plugins: [tailwindPlugin(tailwindConfig)],
+ plugins: [
+ tailwind(tailwindConfig), // ignore error, bug within the framework plugin
+ ],
});
diff --git a/fresh.gen.ts b/fresh.gen.ts
index 1245c18..721d21d 100644
--- a/fresh.gen.ts
+++ b/fresh.gen.ts
@@ -16,7 +16,8 @@ import * as $license from './routes/license.tsx';
import * as $privacy_policy from './routes/privacy-policy.tsx';
import * as $resume from './routes/resume.tsx';
import * as $sitemap_xml from './routes/sitemap.xml.ts';
-import * as $ColorMode from './islands/ColorMode.tsx';
+import * as $MobileNav from './islands/MobileNav.tsx';
+import * as $ThemeMode from './islands/ThemeMode.tsx';
import { type Manifest } from '$fresh/server.ts';
const manifest = {
@@ -37,7 +38,8 @@ const manifest = {
'./routes/sitemap.xml.ts': $sitemap_xml,
},
islands: {
- './islands/ColorMode.tsx': $ColorMode,
+ './islands/MobileNav.tsx': $MobileNav,
+ './islands/ThemeMode.tsx': $ThemeMode,
},
baseUrl: import.meta.url,
} satisfies Manifest;
diff --git a/islands/ColorMode.tsx b/islands/ColorMode.tsx
deleted file mode 100644
index fef3c11..0000000
--- a/islands/ColorMode.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { useEffect } from 'preact/hooks';
-import { useSignal } from '@preact/signals';
-import IconMoon from '@tabler/icons/moon.tsx';
-import IconSun from '@tabler/icons/sun.tsx';
-import { classNames } from '../utils/classNames.ts';
-
-const modes = ['dark', 'light'] as const;
-
-export default function ColorMode() {
- const state = useSignal<(typeof modes)[number]>('light');
-
- function detectColorMode() {
- if (
- globalThis.localStorage.colorMode === 'dark' ||
- (!('colorMode' in globalThis.localStorage) &&
- window.matchMedia('(prefers-color-scheme: dark)').matches)
- ) {
- document.documentElement.classList.add('dark');
- state.value = 'dark';
- } else {
- document.documentElement.classList.remove('dark');
- state.value = 'light';
- }
- }
-
- function toggleColorMode() {
- state.value = modes[(modes.indexOf(state.value) + 1) % modes.length];
-
- globalThis.localStorage.colorMode = state.value;
-
- if (
- globalThis.localStorage.colorMode === 'dark' ||
- (!('colorMode' in globalThis.localStorage) &&
- window.matchMedia('(prefers-color-scheme: dark)').matches)
- ) {
- document.documentElement.classList.add('dark');
- } else {
- document.documentElement.classList.remove('dark');
- }
- }
-
- useEffect(detectColorMode, []);
-
- return (
-
- );
-}
diff --git a/islands/MobileNav.tsx b/islands/MobileNav.tsx
new file mode 100644
index 0000000..0cda3ac
--- /dev/null
+++ b/islands/MobileNav.tsx
@@ -0,0 +1,122 @@
+import { Route, Routes } from '@/routes.ts';
+import { classNames } from '@/utils/classNames.ts';
+import { JSX } from 'preact/jsx-runtime';
+
+interface MobileNavProps {
+ routes: Route[];
+ active: Routes;
+}
+
+export default function MobileNav(props: MobileNavProps): JSX.Element {
+ return (
+ <>
+
+
+
+
+
+ {props.routes.map((item: Route) => {
+ // if the route has a displayName, render it
+ if (item.displayName) {
+ return (
+ -
+
+
+
+ {item.displayName}
+
+
+
+ );
+ }
+ })}
+
+ >
+ );
+}
diff --git a/islands/ThemeMode.tsx b/islands/ThemeMode.tsx
new file mode 100644
index 0000000..4ab172e
--- /dev/null
+++ b/islands/ThemeMode.tsx
@@ -0,0 +1,80 @@
+import { IS_BROWSER } from '$fresh/runtime.ts';
+
+import IconMoon from '@tabler/icons/moon.tsx';
+import IconSun from '@tabler/icons/sun.tsx';
+import { useSignal } from '@preact/signals';
+import { useEffect } from 'preact/hooks';
+import { JSX } from 'preact/jsx-runtime';
+
+export const modes = ['dark', 'light'] as const;
+
+export default function ThemeMode(): JSX.Element {
+ const state = useSignal<(typeof modes)[number]>('light');
+
+ /**
+ * Toggles the color mode.
+ * @param themeMode - Explicitly set a color mode.
+ * @returns The next color mode.
+ */
+ function setThemeMode(themeMode: typeof modes[number]): typeof modes[number] {
+ switch (themeMode) {
+ case 'dark':
+ document.documentElement.classList.add('dark');
+ break;
+ case 'light':
+ document.documentElement.classList.remove('dark');
+ break;
+ }
+
+ state.value = themeMode;
+ globalThis.localStorage.theme = themeMode;
+
+ return themeMode;
+ }
+
+ /**
+ * Detects the color mode based on the user's preference or the system's default.
+ * @returns The detected color mode ('dark' or 'light').
+ */
+ function detectThemeMode(): typeof modes[number] {
+ if (
+ globalThis.localStorage.theme === 'dark' ||
+ (!('theme' in globalThis.localStorage) &&
+ globalThis.matchMedia('(prefers-color-scheme: dark)').matches)
+ ) {
+ state.value = 'dark';
+ document.documentElement.classList.add('dark');
+ return 'dark' as typeof modes[number];
+ } else {
+ state.value = 'light';
+ document.documentElement.classList.remove('dark');
+ return 'light' as typeof modes[number];
+ }
+ }
+
+ useEffect(() => {
+ setThemeMode(detectThemeMode());
+ }, []);
+
+ if (!IS_BROWSER) return
;
+ return (
+
+ );
+}
diff --git a/main.ts b/main.ts
index dbd8e80..2f699e0 100644
--- a/main.ts
+++ b/main.ts
@@ -1,9 +1,11 @@
-///
man
+///
///
///
///
///
+import '$std/dotenv/load.ts';
+
import { start } from '$fresh/server.ts';
import manifest from '@/fresh.gen.ts';
import config from '@/fresh.config.ts';
diff --git a/routes.ts b/routes.ts
new file mode 100644
index 0000000..b3534f8
--- /dev/null
+++ b/routes.ts
@@ -0,0 +1,55 @@
+export interface Route {
+ displayName?: string;
+ id: Routes;
+ href: string;
+}
+
+export enum Routes {
+ home,
+ blog,
+ resume,
+ license,
+ notFound,
+ serverError,
+ privPolicy,
+ mwotw,
+}
+
+export const routes: Route[] = [
+ {
+ id: Routes.home,
+ href: '/home',
+ displayName: 'Home',
+ },
+ {
+ id: Routes.blog,
+ href: '/blog',
+ displayName: 'Blog',
+ },
+ {
+ id: Routes.mwotw,
+ href: '/mwotw',
+ displayName: 'MWOTW',
+ },
+ {
+ id: Routes.resume,
+ href: '/resume',
+ displayName: 'Resume',
+ },
+ {
+ id: Routes.license,
+ href: '/license',
+ },
+ {
+ id: Routes.privPolicy,
+ href: '/privacy-policy',
+ },
+ {
+ id: Routes.notFound,
+ href: '/404',
+ },
+ {
+ id: Routes.serverError,
+ href: '/500',
+ },
+];
diff --git a/routes/500.tsx b/routes/500.tsx
index fd08b89..d8a0af1 100644
--- a/routes/500.tsx
+++ b/routes/500.tsx
@@ -2,6 +2,6 @@ import { Handlers } from '$fresh/server.ts';
export const handler: Handlers = {
GET(_req, ctx) {
- return ctx.render();
+ return ctx.render(new Error('Server Error'));
},
};
diff --git a/routes/_404.tsx b/routes/_404.tsx
index bbeef16..e830c1c 100644
--- a/routes/_404.tsx
+++ b/routes/_404.tsx
@@ -1,16 +1,19 @@
import { asset } from '$fresh/runtime.ts';
-import { UnknownPageProps } from '$fresh/server.ts';
+import { PageProps } from '$fresh/server.ts';
-import { Head } from '../components/Head.tsx';
-import { Navbar, Routes } from '../components/Navbar.tsx';
-import { NoScript } from '../components/NoScript.tsx';
+import { Head } from '@/components/Head.tsx';
+import { Routes } from '@/routes.ts';
+import { Navbar } from '@/components/Navbar.tsx';
+import { NoScript } from '@/components/NoScript.tsx';
import IconDirectionsOff from '@tabler/icons/directions-off.tsx';
-function NotFoundPage(PageProps: UnknownPageProps) {
+function NotFoundPage(PageProps: PageProps) {
return (
<>
>
);
diff --git a/routes/_500.tsx b/routes/_500.tsx
index a5a7957..cdf85be 100644
--- a/routes/_500.tsx
+++ b/routes/_500.tsx
@@ -1,15 +1,18 @@
-import { Navbar, Routes } from '../components/Navbar.tsx';
+import { Routes } from '@/routes.ts';
+import { Navbar } from '@/components/Navbar.tsx';
-import { ErrorPageProps } from '$fresh/server.ts';
-import { Head } from '../components/Head.tsx';
-import { NoScript } from '../components/NoScript.tsx';
+import { PageProps } from '$fresh/server.ts';
+import { Head } from '@/components/Head.tsx';
+import { NoScript } from '@/components/NoScript.tsx';
import { asset } from '$fresh/runtime.ts';
-function ErrorPage(PageProps: ErrorPageProps) {
+function ErrorPage(PageProps: PageProps) {
return (
<>