better color handling

This commit is contained in:
Michael Freno
2025-12-18 22:09:17 -05:00
parent 1a77cc08cf
commit bb11775d82
4 changed files with 225 additions and 205 deletions

View File

@@ -128,8 +128,37 @@
} }
} }
/* Manual dark mode via .dark class */ /* Manual theme override via classes (higher specificity than media query) */
.dark { :root.light {
--color-rosewater: #c14a4a;
--color-flamingo: #c14a4a;
--color-pink: #945e80;
--color-mauve: #945e80;
--color-red: #c14a4a;
--color-maroon: #c14a4a;
--color-peach: #c35e0a;
--color-yellow: #a96b2c;
--color-green: #6c782e;
--color-teal: #4c7a5d;
--color-sky: #4c7a5d;
--color-sapphire: #4c7a5d;
--color-blue: #45707a;
--color-lavender: #45707a;
--color-text: #654735;
--color-subtext1: #7b5d44;
--color-subtext0: #8f6f56;
--color-overlay2: #a28368;
--color-overlay1: #b6977a;
--color-overlay0: #c9aa8c;
--color-surface2: #a79c86;
--color-surface1: #c9c19f;
--color-surface0: #dfd6b1;
--color-base: #fbf1c7;
--color-mantle: #f3eac1;
--color-crust: #e7deb7;
}
:root.dark {
--color-rosewater: #efc9c2; --color-rosewater: #efc9c2;
--color-flamingo: #ebb2b2; --color-flamingo: #ebb2b2;
--color-pink: #f2a7de; --color-pink: #f2a7de;
@@ -496,11 +525,12 @@ input[type="checkbox"]:checked::before {
} }
/* Hamburger menu button - only show on non-touch devices under mobile breakpoint */ /* Hamburger menu button - only show on non-touch devices under mobile breakpoint */
@media (max-width: 767px) and (hover: hover) and (pointer: fine) { @media (max-width: 767px) {
.hamburger-menu-btn { .hamburger-menu-btn {
display: block !important; display: block !important;
} }
} }
@keyframes shaker { @keyframes shaker {
0% { 0% {
transform: rotate(0deg); transform: rotate(0deg);

View File

@@ -9,6 +9,124 @@ import LinkedIn from "./icons/LinkedIn";
import MoonIcon from "./icons/MoonIcon"; import MoonIcon from "./icons/MoonIcon";
import SunIcon from "./icons/SunIcon"; import SunIcon from "./icons/SunIcon";
export function RightBarContent() {
const [isDark, setIsDark] = createSignal(false);
onMount(() => {
const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
if (prefersDark) {
setIsDark(true);
document.documentElement.classList.add("dark");
document.documentElement.classList.remove("light");
} else {
setIsDark(false);
document.documentElement.classList.add("light");
document.documentElement.classList.remove("dark");
}
});
const toggleDarkMode = () => {
const newDarkMode = !isDark();
setIsDark(newDarkMode);
if (newDarkMode) {
document.documentElement.classList.add("dark");
document.documentElement.classList.remove("light");
} else {
document.documentElement.classList.add("light");
document.documentElement.classList.remove("dark");
}
};
return (
<div class="text-text flex h-full flex-col justify-between py-6">
<Typewriter keepAlive={false} class="z-50 px-4 pt-4">
<div class="flex flex-col gap-6">
<h3 class="text-subtext0 rule-around text-lg font-semibold">
Connect
</h3>
<ul class="flex flex-col gap-4">
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<a href="/contact">Contact Me</a>
</li>
<li>
<a
href="https://github.com/MikeFreno/"
target="_blank"
rel="noreferrer"
class="hover:text-subtext0 flex items-center gap-3 transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105"
>
<span class="shaker rounded-full p-2">
<GitHub height={24} width={24} fill={undefined} />
</span>
<span>GitHub</span>
</a>
</li>
<li>
<a
href="https://www.linkedin.com/in/michael-freno-176001256/"
target="_blank"
rel="noreferrer"
class="hover:text-subtext0 flex items-center gap-3 transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105"
>
<span class="shaker rounded-full p-2">
<LinkedIn height={24} width={24} fill={undefined} />
</span>
<span>LinkedIn</span>
</a>
</li>
<li>
<a
href="/resume"
class="hover:text-subtext0 flex items-center gap-3 transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105"
>
<span class="shaker rounded-full p-2">
<svg
xmlns="http://www.w3.org/2000/svg"
height={24}
width={24}
viewBox="0 0 384 512"
class="fill-text"
>
<path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM112 256H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16z" />
</svg>
</span>
<span>Resume</span>
</a>
</li>
</ul>
</div>
</Typewriter>
{/* Dark Mode Toggle */}
<div class="border-overlay0 border-t px-4 pt-6">
<button
onClick={toggleDarkMode}
class="hover:bg-surface0 flex w-full items-center gap-3 rounded-lg p-3 transition-all duration-200 ease-in-out hover:scale-105"
aria-label="Toggle dark mode"
>
<Show
when={isDark()}
fallback={<SunIcon size={24} fill="var(--color-text)" />}
>
<MoonIcon size={24} fill="var(--color-text)" />
</Show>
<span class="font-semibold">
<Show
when={isDark()}
fallback={<Typewriter keepAlive={false}>Light Mode</Typewriter>}
>
<Typewriter keepAlive={false}>Dark Mode</Typewriter>
</Show>
</span>
</button>
</div>
</div>
);
}
export function LeftBar() { export function LeftBar() {
const { setLeftBarSize, leftBarVisible, setLeftBarVisible } = useBars(); const { setLeftBarSize, leftBarVisible, setLeftBarVisible } = useBars();
let ref: HTMLDivElement | undefined; let ref: HTMLDivElement | undefined;
@@ -156,7 +274,7 @@ export function LeftBar() {
{/* Hamburger menu button - positioned at right edge of navbar */} {/* Hamburger menu button - positioned at right edge of navbar */}
<button <button
onClick={() => setLeftBarVisible(!leftBarVisible())} onClick={() => setLeftBarVisible(!leftBarVisible())}
class="hamburger-menu-btn bg-surface0 hover:bg-surface1 absolute top-4 -right-14 rounded-md p-2 shadow-md transition-colors" class="hamburger-menu-btn absolute top-4 -right-14 z-10 rounded-md p-2 shadow-md backdrop-blur-2xl transition-transform duration-600 ease-in-out hover:scale-110"
classList={{ classList={{
hidden: leftBarVisible() hidden: leftBarVisible()
}} }}
@@ -180,12 +298,14 @@ export function LeftBar() {
</svg> </svg>
</button> </button>
<div class="flex h-full min-h-full flex-col overflow-y-auto">
<Typewriter speed={10} keepAlive={10000} class="z-50 pr-8 pl-4"> <Typewriter speed={10} keepAlive={10000} class="z-50 pr-8 pl-4">
<h3 class="hover:text-subtext0 w-fit text-center text-3xl underline transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105"> <h3 class="hover:text-subtext0 w-fit text-center text-3xl underline transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105">
<a href="/">Freno.dev</a> <a href="/">Freno.dev</a>
</h3> </h3>
</Typewriter> </Typewriter>
<div class="text-text flex flex-col px-4 text-xl font-bold">
<div class="text-text flex flex-1 flex-col px-4 pb-4 text-xl font-bold">
<div class="flex flex-col py-8"> <div class="flex flex-col py-8">
<span class="text-lg font-semibold">Recent Posts</span> <span class="text-lg font-semibold">Recent Posts</span>
<div class="flex flex-col gap-3 pt-4"> <div class="flex flex-col gap-3 pt-4">
@@ -222,8 +342,11 @@ export function LeftBar() {
</Show> </Show>
</div> </div>
</div> </div>
<Typewriter keepAlive={false} class="absolute bottom-12">
<div class="flex flex-col gap-4"> {/* Navigation Links */}
<div class="mt-auto">
<Typewriter keepAlive={false}>
<div class="flex flex-col gap-4 py-6">
<ul class="gap-4"> <ul class="gap-4">
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold"> <li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<a href="/">Home</a> <a href="/">Home</a>
@@ -232,23 +355,18 @@ export function LeftBar() {
<a href="/blog">Blog</a> <a href="/blog">Blog</a>
</li> </li>
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold"> <li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<a href="/contact">Contact</a> <a href="/login">Login</a>
</li>
</ul>
{/* Right bar navigation merged for mobile */}
<ul class="border-overlay0 gap-4 border-t pt-4 md:hidden">
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<a href="/">Home</a>
</li>
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<a href="#about">About</a>
</li>
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<a href="/contact">Contact</a>
</li> </li>
</ul> </ul>
</div> </div>
</Typewriter> </Typewriter>
{/* RightBar content on mobile */}
<div class="border-overlay0 border-t pt-8 md:hidden">
<RightBarContent />
</div>
</div>
</div>
</div> </div>
</nav> </nav>
); );
@@ -259,20 +377,7 @@ export function RightBar() {
let ref: HTMLDivElement | undefined; let ref: HTMLDivElement | undefined;
let actualWidth = 0; let actualWidth = 0;
const [isDark, setIsDark] = createSignal(false);
onMount(() => { onMount(() => {
// Initialize dark mode based on system preference
const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
const savedTheme = localStorage.getItem("theme");
if (savedTheme === "dark" || (!savedTheme && prefersDark)) {
setIsDark(true);
document.documentElement.classList.add("dark");
}
if (ref) { if (ref) {
const updateSize = () => { const updateSize = () => {
actualWidth = ref?.offsetWidth || 0; actualWidth = ref?.offsetWidth || 0;
@@ -282,7 +387,6 @@ export function RightBar() {
updateSize(); updateSize();
const resizeObserver = new ResizeObserver((entries) => { const resizeObserver = new ResizeObserver((entries) => {
// Use requestAnimationFrame to avoid ResizeObserver loop error
requestAnimationFrame(() => { requestAnimationFrame(() => {
actualWidth = ref?.offsetWidth || 0; actualWidth = ref?.offsetWidth || 0;
setRightBarSize(rightBarVisible() ? actualWidth : 0); setRightBarSize(rightBarVisible() ? actualWidth : 0);
@@ -296,22 +400,10 @@ export function RightBar() {
} }
}); });
// Update size when visibility changes
createEffect(() => { createEffect(() => {
setRightBarSize(rightBarVisible() ? actualWidth : 0); setRightBarSize(rightBarVisible() ? actualWidth : 0);
}); });
const toggleDarkMode = () => {
setIsDark(!isDark());
if (!isDark()) {
document.documentElement.classList.add("dark");
localStorage.setItem("theme", "dark");
} else {
document.documentElement.classList.remove("dark");
localStorage.setItem("theme", "light");
}
};
return ( return (
<nav <nav
ref={ref} ref={ref}
@@ -322,85 +414,11 @@ export function RightBar() {
}} }}
style={{ style={{
"transition-timing-function": rightBarVisible() "transition-timing-function": rightBarVisible()
? "cubic-bezier(0.34, 1.56, 0.64, 1)" // Bounce out when revealing ? "cubic-bezier(0.34, 1.56, 0.64, 1)"
: "cubic-bezier(0.4, 0, 0.2, 1)" // Smooth when hiding : "cubic-bezier(0.4, 0, 0.2, 1)"
}} }}
> >
<Typewriter keepAlive={false} class="z-50 px-4 pt-4"> <RightBarContent />
<div class="text-text flex h-screen flex-col justify-between py-6">
<div class="flex flex-col gap-6">
<h3 class="text-subtext0 rule-around text-lg font-semibold">
Connect
</h3>
<ul class="flex flex-col gap-4">
<li class="hover:text-subtext0 w-fit transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-110 hover:font-bold">
<a href="/contact">Contact</a>
</li>
<li>
<a
href="https://github.com/MikeFreno/"
target="_blank"
rel="noreferrer"
class="hover:text-subtext0 flex items-center gap-3 transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105"
>
<span class="shaker rounded-full p-2">
<GitHub height={24} width={24} fill={undefined} />
</span>
<span>GitHub</span>
</a>
</li>
<li>
<a
href="https://www.linkedin.com/in/michael-freno-176001256/"
target="_blank"
rel="noreferrer"
class="hover:text-subtext0 flex items-center gap-3 transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105"
>
<span class="shaker rounded-full p-2">
<LinkedIn height={24} width={24} fill={undefined} />
</span>
<span>LinkedIn</span>
</a>
</li>
<li>
<a
href="/resume"
class="hover:text-subtext0 flex items-center gap-3 transition-transform duration-200 ease-in-out hover:-translate-y-0.5 hover:scale-105"
>
<span class="shaker rounded-full p-2">
<svg
xmlns="http://www.w3.org/2000/svg"
height={24}
width={24}
viewBox="0 0 384 512"
class="fill-text"
>
<path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM112 256H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16z" />
</svg>
</span>
<span>Resume</span>
</a>
</li>
</ul>
</div>
{/* Dark Mode Toggle */}
<div class="border-overlay0 border-t pt-6">
<button
onClick={toggleDarkMode}
class="hover:bg-surface0 flex w-full items-center gap-3 rounded-lg p-3 transition-all duration-200 ease-in-out hover:scale-105"
aria-label="Toggle dark mode"
>
<Show when={isDark()} fallback={<MoonIcon size={24} />}>
<SunIcon size={24} fill="var(--color-text)" />
</Show>
<span class="text-text font-semibold">
{isDark() ? "Light Mode" : "Dark Mode"}
</span>
</button>
</div>
</div>
</Typewriter>
</nav> </nav>
); );
} }

View File

@@ -199,11 +199,9 @@ export default function ContactPage() {
<div class="flex min-h-screen w-full justify-center"> <div class="flex min-h-screen w-full justify-center">
<div class="w-full max-w-4xl px-4 pt-[20vh]"> <div class="w-full max-w-4xl px-4 pt-[20vh]">
<div class="text-center text-3xl tracking-widest dark:text-white"> <div class="text-center text-3xl tracking-widest">Contact</div>
Contact
</div>
<Show when={viewer() !== "lineage"}> <Show when={viewer() !== "lineage"}>
<div class="mt-4 -mb-4 text-center text-xl tracking-widest dark:text-white"> <div class="mt-4 -mb-4 text-center text-xl tracking-widest">
(for this website or any of my apps...) (for this website or any of my apps...)
</div> </div>
</Show> </Show>
@@ -302,32 +300,6 @@ export default function ContactPage() {
> >
{emailSent() ? "Email Sent!" : error()} {emailSent() ? "Email Sent!" : error()}
</div> </div>
<ul class="icons flex justify-center pt-24 pb-6">
<li>
<A
href="https://github.com/MikeFreno/"
target="_blank"
rel="noreferrer"
class="shaker rounded-full border-zinc-800 dark:border-zinc-300"
>
<span class="m-auto p-2">
<GitHub height={24} width={24} fill={undefined} />
</span>
</A>
</li>
<li>
<A
href="https://www.linkedin.com/in/michael-freno-176001256/"
target="_blank"
rel="noreferrer"
class="shaker rounded-full border-zinc-800 dark:border-zinc-300"
>
<span class="m-auto rounded-md p-2">
<LinkedIn height={24} width={24} fill={undefined} />
</span>
</A>
</li>
</ul>
</div> </div>
</div> </div>
</> </>

View File

@@ -2,8 +2,8 @@ import { Typewriter } from "~/components/Typewriter";
export default function Home() { export default function Home() {
return ( return (
<Typewriter speed={100} delay={1000} keepAlive={2000}> <Typewriter speed={100} keepAlive={2000}>
<main class="text-center mx-auto text-subtext0 p-4"> <main class="text-subtext0 mx-auto p-4 text-center">
{/* fill in a ipsum lorem */} {/* fill in a ipsum lorem */}
ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem
ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem