/* ============================================================================
AaronMHampton.com UI Kit — shared components, data, atoms
Exports to window for cross-file use (Babel scopes are per-script).
========================================================================== */
/* ---- Icon (Lucide, imperatively mounted so React never fights it) --------- */
function Icon({ name, size = 18, stroke = 1.75, className = "" }) {
const ref = React.useRef(null);
React.useEffect(() => {
const el = ref.current;
if (!el || !window.lucide) return;
el.innerHTML = "";
const pascal = name.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
const node =
window.lucide.icons && window.lucide.icons[pascal] || window.lucide[pascal];
if (node && window.lucide.createElement) {
const svg = window.lucide.createElement(node);
svg.setAttribute("width", size);
svg.setAttribute("height", size);
svg.setAttribute("stroke-width", stroke);
el.appendChild(svg);
}
});
return (
);
}
/* ---- Atoms ---------------------------------------------------------------- */
function Crosshair() {return ;}
function Eyebrow({ idx, children }) {
return (
{idx && {idx} —}
{children}
);
}
function Button({ variant = "primary", children, onClick, as = "button", href }) {
const cls = "btn btn-" + variant;
if (as === "a") return {children};
return ;
}
function Tag({ children, on, onClick }) {
return ;
}
function Placeholder({ label = "Image", style }) {
return (
{label}
);
}
/* ---- Brand / Logo --------------------------------------------------------- */
function BrandMark() {
return (
AMH);
}
function Brand({ onClick }) {
return (
Aaron M. Hampton
);
}
/* ---- Theme toggle (controlled by App) ------------------------------------- */
function ThemeToggle({ theme, onToggle }) {
return (
);
}
/* ---- Search trigger — the front door to ⌘K global search ------------------ */
function SearchTrigger({ onClick, compact }) {
const mod = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || "") ? "\u2318" : "Ctrl";
if (compact) {
return (
);
}
return (
);
}
/* ---- Nav data ------------------------------------------------------------- */
const NAV = [
{ id: "writing", label: "Writing" },
{ id: "projects", label: "Projects" },
{ id: "photos", label: "Photos" },
{ id: "about", label: "About" }];
/* ---- Top bar -------------------------------------------------------------- */
function TopBar({ route, go, theme, onToggleTheme, onSearch }) {
const [open, setOpen] = React.useState(false);
const navTo = (id) => {go(id);setOpen(false);};
return (
navTo("home")} />
);
}
/* ---- Footer --------------------------------------------------------------- */
function Footer({ go }) {
return (
);
}
/* ---- Content data --------------------------------------------------------- */
const POSTS = [
{ id: "p1", num: "014", cat: "Essay", title: "Attention is the only scarce resource I have left",
desc: "A short argument for treating focus as capital — and three habits I changed once I believed it.",
date: "2026-05-12", read: "7 min", tags: ["philosophy", "technology"] },
{ id: "p2", num: "013", cat: "Note", title: "What the printing press actually changed",
desc: "Less about literacy than we think, more about the economics of being wrong in public.",
date: "2026-04-28", read: "5 min", tags: ["history"] },
{ id: "p3", num: "012", cat: "Essay", title: "A bias toward the legible",
desc: "Why systems drift toward what's measurable, and what gets quietly deleted on the way.",
date: "2026-04-09", read: "9 min", tags: ["politics", "science"] },
{ id: "p4", num: "011", cat: "Note", title: "Tools are arguments about how to live",
desc: "Every interface encodes a theory of the person using it. Most of them are wrong about you.",
date: "2026-03-22", read: "4 min", tags: ["technology", "philosophy"] },
{ id: "p5", num: "010", cat: "Essay", title: "On keeping a slow archive",
desc: "I write to remember what I actually thought, not what I wish I had. Here's the system.",
date: "2026-03-01", read: "6 min", tags: ["science"] }];
const PROJECTS = [
{ id: "j1", title: "Marginalia", tagline: "A reader that pulls your notes into the margin where they belong.", lang: "TypeScript", stars: "1.2k", year: "2026" },
{ id: "j2", title: "Plotpaper", tagline: "Minimal charting library for thinking in public. Hairlines, no chartjunk.", lang: "Rust · WASM", stars: "640", year: "2025" },
{ id: "j3", title: "Slowfeed", tagline: "An RSS reader that refuses to show you anything more than once a day.", lang: "Go", stars: "2.1k", year: "2025" },
{ id: "j4", title: "Index", tagline: "The engine behind this site. Markdown in, quiet HTML out.", lang: "TypeScript", stars: "310", year: "2024" }];
const PHOTOS = [
{ id: "ph1", cap: "Coastal range", meta: "35mm", ar: "3/4" },
{ id: "ph2", cap: "Reading desk", meta: "50mm", ar: "1/1" },
{ id: "ph3", cap: "Fog, early", meta: "35mm", ar: "4/5" },
{ id: "ph4", cap: "Library stacks", meta: "28mm", ar: "1/1" },
{ id: "ph5", cap: "Long exposure", meta: "35mm", ar: "3/4" },
{ id: "ph6", cap: "Train window", meta: "50mm", ar: "4/5" },
{ id: "ph7", cap: "Workbench", meta: "35mm", ar: "1/1" },
{ id: "ph8", cap: "Dusk field", meta: "28mm", ar: "3/4" },
{ id: "ph9", cap: "Notebook study", meta: "50mm", ar: "4/5" }];
Object.assign(window, {
Icon, Crosshair, Eyebrow, Button, Tag, Placeholder,
BrandMark, Brand, ThemeToggle, SearchTrigger, TopBar, Footer,
NAV, POSTS, PROJECTS, PHOTOS
});