fix: replace all hardcoded hex colors with CSS theme variables
CI / typecheck + build (push) Canceled after 0s
CI / deploy to VPS (push) Canceled after 0s

Phase 15c — theme-consistent color system:

Replaced 63 instances of hardcoded hex colors (#5e6ad2, #7170ff, #4f46e5,
#6a75e0, #828fff, #94a3b8, etc.) with CSS variables from globals.css:
- #5e6ad2 → var(--brand) (light: #5e6ad2, dark: #5e6ad2)
- #7170ff → var(--accent) (light: #4f46e5, dark: #7170ff)
- #4f46e5 → var(--accent)
- #6a75e0 → var(--brand-hover) (new var, light: #6a75e0, dark: #7170ff)
- #828fff → var(--accent-hover)
- #868686 → var(--text-dim)
- Dark-mode-specific hex in Markdown.tsx → CSS variables

Added --brand-hover CSS variable (light: #6a75e0, dark: #7170ff)
Added dark:prose-invert instead of prose-invert (light mode now uses
default prose theme instead of forced dark)

Files changed: 15 component files + globals.css
This commit is contained in:
asepharyana
2026-08-21 13:06:01 +07:00
parent ea75d68b1a
commit be923cb432
18 changed files with 149 additions and 105 deletions
+11 -11
View File
@@ -52,7 +52,7 @@ function CustomFieldBadges({
let displayValue: string;
if (typeof value === "number") {
colorClass = "bg-[#5e6ad2]/10 border-[#5e6ad2]/30 text-[#5e6ad2] dark:text-[#7170ff]";
colorClass = "bg-[var(--brand)]/10 border-[var(--brand)]/30 text-[var(--brand)] dark:text-[var(--accent)]";
icon = "🎯";
displayValue = `${value} pts`;
} else if (typeof value === "boolean") {
@@ -62,7 +62,7 @@ function CustomFieldBadges({
icon = value ? "✓" : "✕";
displayValue = value ? "Solved" : "Pending";
} else if (Array.isArray(value)) {
colorClass = "bg-[#5e6ad2]/10 border-[#5e6ad2]/30 text-[#5e6ad2] dark:text-[#7170ff]";
colorClass = "bg-[var(--brand)]/10 border-[var(--brand)]/30 text-[var(--brand)] dark:text-[var(--accent)]";
icon = "🗂️";
displayValue = value.join(", ");
} else {
@@ -84,7 +84,7 @@ function CustomFieldBadges({
icon = "⚡";
displayValue = v.toUpperCase();
} else if (/ctf|def.?con|event/i.test(v)) {
colorClass = "bg-[#5e6ad2]/10 border-[#5e6ad2]/30 text-[#5e6ad2] dark:text-[#7170ff]";
colorClass = "bg-[var(--brand)]/10 border-[var(--brand)]/30 text-[var(--brand)] dark:text-[var(--accent)]";
icon = "🏆";
displayValue = String(value);
} else {
@@ -182,7 +182,7 @@ function FolderIndexPage({
{folderName}
</h1>
<p className="text-xs text-[var(--text-muted)] mt-0.5">
Directory path: <code className="text-[#5e6ad2] dark:text-[#7170ff] font-mono">{section}/{slug}</code>
Directory path: <code className="text-[var(--brand)] dark:text-[var(--accent)] font-mono">{section}/{slug}</code>
</p>
</div>
</div>
@@ -208,7 +208,7 @@ function FolderIndexPage({
>
<span className="text-2xl group-hover:scale-110 transition-transform">📁</span>
<div className="flex-1 min-w-0">
<div className="text-sm font-semibold text-[var(--text-primary)] group-hover:text-[#5e6ad2] dark:group-hover:text-[#7170ff] transition-colors truncate">
<div className="text-sm font-semibold text-[var(--text-primary)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors truncate">
{folder.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ")}
</div>
<div className="text-xs text-[var(--text-muted)] mt-0.5 font-mono">
@@ -238,7 +238,7 @@ function FolderIndexPage({
<div className="flex items-center gap-3 min-w-0">
<span className="text-xl opacity-70 group-hover:opacity-100">📄</span>
<div className="min-w-0">
<div className="text-sm font-semibold text-[var(--text-primary)] group-hover:text-[#5e6ad2] dark:group-hover:text-[#7170ff] transition-colors truncate">
<div className="text-sm font-semibold text-[var(--text-primary)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors truncate">
{doc.title}
</div>
{doc.tags.length > 0 && (
@@ -376,7 +376,7 @@ export default async function DocPage({ params, searchParams }: DocPageProps) {
<div className="flex flex-col sm:flex-row sm:items-start justify-between gap-4 mb-4">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-2 flex-wrap">
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-mono font-medium bg-[#5e6ad2]/15 text-[#5e6ad2] dark:text-[#7170ff] border border-[#5e6ad2]/30">
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-mono font-medium bg-[var(--brand)]/15 text-[var(--brand)] dark:text-[var(--accent)] border border-[var(--brand)]/30">
<span>{sectionInfo.icon}</span>
<span className="uppercase">{sectionInfo.label}</span>
</span>
@@ -402,7 +402,7 @@ export default async function DocPage({ params, searchParams }: DocPageProps) {
<div className="flex flex-wrap items-center gap-3 text-xs text-[var(--text-muted)] pt-3 border-t border-[var(--border-color)]">
{doc.author && (
<div className="flex items-center gap-1.5">
<div className="w-4 h-4 rounded-full bg-[#5e6ad2] text-white flex items-center justify-center text-[10px] font-bold">
<div className="w-4 h-4 rounded-full bg-[var(--brand)] text-white flex items-center justify-center text-[10px] font-bold">
{doc.author.charAt(0).toUpperCase()}
</div>
<span className="text-[var(--text-secondary)]">{doc.author}</span>
@@ -463,7 +463,7 @@ export default async function DocPage({ params, searchParams }: DocPageProps) {
className="group block bg-[var(--bg-surface)] hover:bg-[var(--bg-surface-hover)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-lg p-4 transition-all shadow-sm"
>
<div className="flex items-center justify-between gap-2 mb-2">
<span className="text-xs text-[#5e6ad2] dark:text-[#7170ff] font-mono uppercase">
<span className="text-xs text-[var(--brand)] dark:text-[var(--accent)] font-mono uppercase">
{rInfo.icon} {r.section}
</span>
<time className="text-[11px] text-[var(--text-dim)] font-mono">
@@ -473,7 +473,7 @@ export default async function DocPage({ params, searchParams }: DocPageProps) {
})}
</time>
</div>
<h3 className="text-sm font-semibold text-[var(--text-primary)] group-hover:text-[#5e6ad2] dark:group-hover:text-[#7170ff] transition-colors line-clamp-1 mb-1">
<h3 className="text-sm font-semibold text-[var(--text-primary)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors line-clamp-1 mb-1">
{r.title}
</h3>
</Link>
@@ -498,7 +498,7 @@ export default async function DocPage({ params, searchParams }: DocPageProps) {
className="flex items-center justify-between gap-3 p-3 bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-lg text-xs shadow-sm"
>
<div className="flex items-center gap-2 min-w-0">
<span className="font-mono text-[#5e6ad2] dark:text-[#7170ff] bg-[#5e6ad2]/10 px-1.5 py-0.5 rounded border border-[#5e6ad2]/30 font-semibold">
<span className="font-mono text-[var(--brand)] dark:text-[var(--accent)] bg-[var(--brand)]/10 px-1.5 py-0.5 rounded border border-[var(--brand)]/30 font-semibold">
v{rev.revisionNo}
</span>
<span className="text-[var(--text-primary)] truncate">
+4 -4
View File
@@ -83,7 +83,7 @@ function renderTree(nodes: TreeNode[], pathname: string) {
href={`/${node.slug}`}
className={`flex items-center justify-between gap-2 text-xs py-1.5 px-2.5 rounded-lg transition-all ${
isActive
? "text-[#5e6ad2] dark:text-[#7170ff] bg-[#5e6ad2]/15 font-semibold border border-[#5e6ad2]/30"
? "text-[var(--brand)] dark:text-[var(--accent)] bg-[var(--brand)]/15 font-semibold border border-[var(--brand)]/30"
: hasActiveChild
? "text-[var(--text-primary)] bg-[var(--bg-elevated)]"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-elevated)]"
@@ -152,12 +152,12 @@ export default async function SectionIndexPage({
</div>
<div className="flex items-center gap-2">
<span className="text-xs font-mono text-[#5e6ad2] dark:text-[#7170ff] bg-[#5e6ad2]/10 border border-[#5e6ad2]/25 px-2.5 py-1 rounded-md">
<span className="text-xs font-mono text-[var(--brand)] dark:text-[var(--accent)] bg-[var(--brand)]/10 border border-[var(--brand)]/25 px-2.5 py-1 rounded-md">
{sectionDocs.length} document{sectionDocs.length !== 1 ? "s" : ""}
</span>
<Link
href="/create"
className="inline-flex items-center gap-1 text-xs bg-[#5e6ad2] hover:bg-[#6a75e0] text-white px-3 py-1 rounded-md font-medium transition-all"
className="inline-flex items-center gap-1 text-xs bg-[var(--brand)] hover:bg-[var(--brand-hover)] text-white px-3 py-1 rounded-md font-medium transition-all"
>
<span>+ Create</span>
</Link>
@@ -200,7 +200,7 @@ export default async function SectionIndexPage({
<div className="flex items-center gap-3 min-w-0">
<span className="text-lg opacity-70 group-hover:opacity-100">📄</span>
<div className="min-w-0">
<h3 className="text-sm font-medium text-[var(--text-primary)] group-hover:text-[#5e6ad2] dark:group-hover:text-[#7170ff] transition-colors truncate">
<h3 className="text-sm font-medium text-[var(--text-primary)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors truncate">
{d.title}
</h3>
{d.tags.length > 0 && (
+7 -7
View File
@@ -116,7 +116,7 @@ export default function CommandMenu() {
<div className="relative w-full max-w-2xl bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-xl shadow-2xl overflow-hidden animate-fade-in">
{/* Search input bar */}
<div className="flex items-center gap-3 px-4 py-3.5 border-b border-[var(--border-color)] bg-[var(--bg-elevated)]">
<svg className="w-5 h-5 text-[#5e6ad2] dark:text-[#7170ff] shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<svg className="w-5 h-5 text-[var(--brand)] dark:text-[var(--accent)] shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<input
@@ -129,7 +129,7 @@ export default function CommandMenu() {
className="w-full bg-transparent text-[var(--text-primary)] placeholder-[var(--text-dim)] text-sm focus:outline-none"
/>
{loading && (
<div className="w-4 h-4 border-2 border-[#5e6ad2] border-t-transparent rounded-full animate-spin shrink-0" />
<div className="w-4 h-4 border-2 border-[var(--brand)] border-t-transparent rounded-full animate-spin shrink-0" />
)}
<button
onClick={() => setOpen(false)}
@@ -164,22 +164,22 @@ export default function CommandMenu() {
onMouseEnter={() => setSelectedIndex(index)}
className={`flex items-start justify-between gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all ${
isSelected
? "bg-[#5e6ad2]/15 border border-[#5e6ad2]/40 text-[var(--text-primary)]"
? "bg-[var(--brand)]/15 border border-[var(--brand)]/40 text-[var(--text-primary)]"
: "hover:bg-[var(--bg-elevated)] text-[var(--text-secondary)] border border-transparent"
}`}
>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="text-xs px-1.5 py-0.25 bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded text-[#5e6ad2] dark:text-[#7170ff] font-mono">
<span className="text-xs px-1.5 py-0.25 bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded text-[var(--brand)] dark:text-[var(--accent)] font-mono">
{hit.section}
</span>
<span className={`text-sm font-medium ${isSelected ? "text-[#5e6ad2] dark:text-[#7170ff]" : "text-[var(--text-primary)]"} truncate`}>
<span className={`text-sm font-medium ${isSelected ? "text-[var(--brand)] dark:text-[var(--accent)]" : "text-[var(--text-primary)]"} truncate`}>
{hit.title}
</span>
</div>
{hit.snippet && (
<p
className="text-xs text-[var(--text-muted)] mt-1 line-clamp-1 [&>mark]:bg-[#5e6ad2]/30 [&>mark]:text-[var(--text-primary)] [&>mark]:rounded [&>mark]:px-0.5"
className="text-xs text-[var(--text-muted)] mt-1 line-clamp-1 [&>mark]:bg-[var(--brand)]/30 [&>mark]:text-[var(--text-primary)] [&>mark]:rounded [&>mark]:px-0.5"
dangerouslySetInnerHTML={{ __html: hit.snippet }}
/>
)}
@@ -200,7 +200,7 @@ export default function CommandMenu() {
<Link
href={`/search?q=${encodeURIComponent(query)}`}
onClick={() => setOpen(false)}
className="hover:text-[#5e6ad2] dark:hover:text-[#7170ff] transition-colors"
className="hover:text-[var(--brand)] dark:hover:text-[var(--accent)] transition-colors"
>
Open full search page →
</Link>
+1 -1
View File
@@ -78,7 +78,7 @@ export default function DocActions({ slug, body, canEdit }: DocActionsProps) {
{/* Edit Link */}
<Link
href={canEdit ? `/${slug}?edit=1` : `/login?redirect=/${slug}`}
className="inline-flex items-center gap-1 px-2.5 py-1 text-xs bg-[#5e6ad2]/10 hover:bg-[#5e6ad2]/20 border border-[#5e6ad2]/30 hover:border-[#5e6ad2]/60 text-[#5e6ad2] dark:text-[#7170ff] rounded-md transition-all font-medium"
className="inline-flex items-center gap-1 px-2.5 py-1 text-xs bg-[var(--brand)]/10 hover:bg-[var(--brand)]/20 border border-[var(--brand)]/30 hover:border-[var(--brand)]/60 text-[var(--brand)] dark:text-[var(--accent)] rounded-md transition-all font-medium"
>
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
+6 -6
View File
@@ -88,7 +88,7 @@ export default function DocForm({
const availableFolders = existingFolders?.[section] ?? [];
const baseInputCls =
"w-full px-3.5 py-2 bg-[var(--bg-surface)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-lg text-sm text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[#5e6ad2] transition-colors";
"w-full px-3.5 py-2 bg-[var(--bg-surface)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-lg text-sm text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[var(--brand)] transition-colors";
function addCustomField(key = "", value = "") {
setCustomFields([...customFields, { key, value }]);
@@ -314,7 +314,7 @@ export default function DocForm({
/>
)}
<p className="text-[11px] text-[var(--text-dim)] mt-1 font-mono">
Path: <span className="text-[#5e6ad2] dark:text-[#7170ff]">{effectiveSlug || section}</span>
Path: <span className="text-[var(--brand)] dark:text-[var(--accent)]">{effectiveSlug || section}</span>
</p>
</div>
</div>
@@ -404,7 +404,7 @@ export default function DocForm({
<button
type="button"
onClick={() => addCustomField()}
className="text-xs text-[#5e6ad2] dark:text-[#7170ff] hover:underline px-3 py-1.5 rounded-lg border border-[#5e6ad2]/30 hover:border-[#5e6ad2]/60 bg-[#5e6ad2]/5 transition-colors font-medium"
className="text-xs text-[var(--brand)] dark:text-[var(--accent)] hover:underline px-3 py-1.5 rounded-lg border border-[var(--brand)]/30 hover:border-[var(--brand)]/60 bg-[var(--brand)]/5 transition-colors font-medium"
>
+ Add Custom Field
</button>
@@ -421,7 +421,7 @@ export default function DocForm({
onClick={() => setActiveTab("write")}
className={`px-3 py-1 text-xs rounded-md font-medium transition-all ${
activeTab === "write"
? "bg-[#5e6ad2] text-white shadow-sm"
? "bg-[var(--brand)] text-white shadow-sm"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)]"
}`}
>
@@ -432,7 +432,7 @@ export default function DocForm({
onClick={() => setActiveTab("preview")}
className={`px-3 py-1 text-xs rounded-md font-medium transition-all ${
activeTab === "preview"
? "bg-[#5e6ad2] text-white shadow-sm"
? "bg-[var(--brand)] text-white shadow-sm"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)]"
}`}
>
@@ -521,7 +521,7 @@ export default function DocForm({
<button
type="submit"
disabled={loading}
className="px-6 py-2.5 bg-[#5e6ad2] hover:bg-[#6a75e0] text-white rounded-lg font-medium text-sm transition-all shadow-md shadow-[#5e6ad2]/20 disabled:opacity-50 flex items-center gap-2"
className="px-6 py-2.5 bg-[var(--brand)] hover:bg-[var(--brand-hover)] text-white rounded-lg font-medium text-sm transition-all shadow-md shadow-[var(--brand)]/20 disabled:opacity-50 flex items-center gap-2"
>
{loading && (
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
+3 -3
View File
@@ -29,7 +29,7 @@ export default function Header() {
{/* Brand */}
<div className="flex items-center gap-3">
<Link href="/" className="flex items-center gap-2 group">
<div className="w-7 h-7 rounded-lg bg-gradient-to-br from-[#5e6ad2] to-[#7170ff] flex items-center justify-center text-white font-bold text-xs shadow-md shadow-[#5e6ad2]/20 group-hover:scale-105 transition-transform">
<div className="w-7 h-7 rounded-lg bg-gradient-to-br from-[var(--brand)] to-[var(--accent)] flex items-center justify-center text-white font-bold text-xs shadow-md shadow-[var(--brand)]/20 group-hover:scale-105 transition-transform">
M
</div>
<span className="font-semibold text-[var(--text-primary)] text-base tracking-tight transition-colors">
@@ -78,7 +78,7 @@ export default function Header() {
<Link
href="/create"
className="hidden sm:inline-flex items-center gap-1.5 px-3 py-1 bg-[#5e6ad2] hover:bg-[#7170ff] text-white text-xs font-medium rounded-lg transition-all shadow-sm"
className="hidden sm:inline-flex items-center gap-1.5 px-3 py-1 bg-[var(--brand)] hover:bg-[var(--accent)] text-white text-xs font-medium rounded-lg transition-all shadow-sm"
>
<span>+</span>
<span>New Doc</span>
@@ -141,7 +141,7 @@ export default function Header() {
<Link
href="/create"
onClick={() => setMobileMenuOpen(false)}
className="flex-1 text-center py-2 text-xs bg-[#5e6ad2] text-white rounded-lg font-medium"
className="flex-1 text-center py-2 text-xs bg-[var(--brand)] text-white rounded-lg font-medium"
>
+ Create
</Link>
+16 -16
View File
@@ -27,19 +27,19 @@ function CodeBlock({ children, className, ...props }: React.HTMLAttributes<HTMLE
}
return (
<div className="relative group my-4 rounded-lg overflow-hidden border border-[#23252a] bg-[#0c0d0e]">
<div className="relative group my-4 rounded-lg overflow-hidden border border-[var(--border-color)] bg-[var(--code-block-bg)]">
{/* Code block header bar */}
<div className="flex items-center justify-between px-3.5 py-1.5 bg-[#141517] border-b border-[#1f2022] text-[11px] text-[#8a8f98] font-mono">
<div className="flex items-center justify-between px-3.5 py-1.5 bg-[var(--bg-elevated)] border-b border-[var(--border-color)] text-[11px] text-[var(--text-muted)] font-mono">
<span>{language || "text"}</span>
<button
onClick={handleCopy}
type="button"
className="flex items-center gap-1 text-[#8a8f98] hover:text-[#f7f8f8] bg-[#1a1b1d] hover:bg-[#23252a] px-2 py-0.5 rounded border border-[#27292d] transition-all"
className="flex items-center gap-1 text-[var(--text-muted)] hover:text-[var(--text-primary)] bg-[var(--bg-elevated-hover)] hover:bg-[var(--border-subtle)] px-2 py-0.5 rounded border border-[var(--border-color)] transition-all"
>
{copied ? (
<>
<svg className="w-3 h-3 text-emerald-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4 L19 7" />
</svg>
<span className="text-emerald-400 font-sans">Copied!</span>
</>
@@ -54,7 +54,7 @@ function CodeBlock({ children, className, ...props }: React.HTMLAttributes<HTMLE
</button>
</div>
<pre className="!m-0 !bg-transparent p-4 overflow-x-auto text-sm text-[#e2e4e7] font-mono leading-relaxed">
<pre className="!m-0 !bg-transparent p-4 overflow-x-auto text-sm text-[var(--pre-text)] font-mono leading-relaxed">
<code className={className} {...props}>
{children}
</code>
@@ -66,17 +66,17 @@ function CodeBlock({ children, className, ...props }: React.HTMLAttributes<HTMLE
export default function Markdown({ source }: { source: string }) {
return (
<div
className="prose prose-invert max-w-none
prose-headings:text-[#f7f8f8] prose-headings:font-medium prose-headings:tracking-tight
prose-h1:text-2xl prose-h1:mb-6 prose-h1:mt-8 prose-h1:pb-2 prose-h1:border-b prose-h1:border-[#1f2022]
className="prose max-w-none
prose-headings:text-[var(--text-primary)] prose-headings:font-medium prose-headings:tracking-tight
prose-h1:text-2xl prose-h1:mb-6 prose-h1:mt-8 prose-h1:pb-2 prose-h1:border-b prose-h1:border-[var(--border-color)]
prose-h2:text-xl prose-h2:mb-4 prose-h2:mt-8
prose-h3:text-base prose-h3:mb-3 prose-h3:mt-6
prose-p:text-[#d0d6e0] prose-p:leading-relaxed prose-p:mb-4
prose-a:text-[#7170ff] prose-a:no-underline hover:prose-a:underline hover:prose-a:text-[#828fff]
prose-ul:text-[#d0d6e0] prose-ul:my-3 prose-li:my-1
prose-ol:text-[#d0d6e0] prose-ol:my-3
prose-strong:text-[#f7f8f8] prose-strong:font-semibold
prose-hr:border-[#1f2022] prose-hr:my-8"
prose-p:text-[var(--text-secondary)] prose-p:leading-relaxed prose-p:mb-4
prose-a:text-[var(--accent)] prose-a:no-underline hover:prose-a:underline hover:prose-a:text-[var(--accent-hover)]
prose-ul:text-[var(--text-secondary)] prose-ul:my-3 prose-li:my-1
prose-ol:text-[var(--text-secondary)] prose-ol:my-3
prose-strong:text-[var(--text-primary)] prose-strong:font-semibold
prose-hr:border-[var(--border-color)] prose-hr:my-8"
>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
@@ -85,13 +85,13 @@ export default function Markdown({ source }: { source: string }) {
code: CodeBlock as any,
pre: ({ children }) => <>{children}</>,
table: ({ node, ...props }) => (
<div className="overflow-x-auto my-6 border border-[#1f2022] rounded-lg">
<div className="overflow-x-auto my-6 border border-[var(--border-color)] rounded-lg">
<table className="w-full border-collapse" {...props} />
</div>
),
blockquote: ({ children }) => {
return (
<blockquote className="border-l-4 border-[#5e6ad2] bg-[#141517]/60 pl-4 py-2 my-4 rounded-r text-[#a0a4a8] not-italic">
<blockquote className="border-l-4 border-[var(--brand)] bg-[var(--bg-elevated)]/60 pl-4 py-2 my-4 rounded-r text-[var(--text-muted)] not-italic">
{children}
</blockquote>
);
+1 -1
View File
@@ -70,7 +70,7 @@ export default function McpConfigSnippet() {
onClick={() => setActiveTab(c.id)}
className={`px-3 py-1 text-xs rounded-md font-medium transition-all ${
activeTab === c.id
? "bg-[#5e6ad2] text-white shadow-sm"
? "bg-[var(--brand)] text-white shadow-sm"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-surface)]"
}`}
>
+2 -2
View File
@@ -106,7 +106,7 @@ function TreeNodeItem({
<div
className={`group flex items-center justify-between gap-1.5 text-xs py-1.5 px-2 rounded-md transition-all ${
isActive
? "text-[#5e6ad2] dark:text-[#7170ff] bg-[#5e6ad2]/15 font-semibold border border-[#5e6ad2]/30 shadow-sm"
? "text-[var(--brand)] dark:text-[var(--accent)] bg-[var(--brand)]/15 font-semibold border border-[var(--brand)]/30 shadow-sm"
: isParentOfActive
? "text-[var(--text-primary)] bg-[var(--bg-elevated)]"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-elevated)]"
@@ -263,7 +263,7 @@ export default function Sidebar() {
href={`/${id}`}
className={`flex items-center justify-between gap-1.5 px-1 py-1 mb-1 rounded text-xs font-semibold uppercase tracking-wider transition-colors ${
isSectionActive
? "text-[#5e6ad2] dark:text-[#7170ff]"
? "text-[var(--brand)] dark:text-[var(--accent)]"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)]"
}`}
>
+2 -2
View File
@@ -61,7 +61,7 @@ export default function TOC({ source }: { source: string }) {
return (
<nav className="text-xs">
<div className="flex items-center gap-2 mb-3 pb-2 border-b border-[var(--border-color)]">
<svg className="w-3.5 h-3.5 text-[#5e6ad2] dark:text-[#7170ff]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<svg className="w-3.5 h-3.5 text-[var(--brand)] dark:text-[var(--accent)]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h7" />
</svg>
<span className="font-semibold uppercase tracking-wider text-[var(--text-muted)] text-[11px]">
@@ -92,7 +92,7 @@ export default function TOC({ source }: { source: string }) {
}}
className={`block py-0.5 transition-colors line-clamp-1 ${
isActive
? "text-[#5e6ad2] dark:text-[#7170ff] font-semibold translate-x-0.5"
? "text-[var(--brand)] dark:text-[var(--accent)] font-semibold translate-x-0.5"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)]"
}`}
title={entry.text}
+1 -1
View File
@@ -34,7 +34,7 @@ export default async function CreatePage() {
{/* Header */}
<div className="flex items-center justify-between pb-4 border-b border-[var(--border-color)]">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-[#5e6ad2]/15 border border-[#5e6ad2]/30 flex items-center justify-center text-xl text-[#5e6ad2] dark:text-[#7170ff]">
<div className="w-10 h-10 rounded-lg bg-[var(--brand)]/15 border border-[var(--brand)]/30 flex items-center justify-center text-xl text-[var(--brand)] dark:text-[var(--accent)]">
✏️
</div>
<div>
+3
View File
@@ -19,6 +19,7 @@
--text-dim: #94a3b8;
--brand: #5e6ad2;
--brand-hover: #6a75e0; /* lighter for hover in light mode */
--accent: #4f46e5;
--accent-hover: #4338ca;
@@ -50,6 +51,7 @@ html.dark {
--text-dim: #62666d;
--brand: #5e6ad2;
--brand-hover: #7170ff; /* lighter for hover in dark mode */
--accent: #7170ff;
--accent-hover: #828fff;
@@ -81,6 +83,7 @@ html.dark {
--color-text-quaternary: var(--text-dim);
--color-brand: var(--brand);
--color-brand-hover: var(--brand-hover);
--color-accent: var(--accent);
--color-accent-hover: var(--accent-hover);
}
+1 -1
View File
@@ -35,7 +35,7 @@ export default function RootLayout({
}}
/>
</head>
<body className="min-h-screen bg-[var(--bg-app)] text-[var(--text-secondary)] font-sans antialiased selection:bg-[#5e6ad2]/30 selection:text-white flex flex-col transition-colors duration-150">
<body className="min-h-screen bg-[var(--bg-app)] text-[var(--text-secondary)] font-sans antialiased selection:bg-[var(--brand)]/30 selection:text-white flex flex-col transition-colors duration-150">
<div hidden>
<div id="a11y-live-region" aria-live="polite" aria-atomic="true" />
</div>
+4 -4
View File
@@ -40,10 +40,10 @@ export default function LoginPage() {
<div className="flex min-h-[65vh] items-center justify-center px-4">
<div className="w-full max-w-md bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-2xl p-6 sm:p-8 shadow-2xl relative overflow-hidden">
{/* Top decorative glow */}
<div className="absolute -top-12 left-1/2 -translate-x-1/2 w-48 h-24 bg-[#5e6ad2]/20 blur-2xl pointer-events-none rounded-full" />
<div className="absolute -top-12 left-1/2 -translate-x-1/2 w-48 h-24 bg-[var(--brand)]/20 blur-2xl pointer-events-none rounded-full" />
<div className="text-center mb-8">
<div className="w-12 h-12 rounded-xl bg-[#5e6ad2] text-white font-bold text-xl flex items-center justify-center mx-auto mb-3 shadow-lg shadow-[#5e6ad2]/25">
<div className="w-12 h-12 rounded-xl bg-[var(--brand)] text-white font-bold text-xl flex items-center justify-center mx-auto mb-3 shadow-lg shadow-[var(--brand)]/25">
M
</div>
<h1 className="text-2xl font-bold text-[var(--text-primary)] tracking-tight">Admin Authentication</h1>
@@ -69,7 +69,7 @@ export default function LoginPage() {
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter password..."
className="w-full px-3.5 py-2.5 bg-[var(--bg-elevated)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-lg text-sm text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[#5e6ad2] transition-colors"
className="w-full px-3.5 py-2.5 bg-[var(--bg-elevated)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-lg text-sm text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[var(--brand)] transition-colors"
required
autoFocus
/>
@@ -86,7 +86,7 @@ export default function LoginPage() {
<button
type="submit"
disabled={loading}
className="w-full py-2.5 bg-[#5e6ad2] hover:bg-[#6a75e0] text-white rounded-lg transition-all font-medium text-sm shadow-md shadow-[#5e6ad2]/20 disabled:opacity-50 flex items-center justify-center gap-2"
className="w-full py-2.5 bg-[var(--brand)] hover:bg-[var(--brand-hover)] text-white rounded-lg transition-all font-medium text-sm shadow-md shadow-[var(--brand)]/20 disabled:opacity-50 flex items-center justify-center gap-2"
>
{loading && (
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
+11 -11
View File
@@ -86,7 +86,7 @@ function renderTree(nodes: TreeNode[], pathname: string) {
href={`/${node.slug}`}
className={`flex items-center gap-1.5 text-xs py-1 px-1.5 rounded transition-all ${
pathname === `/${node.slug}`
? "text-[#5e6ad2] dark:text-[#7170ff] bg-[#5e6ad2]/15 font-medium"
? "text-[var(--brand)] dark:text-[var(--accent)] bg-[var(--brand)]/15 font-medium"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-elevated)]"
}`}
title={node.title}
@@ -145,14 +145,14 @@ export default async function HomePage() {
<div className="space-y-16">
{/* Hero Section */}
<section className="relative pt-4 pb-2">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-[#5e6ad2]/10 border border-[#5e6ad2]/25 text-[#5e6ad2] dark:text-[#7170ff] text-xs font-medium mb-6">
<span className="w-2 h-2 rounded-full bg-[#5e6ad2] animate-pulse" />
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-[var(--brand)]/10 border border-[var(--brand)]/25 text-[var(--brand)] dark:text-[var(--accent)] text-xs font-medium mb-6">
<span className="w-2 h-2 rounded-full bg-[var(--brand)] animate-pulse" />
<span>Model Context Protocol + PostgreSQL Core</span>
</div>
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold text-[var(--text-primary)] tracking-tight mb-5 leading-tight">
Knowledge Base for <br />
<span className="bg-gradient-to-r from-[#5e6ad2] via-[#7170ff] to-[#4338ca] dark:to-white bg-clip-text text-transparent">
<span className="bg-gradient-to-r from-[var(--brand)] via-[var(--accent)] to-[var(--accent-hover)] dark:to-white bg-clip-text text-transparent">
Humans and AI Agents
</span>
</h1>
@@ -165,7 +165,7 @@ export default async function HomePage() {
<div className="flex flex-wrap items-center gap-3 mb-10">
<Link
href="/docs"
className="inline-flex items-center gap-2 px-5 py-2.5 bg-[#5e6ad2] hover:bg-[#6a75e0] text-white rounded-lg font-medium text-sm transition-all shadow-md shadow-[#5e6ad2]/20"
className="inline-flex items-center gap-2 px-5 py-2.5 bg-[var(--brand)] hover:bg-[var(--brand-hover)] text-white rounded-lg font-medium text-sm transition-all shadow-md shadow-[var(--brand)]/20"
>
<span>Browse Documentation</span>
<span>→</span>
@@ -198,7 +198,7 @@ export default async function HomePage() {
<div className="text-xs text-[var(--text-muted)]">Active sections</div>
</div>
<div>
<div className="text-xl font-bold text-[#5e6ad2] dark:text-[#7170ff]">RRF Hybrid</div>
<div className="text-xl font-bold text-[var(--brand)] dark:text-[var(--accent)]">RRF Hybrid</div>
<div className="text-xs text-[var(--text-muted)]">FTS + Cosine Vectors</div>
</div>
<div>
@@ -229,12 +229,12 @@ export default async function HomePage() {
<span className="text-2xl">{s.icon}</span>
<Link
href={`/${s.id}`}
className="text-xs font-mono text-[#5e6ad2] dark:text-[#7170ff] hover:underline"
className="text-xs font-mono text-[var(--brand)] dark:text-[var(--accent)] hover:underline"
>
{sectionDocs.length} docs →
</Link>
</div>
<h3 className="font-semibold text-base text-[var(--text-primary)] group-hover:text-[#5e6ad2] dark:group-hover:text-[#7170ff] transition-colors mb-1.5">
<h3 className="font-semibold text-base text-[var(--text-primary)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors mb-1.5">
{s.label}
</h3>
<p className="text-xs text-[var(--text-muted)] mb-4 line-clamp-2 leading-relaxed">
@@ -265,7 +265,7 @@ export default async function HomePage() {
</h2>
<Link
href="/docs"
className="text-xs text-[#5e6ad2] dark:text-[#7170ff] hover:underline font-medium"
className="text-xs text-[var(--brand)] dark:text-[var(--accent)] hover:underline font-medium"
>
View all ({all.length}) →
</Link>
@@ -281,7 +281,7 @@ export default async function HomePage() {
className="group block bg-[var(--bg-surface)] hover:bg-[var(--bg-surface-hover)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-xl p-4 transition-all shadow-sm"
>
<div className="flex items-center justify-between gap-2 mb-2">
<span className="text-xs font-mono text-[#5e6ad2] dark:text-[#7170ff] uppercase">
<span className="text-xs font-mono text-[var(--brand)] dark:text-[var(--accent)] uppercase">
{sInfo?.icon} {d.section}
</span>
<time className="text-[11px] text-[var(--text-dim)] font-mono">
@@ -291,7 +291,7 @@ export default async function HomePage() {
})}
</time>
</div>
<h3 className="font-medium text-sm text-[var(--text-primary)] group-hover:text-[#5e6ad2] dark:group-hover:text-[#7170ff] transition-colors line-clamp-1 mb-2">
<h3 className="font-medium text-sm text-[var(--text-primary)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors line-clamp-1 mb-2">
{d.title}
</h3>
{d.tags.length > 0 && (
+7 -7
View File
@@ -103,11 +103,11 @@ function SearchContent() {
value={query}
onChange={handleChange}
placeholder="Search by keywords, challenge names, tools, concepts..."
className="w-full pl-11 pr-10 py-3 bg-[var(--bg-elevated)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-lg text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--brand)] focus:ring-2 focus:ring-[#5e6ad2]/20 text-base transition-all"
className="w-full pl-11 pr-10 py-3 bg-[var(--bg-elevated)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-lg text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--brand)] focus:ring-2 focus:ring-[var(--brand)]/20 text-base transition-all"
autoFocus
/>
<svg
className="w-5 h-5 text-[#5e6ad2] dark:text-[#7170ff] absolute left-3.5 top-3.5"
className="w-5 h-5 text-[var(--brand)] dark:text-[var(--accent)] absolute left-3.5 top-3.5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@@ -144,7 +144,7 @@ function SearchContent() {
title={m.desc}
className={`px-3 py-1 rounded-md border font-medium transition-all ${
mode === m.id
? "bg-[#5e6ad2] border-[#5e6ad2] text-white shadow-sm"
? "bg-[var(--brand)] border-[var(--brand)] text-white shadow-sm"
: "bg-[var(--bg-elevated)] border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--text-primary)]"
}`}
>
@@ -187,7 +187,7 @@ function SearchContent() {
{/* Loading State */}
{loading && (
<div className="flex items-center gap-3 py-8 text-[var(--text-muted)] text-sm">
<div className="w-5 h-5 border-2 border-[#5e6ad2] border-t-transparent rounded-full animate-spin" />
<div className="w-5 h-5 border-2 border-[var(--brand)] border-t-transparent rounded-full animate-spin" />
<span>Searching knowledge base...</span>
</div>
)}
@@ -241,7 +241,7 @@ function SearchContent() {
>
<div className="flex items-center justify-between gap-3 mb-2">
<div className="flex items-center gap-2">
<span className="text-xs font-mono px-2 py-0.5 rounded bg-[#5e6ad2]/10 border border-[#5e6ad2]/30 text-[#5e6ad2] dark:text-[#7170ff] uppercase">
<span className="text-xs font-mono px-2 py-0.5 rounded bg-[var(--brand)]/10 border border-[var(--brand)]/30 text-[var(--brand)] dark:text-[var(--accent)] uppercase">
{r.section}
</span>
<span className="text-xs text-[var(--text-dim)] font-mono truncate">
@@ -253,13 +253,13 @@ function SearchContent() {
</span>
</div>
<h2 className="text-base font-semibold text-[var(--text-primary)] group-hover:text-[#5e6ad2] dark:group-hover:text-[#7170ff] transition-colors mb-2">
<h2 className="text-base font-semibold text-[var(--text-primary)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors mb-2">
{r.title}
</h2>
{r.snippet && (
<p
className="text-xs text-[var(--text-muted)] leading-relaxed line-clamp-2 [&>mark]:bg-[#5e6ad2]/30 [&>mark]:text-[var(--text-primary)] [&>mark]:px-1 [&>mark]:rounded [&>mark]:font-medium"
className="text-xs text-[var(--text-muted)] leading-relaxed line-clamp-2 [&>mark]:bg-[var(--brand)]/30 [&>mark]:text-[var(--text-primary)] [&>mark]:px-1 [&>mark]:rounded [&>mark]:font-medium"
dangerouslySetInnerHTML={{ __html: r.snippet }}
/>
)}