diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/Dockerfile b/Dockerfile index 5853810..6513b95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,9 @@ FROM nginx:1.27-alpine COPY nginx.conf /etc/nginx/conf.d/default.conf WORKDIR /usr/share/nginx/html -COPY index.html standalone.html styles.css data.js projects-data.js components.jsx tweaks-panel.jsx llms.txt ./ +COPY index.html styles.css data.js projects-data.js llms.txt robots.txt sitemap.xml ./ +COPY app.min.js components.min.js tweaks-panel.min.js ./ +COPY vendor ./vendor COPY images ./images EXPOSE 43036 diff --git a/README.md b/README.md index 5b4b878..481672f 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,20 @@ Source for [wilddragon.net](https://www.wilddragon.net/) — Zachary Gaetano's p ## Stack -Single-page React app, no build step. React + Babel are loaded from CDN; the app is rendered from `.jsx` files at runtime. +Single-page React app. Sources are `.jsx`; `./build.sh` minifies them with esbuild into `*.min.js`, which `index.html` loads alongside vendored React (`vendor/`). Rebuild after editing any `.jsx`. ``` index.html — entry point (loads CSS + JS + components, mounts App) styles.css — all styles data.js — site copy (about, stats, services, tech stack, on-set, etc.) projects-data.js — case study content for each project +app.jsx — App root: tweaks state, hash router, mounts sections components.jsx — every section component + project overlay tweaks-panel.jsx — in-page Tweaks panel (accent presets, fonts, density, toggles) images/ — photos, client logos, partner logos +vendor/ — React 18 production UMD builds (no CDN) +robots.txt, sitemap.xml +build.sh — esbuild: *.jsx -> *.min.js llms.txt — machine-readable site summary for LLM crawlers standalone.html — fully self-contained single-file build (all images base64'd in) ``` diff --git a/app.jsx b/app.jsx new file mode 100644 index 0000000..0ba85d4 --- /dev/null +++ b/app.jsx @@ -0,0 +1,135 @@ +const TWEAKS_DEFAULTS = /*EDITMODE-BEGIN*/{ + "accentPreset": "blue", + "accent": "#3b82f6", + "accentLight": "#60a5fa", + "primary": "#0a0a0a", + "headingFont": "Inter", + "monoFont": "JetBrains Mono", + "imageTreatment": "default", + "density": "comfortable", + "marquee": true, + "cursorGlow": true, + "showPartners": true, + "showStats": true, + "showServices": true, + "showTech": true, + "showOnSet": true, + "ctaLabel": "View Projects" +}/*EDITMODE-END*/; + +const FONT_OPTIONS = ["Inter","Manrope","Space Grotesk","DM Sans","Outfit","Plus Jakarta Sans","IBM Plex Sans","Geist","Fraunces"]; +const MONO_OPTIONS = ["JetBrains Mono","IBM Plex Mono","Space Mono","Roboto Mono","Fira Code","Geist Mono"]; + +const ACCENT_PRESETS = { + blue: { accent:"#3b82f6", accentLight:"#60a5fa", label:"Blue" }, + ember: { accent:"#e25822", accentLight:"#f3895a", label:"Ember" }, + amber: { accent:"#f59e0b", accentLight:"#fbbf24", label:"Amber" }, + ruby: { accent:"#dc2626", accentLight:"#f87171", label:"Ruby" }, + mono: { accent:"#e5e5e5", accentLight:"#ffffff", label:"Mono" }, +}; + +function loadFont(family) { + const id = "wd-font-" + family.replace(/\s/g,"-"); + if (document.getElementById(id)) return; + const l = document.createElement("link"); + l.id = id; l.rel = "stylesheet"; + l.href = `https://fonts.googleapis.com/css2?family=${family.replace(/\s/g,"+")}:wght@300;400;500;600;700&display=swap`; + document.head.appendChild(l); +} + +function App() { + const [tweaks, setTweak] = useTweaks(TWEAKS_DEFAULTS); + const [openSlug, setOpenSlug] = React.useState(() => { + const m = location.hash.match(/#\/projects\/([\w-]+)/); + return m ? m[1] : null; + }); + + React.useEffect(() => { + if (openSlug) location.hash = `#/projects/${openSlug}`; + else if (location.hash.startsWith("#/projects/")) location.hash = ""; + }, [openSlug]); + + React.useEffect(() => { + loadFont(tweaks.headingFont); + loadFont(tweaks.monoFont); + const r = document.documentElement.style; + r.setProperty("--color-accent", tweaks.accent); + r.setProperty("--color-accent-light", tweaks.accentLight); + r.setProperty("--color-primary", tweaks.primary); + r.setProperty("--font-inter", `"${tweaks.headingFont}"`); + r.setProperty("--font-mono-stack", `"${tweaks.monoFont}"`); + }, [tweaks]); + + // Image treatment via class on body + React.useEffect(() => { + document.body.dataset.imgTreat = tweaks.imageTreatment; + document.body.dataset.density = tweaks.density; + }, [tweaks.imageTreatment, tweaks.density]); + + const onAccentPreset = (key) => { + const p = ACCENT_PRESETS[key]; + if (!p) return; + setTweak({ accentPreset: key, accent: p.accent, accentLight: p.accentLight }); + }; + + return ( + + + {tweaks.cursorGlow && } + + + + {tweaks.showPartners && } + + {tweaks.showStats && } + {tweaks.showServices && } + + {tweaks.showTech && } + {tweaks.showOnSet && } + +