fix: replace all hardcoded hex colors with CSS theme variables
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:
@@ -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">
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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)]"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -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)]"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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 && (
|
||||
|
||||
@@ -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 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user