Configure Vercel deployment and preserve deployed theme support
This commit is contained in:
@@ -4,6 +4,8 @@ import Icon from "./Icon";
|
||||
import AttachmentList from "./AttachmentList";
|
||||
|
||||
const imageTypes = ["image/jpeg", "image/png", "image/webp", "image/gif"];
|
||||
const maxUploadMB = Number(import.meta.env.VITE_MAX_UPLOAD_MB ?? 20);
|
||||
const uploadLimitMB = (purpose) => Math.min(purpose === "banner" ? 8 : 20, maxUploadMB);
|
||||
const isImage = (file) => imageTypes.includes(file.type) || (!file.type && /\.(jpe?g|png|webp|gif)$/i.test(file.name));
|
||||
const fileSize = (size) => size < 1024 * 1024 ? `${Math.max(1, Math.round(size / 1024))} KB` : `${(size / 1024 / 1024).toFixed(1)} MB`;
|
||||
|
||||
@@ -27,7 +29,7 @@ function Dropzone({ purpose, disabled, onFiles, hasBanner }) {
|
||||
<button type="button" className="upload-browse" disabled={disabled} aria-describedby={hintId} onClick={() => input.current.click()}>
|
||||
<Icon name="plus" size={16} /> {banner ? hasBanner ? "Replace banner" : "Choose banner" : "Browse files"}
|
||||
</button>
|
||||
<small id={hintId}>{banner ? "JPG, PNG, WebP or GIF · Up to 8 MB" : "Up to 10 files · 20 MB each"}</small>
|
||||
<small id={hintId}>{banner ? `JPG, PNG, WebP or GIF · Up to ${uploadLimitMB(purpose)} MB` : `Up to 10 files · ${uploadLimitMB(purpose)} MB each`}</small>
|
||||
<input ref={input} className="upload-input" type="file" tabIndex={-1}
|
||||
aria-label={banner ? "Banner image" : "Additional photos & files"}
|
||||
accept={banner ? "image/jpeg,image/png,image/webp,image/gif" : undefined}
|
||||
@@ -106,9 +108,9 @@ export default function ArticleUploads({ banner, attachments, busy, title, subti
|
||||
const additions = [];
|
||||
const existing = [...attachments, ...queue.current.filter((task) => task.purpose === "attachment").map((task) => task.file)];
|
||||
for (const file of files) {
|
||||
const limit = (purpose === "banner" ? 8 : 20) * 1024 * 1024;
|
||||
const limit = uploadLimitMB(purpose) * 1024 * 1024;
|
||||
if (!file.size) { errors.push(`${file.name}: this file is empty.`); continue; }
|
||||
if (file.size > limit) { errors.push(`${file.name}: exceeds the ${purpose === "banner" ? 8 : 20} MB limit.`); continue; }
|
||||
if (file.size > limit) { errors.push(`${file.name}: exceeds the ${uploadLimitMB(purpose)} MB limit.`); continue; }
|
||||
if (purpose === "banner" && !isImage(file)) { errors.push(`${file.name}: choose a JPG, PNG, WebP, or GIF image.`); continue; }
|
||||
if (purpose === "attachment" && existing.some((item) => item.name === file.name && item.size === file.size)) {
|
||||
errors.push(`${file.name}: already added.`); continue;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const paths = {
|
||||
sun: <><circle cx="12" cy="12" r="4" /><path d="M12 2v2m0 16v2M2 12h2m16 0h2M4.93 4.93l1.42 1.42m11.3 11.3 1.42 1.42M4.93 19.07l1.42-1.42m11.3-11.3 1.42-1.42" /></>,
|
||||
moon: <path d="M20.9 13.1A9 9 0 0 1 10.9 3.1a9 9 0 1 0 10 10Z" />,
|
||||
grip: <path d="M9 5h.01M15 5h.01M9 12h.01M15 12h.01M9 19h.01M15 19h.01" strokeWidth="3" />,
|
||||
upload: <path d="M12 16V3m-5 5 5-5 5 5M4 16v5h16v-5" />,
|
||||
photo: <><rect x="3" y="3" width="18" height="18" rx="3" /><circle cx="8" cy="8" r="1.5" /><path d="m3 17 6-6 4 4 3-3 5 5" /></>,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useId, useState } from "react";
|
||||
import { NavLink, useLocation } from "react-router-dom";
|
||||
import Icon from "./Icon";
|
||||
import { useAdmin } from "./AdminSession";
|
||||
import ThemeToggle from "./ThemeToggle";
|
||||
|
||||
const navItems = [
|
||||
{ label: "About", to: "/" },
|
||||
@@ -55,6 +56,7 @@ function Header({ name }) {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ThemeToggle />
|
||||
<button type="button" className={`icon-button header-signin ${isAdmin ? "is-admin" : ""}`}
|
||||
aria-label={isAdmin ? "Admin account" : "Admin sign in"} title={isAdmin ? "Admin account" : "Admin sign in"}
|
||||
aria-haspopup="dialog" disabled={checking} onClick={openSignIn}>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Icon from "./Icon";
|
||||
|
||||
const STORAGE_KEY = "portfolio-theme";
|
||||
const isTheme = (value) => value === "light" || value === "dark";
|
||||
|
||||
function storedTheme() {
|
||||
try {
|
||||
const value = localStorage.getItem(STORAGE_KEY);
|
||||
return isTheme(value) ? value : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default function ThemeToggle() {
|
||||
const [preference, setPreference] = useState(storedTheme);
|
||||
const [systemDark, setSystemDark] = useState(() => window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
const theme = preference ?? (systemDark ? "dark" : "light");
|
||||
const nextTheme = theme === "dark" ? "light" : "dark";
|
||||
|
||||
useEffect(() => {
|
||||
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
const onSystemChange = (event) => setSystemDark(event.matches);
|
||||
const onStorageChange = (event) => {
|
||||
if (event.key === STORAGE_KEY || event.key === null) {
|
||||
setPreference(isTheme(event.newValue) ? event.newValue : null);
|
||||
}
|
||||
};
|
||||
media.addEventListener("change", onSystemChange);
|
||||
window.addEventListener("storage", onStorageChange);
|
||||
return () => {
|
||||
media.removeEventListener("change", onSystemChange);
|
||||
window.removeEventListener("storage", onStorageChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.dataset.theme = theme;
|
||||
document.documentElement.style.colorScheme = theme;
|
||||
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", theme === "dark" ? "#141d19" : "#f7f8f4");
|
||||
}, [theme]);
|
||||
|
||||
function toggle() {
|
||||
setPreference(nextTheme);
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, nextTheme);
|
||||
} catch {
|
||||
// Keep the in-memory preference for this visit.
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button type="button" className="icon-button theme-toggle" onClick={toggle}
|
||||
aria-label={`Switch to ${nextTheme} mode`} title={`Switch to ${nextTheme} mode`}>
|
||||
<Icon name={nextTheme === "dark" ? "moon" : "sun"} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { createRoot } from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import App from "./App";
|
||||
import "./styles.css";
|
||||
import "./theme.css";
|
||||
|
||||
createRoot(document.getElementById("root")).render(
|
||||
<StrictMode>
|
||||
@@ -11,4 +12,3 @@ createRoot(document.getElementById("root")).render(
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
);
|
||||
|
||||
|
||||
+51
-44
@@ -1,4 +1,10 @@
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--surface-rgb: 255, 255, 255;
|
||||
--accent-fill: #2f5147;
|
||||
--accent-fill-hover: #27463d;
|
||||
--glow-mint: rgba(225, 238, 233, 0.8);
|
||||
--glow-lavender: rgba(235, 231, 243, 0.7);
|
||||
--ink: #24342f;
|
||||
--ink-soft: #52615c;
|
||||
--ink-faint: #77837f;
|
||||
@@ -46,8 +52,8 @@ body {
|
||||
overflow-x: clip;
|
||||
color: var(--ink);
|
||||
background:
|
||||
radial-gradient(circle at 8% 3%, rgba(225, 238, 233, 0.8), transparent 24rem),
|
||||
radial-gradient(circle at 93% 16%, rgba(235, 231, 243, 0.7), transparent 26rem),
|
||||
radial-gradient(circle at 8% 3%, var(--glow-mint), transparent 24rem),
|
||||
radial-gradient(circle at 93% 16%, var(--glow-lavender), transparent 26rem),
|
||||
var(--paper);
|
||||
}
|
||||
|
||||
@@ -118,7 +124,7 @@ button {
|
||||
padding: 10px 16px;
|
||||
border-radius: 12px;
|
||||
color: white;
|
||||
background: var(--sage-deep);
|
||||
background: var(--accent-fill);
|
||||
transform: translateY(-150%);
|
||||
transition: transform 160ms ease;
|
||||
}
|
||||
@@ -157,7 +163,7 @@ button {
|
||||
place-items: center;
|
||||
border-radius: 15px;
|
||||
color: white;
|
||||
background: var(--sage-deep);
|
||||
background: var(--accent-fill);
|
||||
box-shadow: 0 8px 18px rgba(47, 81, 71, 0.2);
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.08em;
|
||||
@@ -174,14 +180,14 @@ button {
|
||||
padding: 5px;
|
||||
border: 0;
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.67);
|
||||
background: rgba(var(--surface-rgb), 0.67);
|
||||
box-shadow: 0 8px 28px rgba(46, 65, 58, 0.05);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
/* Only navigation is pinned. Identity and account controls stay in normal flow. */
|
||||
.nav-positioner { position: fixed; top: var(--header-top); inset-inline: 0; pointer-events: none; }
|
||||
.nav-positioner__inner { position: relative; display: flex; align-items: center; justify-content: flex-end; min-height: 58px; padding-right: 56px; }
|
||||
.nav-positioner__inner { position: relative; display: flex; align-items: center; justify-content: flex-end; min-height: 58px; padding-right: 112px; }
|
||||
.nav-positioner .nav-shell, .nav-positioner .menu-button { pointer-events: auto; }
|
||||
@media (min-width: 721px) and (max-width: 900px) { .brand-name { display: none; } }
|
||||
|
||||
@@ -200,7 +206,7 @@ button {
|
||||
|
||||
.nav-link.is-active {
|
||||
color: white;
|
||||
background: var(--sage-deep);
|
||||
background: var(--accent-fill);
|
||||
}
|
||||
|
||||
.menu-button {
|
||||
@@ -286,12 +292,12 @@ button {
|
||||
|
||||
.button--primary {
|
||||
color: white;
|
||||
background: var(--sage-deep);
|
||||
background: var(--accent-fill);
|
||||
box-shadow: 0 12px 26px rgba(47, 81, 71, 0.2);
|
||||
}
|
||||
|
||||
.button--primary:hover {
|
||||
background: #27463d;
|
||||
background: var(--accent-fill-hover);
|
||||
box-shadow: 0 15px 31px rgba(47, 81, 71, 0.24);
|
||||
}
|
||||
|
||||
@@ -304,11 +310,11 @@ button {
|
||||
.button--quiet {
|
||||
border: 1px solid var(--line);
|
||||
color: var(--ink);
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
background: rgba(var(--surface-rgb), 0.6);
|
||||
}
|
||||
|
||||
.button--quiet:hover {
|
||||
background: white;
|
||||
background: var(--surface-solid);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
@@ -404,7 +410,7 @@ button {
|
||||
overflow: hidden;
|
||||
border: 12px solid rgba(255, 255, 255, 0.88);
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
background: var(--surface-solid);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
@@ -439,7 +445,7 @@ button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid rgba(54, 78, 70, 0.08);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
background: rgba(var(--surface-rgb), 0.9);
|
||||
box-shadow: 0 18px 46px rgba(46, 65, 58, 0.12);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
@@ -522,7 +528,7 @@ button {
|
||||
padding: 17px 19px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
background: rgba(var(--surface-rgb), 0.55);
|
||||
}
|
||||
|
||||
.heading-stat strong {
|
||||
@@ -658,13 +664,13 @@ button {
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
border-radius: var(--radius-lg);
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
background: rgba(var(--surface-rgb), 0.72);
|
||||
box-shadow: 0 10px 38px rgba(46, 65, 58, 0.04);
|
||||
transition: box-shadow 180ms ease, background 180ms ease, transform 180ms ease;
|
||||
}
|
||||
|
||||
.timeline-card:hover {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
background: rgba(var(--surface-rgb), 0.9);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
@@ -857,7 +863,7 @@ button {
|
||||
border: 1px solid rgba(54, 78, 70, 0.07);
|
||||
border-radius: 999px;
|
||||
color: var(--ink-soft);
|
||||
background: rgba(255, 255, 255, 0.62);
|
||||
background: rgba(var(--surface-rgb), 0.62);
|
||||
font-size: 10px;
|
||||
font-weight: 660;
|
||||
}
|
||||
@@ -882,7 +888,7 @@ button {
|
||||
padding: 25px 28px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-lg);
|
||||
background: rgba(255, 255, 255, 0.58);
|
||||
background: rgba(var(--surface-rgb), 0.58);
|
||||
}
|
||||
|
||||
.principles-panel .eyebrow {
|
||||
@@ -991,7 +997,7 @@ button {
|
||||
}
|
||||
|
||||
.skill-tags span {
|
||||
background: rgba(255, 255, 255, 0.48);
|
||||
background: rgba(var(--surface-rgb), 0.48);
|
||||
}
|
||||
|
||||
.issue-count {
|
||||
@@ -1031,7 +1037,7 @@ button {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 15px;
|
||||
color: var(--ink-faint);
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
background: rgba(var(--surface-rgb), 0.65);
|
||||
}
|
||||
|
||||
.search-box:focus-within {
|
||||
@@ -1081,7 +1087,7 @@ button {
|
||||
.filter-row button.is-active {
|
||||
border-color: transparent;
|
||||
color: var(--ink);
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
background: rgba(var(--surface-rgb), 0.72);
|
||||
}
|
||||
|
||||
.journal-filters { flex: 1; min-width: 0; }
|
||||
@@ -1091,13 +1097,13 @@ button {
|
||||
.filter-row .others-filter { display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--line); }
|
||||
.others-filter svg { flex-shrink: 0; transition: transform 150ms ease; }
|
||||
.others-filter[aria-expanded="true"] svg { transform: rotate(180deg); }
|
||||
.custom-topic-filters { margin-top: 12px; padding: 15px; border: 1px solid var(--line); border-radius: 16px; background: rgba(255, 255, 255, 0.45); }
|
||||
.custom-topic-filters { margin-top: 12px; padding: 15px; border: 1px solid var(--line); border-radius: 16px; background: rgba(var(--surface-rgb), 0.45); }
|
||||
.custom-topic-filters[hidden] { display: none; }
|
||||
.custom-topic-filters > p { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; color: var(--ink-soft); font-size: 11px; font-weight: 650; }
|
||||
.custom-topic-filters > p span { padding: 2px 7px; border-radius: 10px; background: var(--mint); font-size: 10px; }
|
||||
.custom-topic-filters > small { color: var(--ink-soft); font-size: 11px; }
|
||||
.custom-topic-filters .filter-row button { background: var(--surface); }
|
||||
.custom-topic-filters .filter-row button.is-active { color: white; background: var(--sage-deep); }
|
||||
.custom-topic-filters .filter-row button.is-active { color: white; background: var(--accent-fill); }
|
||||
|
||||
.posts-grid {
|
||||
display: grid;
|
||||
@@ -1109,7 +1115,7 @@ button {
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
border-radius: var(--radius-lg);
|
||||
background: rgba(255, 255, 255, 0.74);
|
||||
background: rgba(var(--surface-rgb), 0.74);
|
||||
box-shadow: 0 8px 28px rgba(46, 65, 58, 0.04);
|
||||
transition: transform 220ms ease, box-shadow 220ms ease;
|
||||
}
|
||||
@@ -1260,7 +1266,7 @@ button {
|
||||
|
||||
.post-card:hover .read-arrow {
|
||||
color: white;
|
||||
background: var(--sage-deep);
|
||||
background: var(--accent-fill);
|
||||
}
|
||||
|
||||
.empty-card {
|
||||
@@ -1328,7 +1334,7 @@ button {
|
||||
}
|
||||
|
||||
.article-tags span {
|
||||
background: rgba(255, 255, 255, 0.48);
|
||||
background: rgba(var(--surface-rgb), 0.48);
|
||||
}
|
||||
|
||||
.article-hero h1 {
|
||||
@@ -1377,7 +1383,7 @@ button {
|
||||
place-items: center;
|
||||
border-radius: 13px;
|
||||
color: white;
|
||||
background: var(--sage-deep);
|
||||
background: var(--accent-fill);
|
||||
font-size: 10px;
|
||||
font-weight: 750;
|
||||
}
|
||||
@@ -1498,7 +1504,7 @@ button {
|
||||
padding: clamp(25px, 4vw, 40px);
|
||||
border: 0;
|
||||
border-radius: var(--radius-xl);
|
||||
background: rgba(255, 255, 255, 0.78);
|
||||
background: rgba(var(--surface-rgb), 0.78);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
@@ -1548,7 +1554,7 @@ button {
|
||||
.contact-form input:focus,
|
||||
.contact-form textarea:focus {
|
||||
border-color: rgba(47, 81, 71, 0.43);
|
||||
background: white;
|
||||
background: var(--surface-solid);
|
||||
box-shadow: 0 0 0 4px rgba(47, 81, 71, 0.07);
|
||||
}
|
||||
|
||||
@@ -1616,7 +1622,7 @@ button {
|
||||
}
|
||||
|
||||
.social-list {
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
background: rgba(var(--surface-rgb), 0.65);
|
||||
}
|
||||
|
||||
.social-list .eyebrow {
|
||||
@@ -1841,7 +1847,7 @@ button {
|
||||
height: 38px;
|
||||
place-items: center;
|
||||
border-radius: 13px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
background: rgba(var(--surface-rgb), 0.7);
|
||||
}
|
||||
|
||||
.culture-link small,
|
||||
@@ -1892,7 +1898,7 @@ button {
|
||||
margin: -22px auto 30px;
|
||||
padding: clamp(28px, 5vw, 48px);
|
||||
border-radius: var(--radius-xl);
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
background: rgba(var(--surface-rgb), 0.8);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
@@ -1974,7 +1980,7 @@ button {
|
||||
.publisher-field textarea:focus,
|
||||
.publisher-field select:focus {
|
||||
border-color: rgba(47, 81, 71, 0.43);
|
||||
background: white;
|
||||
background: var(--surface-solid);
|
||||
box-shadow: 0 0 0 4px rgba(47, 81, 71, 0.07);
|
||||
}
|
||||
|
||||
@@ -1985,7 +1991,7 @@ button {
|
||||
margin-top: -25px;
|
||||
padding: clamp(28px, 5vw, 48px);
|
||||
border-radius: var(--radius-xl);
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
background: rgba(var(--surface-rgb), 0.8);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
@@ -2074,7 +2080,7 @@ button {
|
||||
|
||||
.topic-options button.is-active {
|
||||
color: white;
|
||||
background: var(--sage-deep);
|
||||
background: var(--accent-fill);
|
||||
box-shadow: 0 8px 19px rgba(47, 81, 71, 0.15);
|
||||
}
|
||||
|
||||
@@ -2087,7 +2093,7 @@ button {
|
||||
padding-left: 13px;
|
||||
border-radius: 12px;
|
||||
color: white;
|
||||
background: var(--sage-deep);
|
||||
background: var(--accent-fill);
|
||||
font-size: 12px;
|
||||
font-weight: 680;
|
||||
box-shadow: 0 8px 19px rgba(47, 81, 71, 0.15);
|
||||
@@ -2186,7 +2192,7 @@ button {
|
||||
.editor-tool-group button:hover,
|
||||
.editor-tool-group button.is-active {
|
||||
color: var(--ink);
|
||||
background: white;
|
||||
background: var(--surface-solid);
|
||||
box-shadow: 0 5px 15px rgba(46, 65, 58, 0.08);
|
||||
}
|
||||
|
||||
@@ -2196,7 +2202,7 @@ button {
|
||||
padding-inline: 10px 28px;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
background: white;
|
||||
background: var(--surface-solid);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -2394,8 +2400,8 @@ button {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.nav-positioner__inner { padding-right: 0; }
|
||||
.site-header .header-actions { padding-right: 56px; }
|
||||
.nav-positioner__inner { padding-right: 112px; }
|
||||
.site-header .header-actions { padding-right: 0; }
|
||||
|
||||
.header-inner {
|
||||
position: relative;
|
||||
@@ -2813,7 +2819,7 @@ button {
|
||||
padding: clamp(18px, 3vw, 30px);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 22px;
|
||||
background: rgba(255, 255, 255, 0.45);
|
||||
background: rgba(var(--surface-rgb), 0.45);
|
||||
}
|
||||
.article-uploads legend { padding-inline: 10px; font-size: 16px; }
|
||||
.section-help { grid-column: 1 / -1; }
|
||||
@@ -2842,10 +2848,10 @@ button {
|
||||
.upload-dropzone strong { font-size: 15px; font-weight: 600; }
|
||||
.upload-dropzone p { margin: 0; color: var(--ink-soft); font-size: 12px; line-height: 1.6; }
|
||||
.upload-dropzone small { font-size: 11px; }
|
||||
.upload-browse { display: inline-flex; justify-content: center; align-items: center; gap: 7px; min-height: 40px; margin-top: 5px; padding: 9px 15px; border: 1px solid var(--line); border-radius: 11px; background: white; color: var(--sage-deep); font-size: 12px; font-weight: 650; cursor: pointer; }
|
||||
.upload-browse { display: inline-flex; justify-content: center; align-items: center; gap: 7px; min-height: 40px; margin-top: 5px; padding: 9px 15px; border: 1px solid var(--line); border-radius: 11px; background: var(--surface-solid); color: var(--sage-deep); font-size: 12px; font-weight: 650; cursor: pointer; }
|
||||
.article-uploads .upload-input { display: none; }
|
||||
.upload-tip { margin: 0; color: var(--ink-soft); font-size: 11px; line-height: 1.6; }
|
||||
.upload-hero-preview { position: relative; isolation: isolate; display: grid; align-items: end; min-height: 220px; overflow: hidden; border-radius: 16px; background: var(--sage-deep); color: white; }
|
||||
.upload-hero-preview { position: relative; isolation: isolate; display: grid; align-items: end; min-height: 220px; overflow: hidden; border-radius: 16px; background: var(--accent-fill); color: white; }
|
||||
.upload-hero-preview > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; }
|
||||
.upload-hero-preview::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(110deg, rgba(15,29,24,.82), rgba(15,29,24,.58)); }
|
||||
.upload-hero-preview > div { display: grid; gap: 12px; padding: 25px; }
|
||||
@@ -2921,7 +2927,7 @@ button {
|
||||
}
|
||||
.icon-button:hover { background: var(--mint); }
|
||||
.header-signin { position: relative; }
|
||||
.header-signin.is-admin { color: #fff; background: var(--sage-deep); }
|
||||
.header-signin.is-admin { color: #fff; background: var(--accent-fill); }
|
||||
.admin-indicator { position: absolute; right: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: #bde9a8; }
|
||||
.admin-dialog {
|
||||
width: min(460px, calc(100vw - 32px));
|
||||
@@ -2982,6 +2988,7 @@ button {
|
||||
button:disabled { cursor: not-allowed; opacity: 0.6; }
|
||||
@media (max-width: 600px) {
|
||||
.header-actions { gap: 8px; }
|
||||
.nav-positioner__inner { padding-right: 104px; }
|
||||
.admin-dialog { padding: 22px; }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
:root[data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
--ink: #e4eee8;
|
||||
--ink-soft: #bccac2;
|
||||
--ink-faint: #a0b0a7;
|
||||
--line: rgba(188, 211, 197, 0.18);
|
||||
--paper: #141d19;
|
||||
--surface-rgb: 35, 48, 41;
|
||||
--surface: rgba(var(--surface-rgb), 0.92);
|
||||
--surface-solid: #233029;
|
||||
--sage: #293e33;
|
||||
--sage-deep: #a4d4b8;
|
||||
--accent-fill: #345d48;
|
||||
--accent-fill-hover: #416e56;
|
||||
--blue: #263640;
|
||||
--blue-deep: #aacde3;
|
||||
--lavender: #342f42;
|
||||
--lavender-deep: #c8bce6;
|
||||
--peach: #41322c;
|
||||
--peach-deep: #e2baa6;
|
||||
--yellow: #3b3827;
|
||||
--yellow-deep: #d9cc99;
|
||||
--mint: #263e33;
|
||||
--glow-mint: rgba(48, 89, 64, 0.22);
|
||||
--glow-lavender: rgba(69, 53, 94, 0.18);
|
||||
--shadow: 0 24px 70px rgba(0, 0, 0, 0.25);
|
||||
--shadow-soft: 0 14px 38px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] ::selection { background: #385745; }
|
||||
:root[data-theme="dark"] .about-copy h2,
|
||||
:root[data-theme="dark"] .article-content section p,
|
||||
:root[data-theme="dark"] .rich-editor .tiptap,
|
||||
:root[data-theme="dark"] .rich-article .tiptap { color: var(--ink-soft); }
|
||||
|
||||
:root[data-theme="dark"] .portrait-frame { border-color: var(--surface-solid); }
|
||||
:root[data-theme="dark"] .portrait-orbit,
|
||||
:root[data-theme="dark"] .portrait-orbit--two,
|
||||
:root[data-theme="dark"] .experience-note,
|
||||
:root[data-theme="dark"] .availability-note,
|
||||
:root[data-theme="dark"] .tag-row span,
|
||||
:root[data-theme="dark"] .skill-tags span,
|
||||
:root[data-theme="dark"] .mini-tags span,
|
||||
:root[data-theme="dark"] .article-tags span,
|
||||
:root[data-theme="dark"] .article-hero { border-color: var(--line); }
|
||||
:root[data-theme="dark"] .skill-dot { border-color: var(--ink-soft); }
|
||||
:root[data-theme="dark"] .skill-orbit { box-shadow: 0 0 0 12px var(--mint); }
|
||||
:root[data-theme="dark"] .expand-button,
|
||||
:root[data-theme="dark"] .project-links a,
|
||||
:root[data-theme="dark"] .topic-options button,
|
||||
:root[data-theme="dark"] .topic-picker kbd,
|
||||
:root[data-theme="dark"] .rich-editor__toolbar { background: var(--paper); }
|
||||
:root[data-theme="dark"] .read-arrow,
|
||||
:root[data-theme="dark"] .upload-dropzone__icon { background: var(--mint); }
|
||||
:root[data-theme="dark"] .topic-options button.is-active,
|
||||
:root[data-theme="dark"] .post-card:hover .read-arrow { background: var(--accent-fill); }
|
||||
:root[data-theme="dark"] .topic-options .topic-chip__remove { background: transparent; }
|
||||
|
||||
:root[data-theme="dark"] .contact-form input,
|
||||
:root[data-theme="dark"] .contact-form textarea,
|
||||
:root[data-theme="dark"] .publisher-login input,
|
||||
:root[data-theme="dark"] .publisher-field input,
|
||||
:root[data-theme="dark"] .publisher-field textarea,
|
||||
:root[data-theme="dark"] .publisher-field select,
|
||||
:root[data-theme="dark"] .custom-topic input,
|
||||
:root[data-theme="dark"] .rich-editor {
|
||||
background: var(--paper);
|
||||
border-color: var(--line);
|
||||
}
|
||||
:root[data-theme="dark"] :is(input, textarea, select):focus,
|
||||
:root[data-theme="dark"] .rich-editor:focus-within,
|
||||
:root[data-theme="dark"] .search-box:focus-within { border-color: var(--sage-deep); }
|
||||
:root[data-theme="dark"] :is(input, textarea)::placeholder,
|
||||
:root[data-theme="dark"] .rich-editor .tiptap p.is-editor-empty:first-child::before { color: var(--ink-faint); }
|
||||
|
||||
:root[data-theme="dark"] .culture-card { background: linear-gradient(145deg, var(--lavender), #262631); }
|
||||
:root[data-theme="dark"] .culture-link--spotify { background: #284334; }
|
||||
:root[data-theme="dark"] .culture-link--instagram { background: #44303c; }
|
||||
:root[data-theme="dark"] .culture-link--steam { background: #293b49; }
|
||||
:root[data-theme="dark"] .publisher-badge,
|
||||
:root[data-theme="dark"] .form-status--success { color: var(--sage-deep); }
|
||||
:root[data-theme="dark"] .state-card--error,
|
||||
:root[data-theme="dark"] .upload-feedback,
|
||||
:root[data-theme="dark"] .upload-list .has-error { background: #3c2825; border-color: #7b5148; color: #f2b7a7; }
|
||||
:root[data-theme="dark"] .form-status--error,
|
||||
:root[data-theme="dark"] .social-error,
|
||||
:root[data-theme="dark"] .upload-file-info p,
|
||||
:root[data-theme="dark"] .text-button--danger { color: #f2b7a7; }
|
||||
:root[data-theme="dark"] .upload-dropzone { border-color: #668979; background: linear-gradient(135deg, #253a2e, #1c2c23); }
|
||||
:root[data-theme="dark"] .upload-dropzone.is-dragging,
|
||||
:root[data-theme="dark"] .upload-list .upload-sortable.is-sorting { background: var(--mint); }
|
||||
|
||||
.theme-toggle { color: var(--sage-deep); }
|
||||
Reference in New Issue
Block a user