diff --git a/app/about/page.tsx b/app/about/page.tsx index c23d35e..a884a01 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -53,7 +53,7 @@ export default function About() { {/* Bottom border accent with gradient */} -
+
diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index a67a406..d66d880 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -53,7 +53,7 @@ export default async function BlogPost({ params }: { params: Promise<{ slug: str href={post.mediumUrl} target="_blank" rel="noopener noreferrer" - className="inline-flex items-center gap-2 px-4 py-2 bg-black text-white rounded-lg hover:bg-gray-800 transition-colors text-sm font-semibold" + className="inline-flex items-center gap-2 px-4 py-2 bg-accent-orange text-on-accent-orange rounded-lg hover:bg-accent-orange-hover transition-colors text-sm font-semibold" > Read on Medium @@ -103,7 +103,7 @@ export default async function BlogPost({ params }: { params: Promise<{ slug: str href={post.mediumUrl} target="_blank" rel="noopener noreferrer" - className="inline-flex items-center gap-2 px-6 py-3 bg-black text-white rounded-lg hover:bg-gray-800 transition-colors font-semibold" + className="inline-flex items-center gap-2 px-6 py-3 bg-accent-orange text-on-accent-orange rounded-lg hover:bg-accent-orange-hover transition-colors font-semibold" > Continue reading on Medium diff --git a/app/blog/page.tsx b/app/blog/page.tsx index 134f40d..28062c5 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -73,7 +73,7 @@ export default async function Blog() { title="Read on Medium" aria-label="Read on Medium" > - + )} diff --git a/app/contact/page.tsx b/app/contact/page.tsx index 5e41c6c..f433b16 100644 --- a/app/contact/page.tsx +++ b/app/contact/page.tsx @@ -83,8 +83,8 @@ export default function Contact() {
-
- +
+

LinkedIn

diff --git a/app/globals.css b/app/globals.css index 11e61fc..4f245ce 100644 --- a/app/globals.css +++ b/app/globals.css @@ -20,18 +20,18 @@ theme, so dark text is always the right call for it. */ --on-accent: #ffffff; --on-accent-orange: #1a1a1a; - /* Fixed (non-theme-dependent) blue used for the small number of buttons - that are deliberately "the blue button" as opposed to "the theme's - highlight color" (e.g. distinguishing two CTAs from each other on the - same page). Unlike --accent, this never changes with the theme - - Xanga's --accent is bright orange, so reusing --accent here would make - a "blue" button render orange and become indistinguishable from an - orange one. Deliberately not overridden in the dark/Xanga blocks below - so every theme gets the same value. Dark enough that white text always - has sufficient contrast on it. */ - --accent-blue: #1170aa; - --accent-blue-hover: #0d5a88; - --on-accent-blue: #ffffff; + /* Shared "primary CTA" color, used by every solid gradient button + (.gradient-btn) so all of them agree on one hue per theme instead of + mixing blue and orange buttons on the same page. Light mode's --accent + is a dark, saturated blue that reads well with white text, so primary + buttons stay blue here; dark and Xanga both lean on the bright orange + instead (see overrides below), since dark mode's --accent is a lighter + blue that needs a dark-text workaround, and Xanga's --accent already + *is* the orange. */ + --btn-primary-from: var(--accent); + --btn-primary-to: var(--accent-hover); + --btn-primary-text: var(--on-accent); + --btn-primary-shadow-rgb: 17, 112, 170; --border: #c8d0d9; /* Very Light Gray from palette */ --link: #1170aa; /* Dark Blue for links */ --link-hover: #fc7d0b; /* Bright Orange for link hover */ @@ -58,6 +58,12 @@ contrast - use dark text instead. */ --on-accent: #0b1220; --on-accent-orange: #1a1a1a; + /* Primary buttons switch to orange in dark mode - see the --btn-primary-* + comment in :root above. */ + --btn-primary-from: var(--accent-orange); + --btn-primary-to: var(--accent-orange-hover); + --btn-primary-text: var(--on-accent-orange); + --btn-primary-shadow-rgb: 252, 125, 11; --border: #57606c; /* Dark Gray from palette */ --link: #5fa2ce; /* Medium Blue for links */ --link-hover: #fc7d0b; /* Bright Orange for link hover */ @@ -86,6 +92,12 @@ needs dark text too. */ --on-accent: #0a0a0a; --on-accent-orange: #1a1a1a; + /* Primary buttons stay orange in Xanga too - it's already the theme's + dominant highlight color, same rationale as dark mode above. */ + --btn-primary-from: var(--accent); + --btn-primary-to: var(--accent-hover); + --btn-primary-text: var(--on-accent-orange); + --btn-primary-shadow-rgb: 252, 125, 11; --border: #fc7d0b; /* Tableau Bright Orange */ --border-navy: #1170aa; /* Tableau Dark Blue */ --link: #5fa2ce; /* Tableau Medium Blue */ @@ -270,15 +282,20 @@ html[data-theme="xanga"] body header nav a:focus-visible * { on top of that background. This is deliberately not scoped to "header" only: the mobile nav menu is a sibling of
{/* Bottom border accent with gradient */} -
+
@@ -238,7 +238,7 @@ export default function Home() {

diff --git a/components/projects/ProjectArtifact.tsx b/components/projects/ProjectArtifact.tsx index 5d588f9..9b6fbca 100644 --- a/components/projects/ProjectArtifact.tsx +++ b/components/projects/ProjectArtifact.tsx @@ -13,11 +13,14 @@ const typeLabels: Record = { example: 'Example', } +// Uses theme-aware tokens (instead of hardcoded Tableau hex values) so these +// badges shift with the active theme like everything else, rather than +// staying frozen at their light-mode colors in dark/Xanga. const typeColors: Record = { - artifact: 'bg-[#1170aa]/10 text-[#1170aa] border-[#1170aa]/20', // Tableau Dark Blue - deliverable: 'bg-[#5fa2ce]/10 text-[#5fa2ce] border-[#5fa2ce]/20', // Tableau Medium Blue - phase: 'bg-[#57606c]/10 text-[#57606c] border-[#57606c]/20', // Tableau Dark Gray - example: 'bg-[#fc7d0b]/10 text-[#fc7d0b] border-[#fc7d0b]/20', // Tableau Bright Orange + artifact: 'bg-accent/10 text-accent border-accent/20', + deliverable: 'bg-accent-hover/10 text-accent-hover border-accent-hover/20', + phase: 'bg-text-secondary/10 text-text-secondary border-text-secondary/20', + example: 'bg-accent-orange/10 text-accent-orange border-accent-orange/20', } export function ProjectArtifact({ artifact }: ProjectArtifactProps) { diff --git a/tailwind.config.ts b/tailwind.config.ts index 7291029..d74cb06 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,5 +1,20 @@ import type { Config } from 'tailwindcss' +// Our theme colors are plain CSS custom properties (e.g. `var(--accent)`) +// rather than static hex/rgb values, since their actual value changes per +// theme (light/dark/xanga). Tailwind can only generate opacity-modifier +// utilities (e.g. `bg-accent/10`, `border-accent-orange/40`) for colors it +// can decompose into RGB channels at build time - a bare `var(--x)` doesn't +// qualify, so those classes were silently generating no CSS at all (fully +// transparent, invisible borders) anywhere they were used. Wrapping each +// color in `color-mix()` with the `` placeholder lets Tailwind +// substitute the requested opacity at build time while still resolving the +// actual color from the CSS variable at runtime. See: +// https://github.com/tailwindlabs/tailwindcss/discussions/12824 +function withOpacity(cssVariable: string) { + return `color-mix(in srgb, var(${cssVariable}) calc( * 100%), transparent)` +} + const config: Config = { content: [ './pages/**/*.{js,ts,jsx,tsx,mdx}', @@ -9,22 +24,19 @@ const config: Config = { theme: { extend: { colors: { - 'bg-primary': 'var(--bg-primary)', - 'bg-secondary': 'var(--bg-secondary)', - 'text-primary': 'var(--text-primary)', - 'text-secondary': 'var(--text-secondary)', - 'accent': 'var(--accent)', - 'accent-hover': 'var(--accent-hover)', - 'accent-orange': 'var(--accent-orange)', - 'accent-orange-hover': 'var(--accent-orange-hover)', - 'accent-blue': 'var(--accent-blue)', - 'accent-blue-hover': 'var(--accent-blue-hover)', - 'on-accent': 'var(--on-accent)', - 'on-accent-orange': 'var(--on-accent-orange)', - 'on-accent-blue': 'var(--on-accent-blue)', - 'border': 'var(--border)', - 'link': 'var(--link)', - 'link-hover': 'var(--link-hover)', + 'bg-primary': withOpacity('--bg-primary'), + 'bg-secondary': withOpacity('--bg-secondary'), + 'text-primary': withOpacity('--text-primary'), + 'text-secondary': withOpacity('--text-secondary'), + 'accent': withOpacity('--accent'), + 'accent-hover': withOpacity('--accent-hover'), + 'accent-orange': withOpacity('--accent-orange'), + 'accent-orange-hover': withOpacity('--accent-orange-hover'), + 'on-accent': withOpacity('--on-accent'), + 'on-accent-orange': withOpacity('--on-accent-orange'), + 'border': withOpacity('--border'), + 'link': withOpacity('--link'), + 'link-hover': withOpacity('--link-hover'), }, }, },