Initial Commit

This commit is contained in:
Greg Kilmer
2026-07-04 07:42:24 -04:00
commit 71bffad507
77 changed files with 14437 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
/**
* animations.css
* ---------------------------------------------------------------------------
* Keyframes used by hero backgrounds, decorative motion, and ambient effects.
* All animations are disabled by the global prefers-reduced-motion rule in
* global.css, so they are safe to use freely.
*/
/* Slowly drift radial-gradient stops for the hero mesh background. */
@keyframes gradient-shift {
0% {
background-position:
0% 0%,
100% 0%,
50% 100%;
}
50% {
background-position:
100% 100%,
0% 100%,
50% 0%;
}
100% {
background-position:
0% 0%,
100% 0%,
50% 100%;
}
}
/* Soft vertical float for decorative shapes / icons. */
@keyframes float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
/* Slow rotation for ambient rings. */
@keyframes spin-slow {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* Pulsing glow for accent dots / status indicators. */
@keyframes pulse-glow {
0%,
100% {
opacity: 0.5;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.15);
}
}
/* Nav backdrop blur is handled via class toggle, but the entry of the nav uses
* a fade-down so it doesn't pop on first paint. */
@keyframes fade-down {
from {
opacity: 0;
transform: translateY(-12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Utility classes bound to the keyframes above. */
.animate-gradient-shift {
background-size: 200% 200%;
animation: gradient-shift 18s var(--ease-in-out-soft, ease-in-out) infinite;
}
.animate-float {
animation: float 6s var(--ease-in-out-soft, ease-in-out) infinite;
}
.animate-spin-slow {
animation: spin-slow 40s linear infinite;
}
.animate-pulse-glow {
animation: pulse-glow 4s var(--ease-in-out-soft, ease-in-out) infinite;
}
.animate-fade-down {
animation: fade-down 0.5s var(--ease-out-soft, ease-out) both;
}
+293
View File
@@ -0,0 +1,293 @@
/**
* global.css
* ---------------------------------------------------------------------------
* Tailwind v4 entry + global base layer for Greg Kilmer's profile site.
*
* Tailwind v4 uses a CSS-first config. The `@import "tailwindcss"` pulls in the
* engine and the `@theme` block below exposes the project palette as utility
* classes (e.g. `bg-surface`, `text-accent`). Section/light-dark tokens live in
* themes.css and are referenced here via CSS variables.
*
* Import order matters: theme tokens first (so @theme can reference them), then
* Tailwind, then animations, then base.
*/
/* Palette + light/dark + section surface tokens (CSS variables). */
@import "./themes.css";
/* Keyframes (gradient shift, fade-up, blur, float). */
@import "./animations.css";
/* Tailwind engine. In v4 this is a single import. */
@import "tailwindcss";
/* ---------------------------------------------------------------------------
* Theme bridge: expose CSS variables from themes.css to Tailwind utilities.
* Each entry below generates a utility family, e.g. `--color-surface` -> the
* `bg-surface` / `text-surface` / `border-surface` classes.
* The values point at the CSS variables so that changing `data-theme` or the
* section wrapper (`data-surface`) instantly restyles every component.
* ------------------------------------------------------------------------- */
@theme {
/* Brand colors (fixed, do not change with theme). Night-sky mystical set:
* star gold, mystic violet, nebula teal, midnight indigo, star-white. */
--color-amber: #e8b54a; /* star gold */
--color-amber-soft: #f4cd72; /* brighter gold for hovers */
--color-violet: #8b7bd8; /* mystic violet */
--color-violet-deep: #6f5fb6;
--color-teal: #6fb6ad; /* nebula teal */
--color-teal-deep: #4f8c84;
--color-slate: #161b3a; /* deep indigo-slate */
--color-ink: #0b1026; /* midnight indigo */
--color-cream: #f4f1e8; /* star-white */
/* Semantic tokens (resolve per theme/section via variables). */
--color-surface: var(--surface);
--color-surface-raised: var(--surface-raised);
--color-surface-sunken: var(--surface-sunken);
--color-text: var(--text);
--color-text-muted: var(--text-muted);
--color-text-subtle: var(--text-subtle);
--color-border: var(--border);
--color-accent: var(--accent);
--color-accent-contrast: var(--accent-contrast);
--color-secondary: var(--secondary);
/* Typography. */
--font-sans: "Plus Jakarta Sans Variable", "Inter Variable", ui-sans-serif,
system-ui, sans-serif;
--font-body: "Inter Variable", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono Variable", ui-monospace, "SFMono-Regular", monospace;
/* Motion tokens (used across reveal + transition classes). */
--ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
--ease-in-out-soft: cubic-bezier(0.65, 0, 0.35, 1);
}
/* ---------------------------------------------------------------------------
* Base layer: resets, defaults, focus styles.
* ------------------------------------------------------------------------- */
@layer base {
:root {
color-scheme: light dark;
}
html {
scroll-behavior: smooth;
/* Offset so anchor jumps clear the fixed nav. */
scroll-padding-top: 6rem;
-webkit-text-size-adjust: 100%;
}
/* Honor reduced motion globally. */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
}
body {
background-color: var(--color-surface);
color: var(--color-text);
font-family: var(--font-body);
line-height: 1.65;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
min-height: 100dvh;
}
h1,
h2,
h3,
h4 {
font-family: var(--font-sans);
font-weight: 700;
line-height: 1.15;
letter-spacing: -0.02em;
color: var(--color-text);
}
/* Strong typographic rhythm for headings. */
h1 {
font-size: clamp(2.25rem, 5vw + 1rem, 4rem);
}
h2 {
font-size: clamp(1.75rem, 2.5vw + 1rem, 2.5rem);
}
h3 {
font-size: clamp(1.25rem, 1vw + 1rem, 1.5rem);
}
a {
color: var(--color-accent);
text-decoration: none;
transition: color 0.2s var(--ease-out-soft);
}
a:hover {
color: var(--color-amber-soft);
}
/* Visible, on-brand focus ring for keyboard users. */
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
border-radius: 4px;
}
::selection {
background: var(--color-amber);
color: var(--color-ink);
}
/* Better default for images: never overflow, lazy by default in components. */
img {
max-width: 100%;
height: auto;
}
}
/* ---------------------------------------------------------------------------
* Component-ish utilities used across the site. Keeping them here (rather than
* as repeated Tailwind class strings) makes tone adjustments easy.
* ------------------------------------------------------------------------- */
@layer components {
/* Content width container. */
.container-prose {
width: 100%;
max-width: 72rem;
margin-inline: auto;
padding-inline: 1.25rem;
}
@media (min-width: 768px) {
.container-prose {
padding-inline: 2rem;
}
}
/* The recurring "card" surface. */
.card {
background-color: var(--color-surface-raised);
border: 1px solid var(--color-border);
border-radius: 1rem;
padding: 1.5rem;
transition:
transform 0.3s var(--ease-out-soft),
box-shadow 0.3s var(--ease-out-soft),
border-color 0.3s var(--ease-out-soft);
}
.card-interactive:hover {
transform: translateY(-4px);
box-shadow: 0 18px 40px -24px rgba(11, 16, 38, 0.55);
border-color: var(--color-accent);
}
/* Pill / tag. */
.pill {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.25rem 0.7rem;
border-radius: 999px;
font-size: 0.8rem;
font-weight: 500;
background: color-mix(in srgb, var(--color-accent) 15%, transparent);
color: var(--color-text);
border: 1px solid color-mix(in srgb, var(--color-accent) 35%, transparent);
}
/* Primary (amber) and secondary (outlined) CTA buttons. */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
border-radius: 0.6rem;
font-family: var(--font-sans);
font-weight: 600;
font-size: 1rem;
line-height: 1;
cursor: pointer;
transition:
transform 0.2s var(--ease-out-soft),
box-shadow 0.2s var(--ease-out-soft),
background-color 0.2s var(--ease-out-soft),
border-color 0.2s var(--ease-out-soft);
}
.btn:active {
transform: translateY(1px);
}
.btn-primary {
background: var(--color-amber);
color: var(--color-ink);
box-shadow: 0 10px 30px -12px rgba(245, 158, 11, 0.7);
}
.btn-primary:hover {
background: var(--color-amber-soft);
color: var(--color-ink);
transform: translateY(-2px);
}
.btn-secondary {
background: transparent;
color: var(--color-text);
border: 1.5px solid var(--color-border);
}
.btn-secondary:hover {
border-color: var(--color-accent);
color: var(--color-text);
transform: translateY(-2px);
}
}
/* Visually hidden but available to screen readers. */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* ---------------------------------------------------------------------------
* Reveal-on-scroll utility. The Reveal.astro component toggles `.is-visible`.
*
* The `.no-js` guard below is critical: <html> starts with class="no-js"
* (set in BaseLayout.astro) and reveal-init.ts removes it once it has wired up
* the observer. If JS is disabled OR the script fails to load, that class is
* never removed — so this fallback forces reveals visible rather than leaving
* the page blank. This is the safety net against the original invisibility bug.
* ------------------------------------------------------------------------- */
.no-js .reveal {
opacity: 1 !important;
transform: none !important;
}
.reveal {
opacity: 0;
transform: translateY(24px);
transition:
opacity 0.7s var(--ease-out-soft),
transform 0.7s var(--ease-out-soft);
will-change: opacity, transform;
}
.reveal.is-visible {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 1;
transform: none;
transition: none;
}
}
+124
View File
@@ -0,0 +1,124 @@
/**
* themes.css
* ---------------------------------------------------------------------------
* Two-axis theming model for the site:
*
* 1. Global light/dark mode -> set on <html data-theme="light|dark">
* 2. Section surface tone -> set on a wrapper via data-surface:
* data-surface="neutral" Home / About / Contact (deep night-sky)
* data-surface="professional" Professional wing (polished obsidian + gold)
* data-surface="interest" Interests wing (candlelit study under stars)
*
* The semantic tokens (--surface, --text, --accent, ...) are redefined for each
* (data-theme x data-surface) combination below. Components only ever reference
* the semantic tokens, so swapping either axis automatically restyles them.
*
* Palette direction: night-sky mystical — midnight indigo bases, star-gold
* accents, mystic-violet secondary, nebula-teal tertiary. Light mode is
* reinterpreted as a "twilight"/parchment variant so the toggle always works
* without breaking the celestial mood.
*
* `theme-init.ts` (inlined in <head>) sets data-theme before paint to avoid a
* flash of the wrong theme. data-surface is applied by the layout wrappers.
*/
/* ===========================================================================
* DEFAULT / NEUTRAL SURFACE (Home, About, Contact)
* Deep midnight indigo with star-gold accents. The default night sky.
* ======================================================================== */
/* Light + neutral — twilight parchment (warm, low-contrast, still celestial). */
:root,
[data-theme="light"][data-surface="neutral"],
[data-theme="light"][data-surface="neutral"] body {
--surface: #f3eee0; /* aged parchment / star-map paper */
--surface-raised: #fbf7ec;
--surface-sunken: #e7dfca;
--text: #1c1830; /* deep indigo ink */
--text-muted: #4a445e;
--text-subtle: #6b6582;
--border: #d8cdb0;
--accent: #9a6b1a; /* deeper gold for AA on light */
--accent-contrast: #fbf7ec;
--secondary: #6f5fb6; /* mystic violet */
}
/* Dark + neutral — the canonical night sky. */
[data-theme="dark"][data-surface="neutral"],
[data-theme="dark"][data-surface="neutral"] body {
--surface: #0b1026; /* midnight indigo */
--surface-raised: #161b3a; /* indigo panel */
--surface-sunken: #070a1a; /* near-black void */
--text: #f4f1e8; /* star-white */
--text-muted: #c7c3df;
--text-subtle: #8e89b5;
--border: #2c3160;
--accent: #e8b54a; /* star gold */
--accent-contrast: #0b1026;
--secondary: #8b7bd8; /* mystic violet */
}
/* ===========================================================================
* PROFESSIONAL SURFACE
* Polished obsidian: deepest indigo with gold + subtle violet. Confident,
* precise, the "polished wing" from the spec — now with a starlit edge.
* ======================================================================== */
[data-surface="professional"] {
--surface: #0a0e22;
--surface-raised: #141a38;
--surface-sunken: #05081a;
--text: #f4f1e8;
--text-muted: #c5c0db;
--text-subtle: #8782ad;
--border: #292e57;
--accent: #e8b54a;
--accent-contrast: #0a0e22;
--secondary: #8b7bd8;
}
/* Light professional: keeps a dignified deep tone, slightly lifted so the
* global toggle is never jarring next to a parchment Home page. */
[data-theme="light"][data-surface="professional"] {
--surface: #1a1f44;
--surface-raised: #252b54;
--surface-sunken: #141838;
--text: #f4f1e8;
--text-muted: #d4d0ec;
--text-subtle: #aaa5cd;
--border: #3a3f72;
}
/* ===========================================================================
* INTEREST SURFACE
* Candlelit study under a starfield: warm amber-gold bases with deep indigo
* text by default (light), flipping to a warm brown-indigo at night. The
* "fun wing" stays inviting, now with an esoteric warmth.
* ======================================================================== */
[data-theme="light"][data-surface="interest"],
[data-surface="interest"] {
--surface: #f6ecd2; /* warm candlelight parchment */
--surface-raised: #fdf5dc;
--surface-sunken: #ecdcb4;
--text: #241a2e; /* plum-indigo ink */
--text-muted: #5c4a3a;
--text-subtle: #826a52;
--border: #ddc79a;
--accent: #9a6b1a; /* deeper gold for AA */
--accent-contrast: #fdf5dc;
--secondary: #6f5fb6; /* mystic violet */
}
[data-theme="dark"][data-surface="interest"] {
--surface: #1a1320; /* warm brown-indigo night */
--surface-raised: #261c2e;
--surface-sunken: #120d18;
--text: #f6ecd2; /* warm starlight */
--text-muted: #ddcbb0;
--text-subtle: #b09a82;
--border: #3a2c1f;
--accent: #e8b54a; /* star gold */
--accent-contrast: #1a1320;
--secondary: #b09a78; /* warm gold-bronze secondary at night */
}