mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-04 17:18:35 +02:00
Complete rework of the docs site, in three passes squashed into one change: 1. P0 fact corrections across the core-loop pages (agent access model, comment triggers and coalescing, issue lifecycle, offline/queue semantics, task states and retries, dispatch via realtime push). 2. Chinese rewrite as the authoring language: final 12-group IA, new "Core concepts" and "Put agents to work" pages, curated welcome page, permanent-UI quickstart (no onboarding-wizard dependency), operator reference restored (CLI command reference, webhook response codes, task state/timeout tables, config set keys, upgrade notes). 3. Verification and sync: every load-bearing claim re-checked against current main with file:line evidence (migration 103 auto-backfill, CLI-only trigger toggling, duplication semantics, COOKIE_DOMAIN, GitHub App variables, Qwen Code as the 17th provider); register standards applied to all 39 pages; en/ja/ko synced from zh with same-language heading anchors (verified, zero dead links); 17 product screenshots plus 2 diagrams wired in. Supporting fixes: i18n middleware no longer swallows /images assets, sidebar folder indent survives custom link padding, unused editorial components removed. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
738 lines
24 KiB
CSS
738 lines
24 KiB
CSS
@import "tailwindcss";
|
|
@import "fumadocs-ui/css/preset.css";
|
|
@import "../../../packages/ui/styles/tokens.css";
|
|
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
@source "../../../packages/ui/**/*.{ts,tsx}";
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Font stack. `--font-inter` is the next/font Inter family (+ synthetic
|
|
* size-adjusted fallback), set on <html> by inter.variable in app/[lang]/layout.tsx.
|
|
* `--font-sans` is composed here in static CSS so it can be overridden per
|
|
* `<html lang>` and stays CSP-safe (no inline <style>). Tailwind's `font-sans`
|
|
* utility resolves `var(--font-sans)`. Mirrors apps/web/app/globals.css.
|
|
*
|
|
* Default (en / zh / ko): Latin → Inter, CJK → Chinese then Korean. Chinese MUST
|
|
* stay before Korean so zh users don't get Korean Hanja glyph shapes.
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
:root {
|
|
--font-sans: var(--font-inter), -apple-system, BlinkMacSystemFont, "Segoe UI",
|
|
"PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", "Apple SD Gothic Neo",
|
|
"Malgun Gothic", "Noto Sans CJK KR", sans-serif;
|
|
}
|
|
|
|
/* Japanese: Kanji share the Han Unicode block with Chinese and CSS fallback
|
|
order is not affected by `<html lang>`, so promote a Japanese-first CJK chain
|
|
only for Japanese docs (`<html lang="ja">`). `[lang|="ja"]` is the BCP-47
|
|
language-range selector — matches exactly `ja` or `ja-<region>`, never
|
|
unrelated subtags like `jam`. Inter still leads for Latin. */
|
|
html[lang|="ja"] {
|
|
--font-sans: var(--font-inter), "Hiragino Sans", "Hiragino Kaku Gothic ProN",
|
|
"Yu Gothic", "YuGothic", "Meiryo", "Noto Sans CJK JP", "Noto Sans JP",
|
|
-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
|
|
"Microsoft YaHei", "Noto Sans CJK SC", "Apple SD Gothic Neo", "Malgun Gothic",
|
|
"Noto Sans CJK KR", sans-serif;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Multica Docs — editorial visual identity (v2)
|
|
*
|
|
* Docs site is intentionally distinct from the product app: warm-paper
|
|
* background, editorial serif headings (Source Serif 4), indigo accent,
|
|
* ruled dividers. Product app keeps its cool-gray dense Linear-style; docs
|
|
* reads like a literary publication. Same split as Stripe, Cursor, Linear.
|
|
*
|
|
* Implementation: docs-scoped token override on top of Multica tokens
|
|
* (whose @theme inline references read --background / --foreground / etc
|
|
* at runtime, so re-pointing the vars cascades through fumadocs's full
|
|
* --color-fd-* bridge below).
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
:root {
|
|
--fd-page-width: 1080px;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Editorial palette — light
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
:root {
|
|
--background: oklch(0.972 0.003 85); /* near-white, faint warm — matches landing #f7f7f5 */
|
|
--foreground: oklch(0.182 0.012 50); /* warm ink */
|
|
--muted: oklch(0.955 0.006 85); /* hairline, slightly warmer than bg */
|
|
--muted-foreground: oklch(0.482 0.012 65); /* warm muted */
|
|
--card: oklch(0.99 0.002 85); /* paper — near white */
|
|
--card-foreground: oklch(0.182 0.012 50);
|
|
--popover: oklch(0.99 0.002 85);
|
|
--popover-foreground: oklch(0.182 0.012 50);
|
|
--primary: oklch(0.55 0.16 255); /* Multica brand */
|
|
--primary-foreground: oklch(0.985 0.008 85);
|
|
--secondary: oklch(0.945 0.012 85);
|
|
--secondary-foreground: oklch(0.182 0.012 50);
|
|
--accent: oklch(0.945 0.022 255); /* brand soft wash */
|
|
--accent-foreground: oklch(0.46 0.16 255); /* brand ink */
|
|
--border: oklch(0.91 0.014 85); /* ruled lines */
|
|
--input: oklch(0.91 0.014 85);
|
|
--ring: oklch(0.55 0.16 255);
|
|
--sidebar: oklch(0.99 0.002 85); /* paper — same as card */
|
|
--sidebar-foreground: oklch(0.182 0.012 50);
|
|
--sidebar-accent: oklch(0.945 0.006 85); /* subtle cream, hover/active fill */
|
|
--sidebar-accent-foreground: oklch(0.182 0.012 50);
|
|
--sidebar-border: oklch(0.91 0.014 85);
|
|
|
|
/* Docs-only extras (not bridged to fumadocs slots) */
|
|
--docs-rule: oklch(0.835 0.018 85); /* heavier rule */
|
|
--docs-faint: oklch(0.72 0.018 75); /* faintest accent */
|
|
--docs-code-bg: oklch(0.94 0.018 85); /* warm beige code surface */
|
|
--docs-code-border: oklch(0.89 0.018 85);
|
|
--docs-terminal-bg: oklch(0.18 0.012 50); /* terminal warm dark */
|
|
--docs-terminal-fg: oklch(0.92 0.012 80);
|
|
--docs-terminal-accent: oklch(0.65 0.16 255);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Editorial palette — dark (warm dark, NOT Multica's cool dark)
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
.dark {
|
|
--background: oklch(0.18 0.008 50);
|
|
--foreground: oklch(0.95 0.012 85);
|
|
--muted: oklch(0.22 0.008 50);
|
|
--muted-foreground: oklch(0.65 0.012 75);
|
|
--card: oklch(0.21 0.008 50);
|
|
--card-foreground: oklch(0.95 0.012 85);
|
|
--popover: oklch(0.22 0.008 50);
|
|
--popover-foreground: oklch(0.95 0.012 85);
|
|
--primary: oklch(0.7 0.15 255); /* Multica brand — dark */
|
|
--primary-foreground: oklch(0.18 0.008 50);
|
|
--secondary: oklch(0.24 0.008 50);
|
|
--secondary-foreground: oklch(0.95 0.012 85);
|
|
--accent: oklch(0.3 0.05 255); /* brand soft wash — dark */
|
|
--accent-foreground: oklch(0.78 0.14 255); /* brand ink — dark */
|
|
--border: oklch(0.28 0.012 50);
|
|
--input: oklch(0.28 0.012 50);
|
|
--ring: oklch(0.7 0.15 255);
|
|
--sidebar: oklch(0.21 0.008 50);
|
|
--sidebar-foreground: oklch(0.95 0.012 85);
|
|
--sidebar-accent: oklch(0.26 0.01 50); /* warm neutral, hover/active fill — dark */
|
|
--sidebar-accent-foreground: oklch(0.95 0.012 85);
|
|
--sidebar-border: oklch(0.28 0.012 50);
|
|
|
|
--docs-rule: oklch(0.36 0.012 50);
|
|
--docs-faint: oklch(0.42 0.012 50);
|
|
--docs-code-bg: oklch(0.165 0.008 50);
|
|
--docs-code-border: oklch(0.26 0.012 50);
|
|
--docs-terminal-bg: oklch(0.155 0.012 50);
|
|
--docs-terminal-fg: oklch(0.92 0.012 80);
|
|
--docs-terminal-accent: oklch(0.78 0.14 255);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Fumadocs slot bridge
|
|
*
|
|
* Map fumadocs's --color-fd-* slots to our (now warm) Multica tokens.
|
|
* @theme inline keeps the var() reference live so the cascade resolves
|
|
* at runtime — same pattern tokens.css uses.
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
@theme inline {
|
|
--color-fd-background: var(--background);
|
|
--color-fd-foreground: var(--foreground);
|
|
--color-fd-muted: var(--muted);
|
|
--color-fd-muted-foreground: var(--muted-foreground);
|
|
--color-fd-popover: var(--popover);
|
|
--color-fd-popover-foreground: var(--popover-foreground);
|
|
--color-fd-card: var(--card);
|
|
--color-fd-card-foreground: var(--card-foreground);
|
|
--color-fd-border: var(--border);
|
|
--color-fd-primary: var(--primary);
|
|
--color-fd-primary-foreground: var(--primary-foreground);
|
|
--color-fd-secondary: var(--secondary);
|
|
--color-fd-secondary-foreground: var(--secondary-foreground);
|
|
--color-fd-accent: var(--accent);
|
|
--color-fd-accent-foreground: var(--accent-foreground);
|
|
--color-fd-ring: var(--ring);
|
|
}
|
|
|
|
/* Sidebar uses dedicated --sidebar-* tokens so it sits a hair off the main
|
|
* canvas. Fumadocs renders it as #nd-sidebar (desktop) and
|
|
* #nd-sidebar-mobile (mobile drawer); both IDs need the override. */
|
|
#nd-sidebar,
|
|
#nd-sidebar-mobile {
|
|
--color-fd-background: var(--sidebar);
|
|
--color-fd-foreground: var(--sidebar-foreground);
|
|
--color-fd-muted: var(--sidebar-accent);
|
|
--color-fd-muted-foreground: var(--sidebar-foreground);
|
|
--color-fd-accent: var(--sidebar-accent);
|
|
--color-fd-accent-foreground: var(--sidebar-accent-foreground);
|
|
--color-fd-border: var(--sidebar-border);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Editorial typography
|
|
*
|
|
* Body keeps Inter for legibility (especially CJK where serif Latin clashes
|
|
* with sans CJK). Headings switch to Source Serif 4 for the editorial
|
|
* signature. Italic is intentionally avoided — Chinese italic is a CSS
|
|
* synthetic slant against upright-designed glyphs and reads as broken.
|
|
* Emphasis is carried by serif/sans contrast, brand color, and weight.
|
|
*
|
|
* Sizing:
|
|
* - DocsHero h1 (welcome page only): 44px serif, brand-color em accent
|
|
* - prose h1 (guide / reference pages): 30px serif
|
|
* - prose h2: 26px serif (no italic)
|
|
* - prose h3: 13px sans uppercase label
|
|
* - body: 15.5px (kept from previous build — proven reading size for CN)
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
article:has(.prose),
|
|
.prose {
|
|
font-size: 0.96875rem; /* 15.5px */
|
|
line-height: 1.7;
|
|
}
|
|
|
|
/* DocsTitle h1 (Fumadocs hardcodes text-[1.75em] font-semibold — utility
|
|
* specificity 0,1,0 beats plain article > h1 0,0,2; !important wins). */
|
|
article > h1 {
|
|
font-family: var(--font-serif), ui-serif, serif !important;
|
|
font-size: 1.875rem !important; /* 30px guide-page heading */
|
|
font-weight: 400 !important;
|
|
letter-spacing: -0.018em;
|
|
line-height: 1.15;
|
|
margin-bottom: 0.5em;
|
|
color: var(--foreground);
|
|
}
|
|
|
|
/* Lead paragraph below DocsTitle */
|
|
article > p.text-lg {
|
|
font-family: var(--font-serif), ui-serif, serif;
|
|
font-size: 1.125rem; /* 18px serif lede */
|
|
line-height: 1.55;
|
|
margin-bottom: 2rem;
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
/* Paragraph rhythm */
|
|
.prose :where(p):not(:where([class~="not-prose"] *)) {
|
|
margin-top: 0;
|
|
margin-bottom: 0.875rem;
|
|
color: oklch(from var(--foreground) calc(l + 0.06) c h);
|
|
}
|
|
.prose :where(p):not(:where([class~="not-prose"] *)):last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
.prose :where(p) strong {
|
|
color: var(--foreground);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.prose :where(ul, ol) {
|
|
margin-top: 0.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.prose h1 {
|
|
font-family: var(--font-serif), ui-serif, serif;
|
|
font-size: 1.875rem; /* 30px */
|
|
font-weight: 400;
|
|
letter-spacing: -0.02em;
|
|
line-height: 1.1;
|
|
margin-bottom: 0.5em;
|
|
color: var(--foreground);
|
|
}
|
|
/* Italic is avoided sitewide (Chinese italic = synthetic slant, looks broken).
|
|
* Force any italicized element to non-italic in prose. Tailwind Typography
|
|
* defaults blockquote to italic; we also undo it here. Emphasis is carried
|
|
* by brand color + font-weight in headings, foreground+weight in body. */
|
|
.prose em,
|
|
.prose i,
|
|
.prose cite,
|
|
.prose blockquote,
|
|
.prose blockquote p {
|
|
font-style: normal;
|
|
}
|
|
.prose h1 em {
|
|
color: var(--primary);
|
|
font-weight: 500;
|
|
}
|
|
.prose p em,
|
|
.prose li em {
|
|
color: var(--foreground);
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* The page article is a flex column (gap-4), so the prose body's first
|
|
element must not stack its own top margin on top of that gap. */
|
|
.prose > :first-child {
|
|
margin-top: 0 !important;
|
|
}
|
|
|
|
.prose h2 {
|
|
font-family: var(--font-serif), ui-serif, serif;
|
|
font-size: 1.625rem; /* 26px */
|
|
font-weight: 400;
|
|
letter-spacing: -0.015em;
|
|
line-height: 1.3;
|
|
margin-top: 2em;
|
|
margin-bottom: 0.5em;
|
|
color: var(--foreground);
|
|
scroll-margin-top: 80px;
|
|
}
|
|
|
|
/* h3 = small uppercase sans label, ruled-bottom — v2 editorial signature */
|
|
.prose h3 {
|
|
font-family: var(--font-sans), system-ui, sans-serif;
|
|
font-size: 0.8125rem; /* 13px */
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
color: var(--muted-foreground);
|
|
margin-top: 2.25em;
|
|
margin-bottom: 0.75em;
|
|
padding-bottom: 0.25em;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.prose h4 {
|
|
font-family: var(--font-serif), ui-serif, serif;
|
|
font-size: 1.0625rem; /* 17px */
|
|
font-weight: 500;
|
|
letter-spacing: -0.005em;
|
|
line-height: 1.4;
|
|
margin-top: 1.5em;
|
|
margin-bottom: 0.375em;
|
|
color: var(--foreground);
|
|
}
|
|
|
|
/* Description paragraph (fumadocs adds text-lg + muted) */
|
|
.prose > p:first-of-type:has(+ *) {
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Links — Vercel-style hairline underline, reveal brand on hover
|
|
*
|
|
* Markdown-heavy prose can put 4+ inline links in a single sentence; a
|
|
* permanent brand-color underline on every one turns the paragraph into
|
|
* highlighter spam. The trick isn't "no underline" — it's underlining
|
|
* in the hairline border color so the line exists but visually recedes.
|
|
* Hover swaps both text and underline to brand color (no thickness
|
|
* change) — the link "arrives" as a single color shift.
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
.prose
|
|
a:not([data-card]):not(.not-prose):not(
|
|
:where([class~="not-prose"] *)
|
|
) {
|
|
color: var(--foreground);
|
|
font-weight: 500;
|
|
text-decoration: underline;
|
|
text-decoration-color: var(--border);
|
|
text-decoration-thickness: 1px;
|
|
text-underline-offset: 3px;
|
|
transition: text-decoration-color 150ms, color 150ms;
|
|
}
|
|
.prose
|
|
a:not([data-card]):not(.not-prose):not(
|
|
:where([class~="not-prose"] *)
|
|
):hover {
|
|
color: var(--primary);
|
|
text-decoration-color: var(--primary);
|
|
}
|
|
|
|
/* Callout already carries four visual signals (left brand bar, brand-wash
|
|
* bg, uppercase NOTE label, body). Another decoration over-loads it — so
|
|
* links inside a callout drop the underline entirely. Color shift on
|
|
* hover is the full affordance. */
|
|
.prose
|
|
div.shadow-md:has(> [role="none"])
|
|
a:not([data-card]):not(.not-prose):not(
|
|
:where([class~="not-prose"] *)
|
|
),
|
|
.prose
|
|
div.shadow-md:has(> [role="none"])
|
|
a:not([data-card]):not(.not-prose):not(
|
|
:where([class~="not-prose"] *)
|
|
):hover {
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* Inline code — warm beige chip, accent-color text */
|
|
.prose :not(pre) > code {
|
|
background: var(--docs-code-bg);
|
|
color: var(--accent-foreground);
|
|
padding: 0.125rem 0.375rem;
|
|
border-radius: 3px;
|
|
font-family: var(--font-mono), ui-monospace, monospace;
|
|
font-size: 0.875em;
|
|
font-weight: 500;
|
|
box-decoration-break: clone;
|
|
-webkit-box-decoration-break: clone;
|
|
}
|
|
.prose :not(pre) > code::before,
|
|
.prose :not(pre) > code::after {
|
|
content: none;
|
|
}
|
|
|
|
/* Lists */
|
|
.prose :where(ul, ol) > li {
|
|
margin-top: 0.375em;
|
|
margin-bottom: 0.375em;
|
|
padding-inline-start: 0.375em;
|
|
}
|
|
.prose :where(ul) > li::marker {
|
|
color: var(--docs-faint);
|
|
content: "— ";
|
|
font-family: var(--font-serif), serif;
|
|
}
|
|
.prose :where(ol) > li::marker {
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
/* Blockquote — editorial accent rule, serif voice */
|
|
.prose blockquote {
|
|
font-family: var(--font-serif), ui-serif, serif;
|
|
font-weight: 400;
|
|
font-size: 1.0625rem;
|
|
line-height: 1.55;
|
|
color: var(--foreground);
|
|
border-inline-start-width: 2px;
|
|
border-inline-start-color: var(--primary);
|
|
padding-inline-start: 1.25em;
|
|
margin-block: 1.5em;
|
|
quotes: none;
|
|
}
|
|
.prose blockquote p::before,
|
|
.prose blockquote p::after {
|
|
content: none;
|
|
}
|
|
|
|
/* Tables — hairline below thead only, no outer frame (Stripe / Linear
|
|
* docs convention). The heavier ink-color top rule v2 used on its API
|
|
* reference block is intentionally not applied here — that treatment is
|
|
* "this is a formal declaration"; regular guide tables want quiet. */
|
|
.prose table {
|
|
font-size: 0.9375em;
|
|
border-collapse: collapse;
|
|
margin-block: 1.5em;
|
|
}
|
|
.prose thead {
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.prose thead th {
|
|
font-family: var(--font-sans), system-ui, sans-serif;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
color: var(--muted-foreground);
|
|
padding-block: 0.5rem 0.625rem;
|
|
text-align: start;
|
|
}
|
|
.prose tbody tr {
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.prose tbody td {
|
|
padding-block: 0.875rem;
|
|
}
|
|
|
|
/* HR — heavier ruled separator */
|
|
.prose hr {
|
|
border: none;
|
|
border-top: 1px solid var(--docs-rule);
|
|
margin-block: 3em;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Callout — editorial 2px accent bar + soft accent wash
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
.prose div.shadow-md:has(> [role="none"]) {
|
|
box-shadow: none !important;
|
|
border-radius: 0 4px 4px 0 !important;
|
|
background: var(--accent) !important;
|
|
border: none !important;
|
|
border-inline-start: 2px solid var(--primary) !important;
|
|
padding: 0.875rem 1.125rem !important;
|
|
gap: 0.625rem !important;
|
|
align-items: flex-start;
|
|
margin-block: 1.5rem;
|
|
}
|
|
|
|
.prose div.shadow-md:has(> [role="none"]) > [role="none"] {
|
|
display: none;
|
|
}
|
|
|
|
.prose div.shadow-md:has(> [role="none"]) > div:last-child > p {
|
|
font-family: var(--font-sans), system-ui, sans-serif;
|
|
font-size: 0.6875rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--primary);
|
|
margin-bottom: 0.375rem;
|
|
}
|
|
|
|
.prose div.shadow-md:has(> [role="none"]) > div:last-child > div {
|
|
color: var(--foreground) !important;
|
|
font-size: 0.9375rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Cards — fallback editorial treatment for fumadocs's <Cards>/<Card>
|
|
* (NumberedCards is the showpiece; this keeps non-showpiece pages on tone)
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
.prose [data-card]:not(.peer) {
|
|
border-radius: 4px !important;
|
|
border: 1px solid var(--border) !important;
|
|
background: var(--card);
|
|
padding: 1.125rem !important;
|
|
transition: border-color 150ms, background-color 150ms !important;
|
|
}
|
|
|
|
.prose [data-card]:not(.peer):hover {
|
|
border-color: var(--primary) !important;
|
|
background: var(--card) !important;
|
|
}
|
|
|
|
.prose [data-card]:not(.peer) > div:first-child {
|
|
box-shadow: none !important;
|
|
border-radius: 0 !important;
|
|
padding: 0 !important;
|
|
background: transparent !important;
|
|
border: none !important;
|
|
color: var(--accent-foreground) !important;
|
|
margin-bottom: 0.75rem !important;
|
|
}
|
|
|
|
.prose [data-card]:not(.peer) > div:first-child svg {
|
|
color: var(--accent-foreground);
|
|
}
|
|
|
|
.prose [data-card]:not(.peer) h3 {
|
|
font-family: var(--font-serif), serif !important;
|
|
font-size: 1.125rem !important;
|
|
font-weight: 500 !important;
|
|
font-style: normal !important;
|
|
letter-spacing: -0.01em;
|
|
margin-bottom: 0.25rem !important;
|
|
margin-top: 0 !important;
|
|
text-transform: none !important;
|
|
border-bottom: none !important;
|
|
padding-bottom: 0 !important;
|
|
color: var(--foreground) !important;
|
|
}
|
|
|
|
.prose [data-card]:not(.peer) p {
|
|
color: var(--muted-foreground) !important;
|
|
line-height: 1.6;
|
|
font-size: 0.9375rem !important;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Sidebar — editorial chrome
|
|
*
|
|
* Section headers: small uppercase sans label, ruled bottom border.
|
|
* Items: muted-foreground at rest, foreground on hover.
|
|
* Active: solid background fill (mirrors product app's app-sidebar.tsx —
|
|
* data-active:bg-sidebar-accent / data-active:text-sidebar-accent-foreground).
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
#nd-sidebar p,
|
|
#nd-sidebar-mobile p {
|
|
font-family: var(--font-sans), system-ui, sans-serif;
|
|
font-size: 0.6875rem; /* 11px */
|
|
font-weight: 600;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--muted-foreground);
|
|
height: auto;
|
|
display: block;
|
|
margin-top: 1.5rem;
|
|
margin-bottom: 0.375rem;
|
|
padding-block: 0 0.375rem;
|
|
padding-inline-start: 0.5rem;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
#nd-sidebar p:first-child,
|
|
#nd-sidebar-mobile p:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
#nd-sidebar a[data-active],
|
|
#nd-sidebar-mobile a[data-active] {
|
|
height: auto;
|
|
padding: 0.375rem 0.625rem;
|
|
font-size: 0.84375rem; /* 13.5px */
|
|
border-radius: var(--radius-sm);
|
|
font-weight: 400;
|
|
line-height: 1.4;
|
|
letter-spacing: -0.005em;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
/* The padding shorthand above flattens the folder tree indent
|
|
(ps-(--sidebar-item-offset)); restore it for links that carry the
|
|
offset utility so children clear the vertical guide line. */
|
|
#nd-sidebar a[data-active][class*="ps-("],
|
|
#nd-sidebar-mobile a[data-active][class*="ps-("] {
|
|
padding-inline-start: var(--sidebar-item-offset, 0.625rem);
|
|
}
|
|
|
|
#nd-sidebar a[data-active="false"],
|
|
#nd-sidebar-mobile a[data-active="false"] {
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
#nd-sidebar a[data-active="false"]:hover,
|
|
#nd-sidebar-mobile a[data-active="false"]:hover {
|
|
background: color-mix(in oklab, var(--sidebar-accent) 70%, transparent);
|
|
color: var(--foreground);
|
|
}
|
|
|
|
/* Active — solid background fill, no left mark (matches product app) */
|
|
#nd-sidebar a[data-active="true"],
|
|
#nd-sidebar-mobile a[data-active="true"] {
|
|
background: var(--sidebar-accent) !important;
|
|
color: var(--sidebar-accent-foreground) !important;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Sidebar footer — drop the hard top rule. The scroll viewport already
|
|
* fades content into the footer, so a 1px line on top reads as a
|
|
* double-weight edge. Fumadocs hardcodes `border-t p-4 pt-2` on its
|
|
* SidebarFooter div; target that exact class trio inside the sidebar IDs
|
|
* so we don't touch any other border-t in the app. */
|
|
#nd-sidebar .border-t.p-4.pt-2,
|
|
#nd-sidebar-mobile .border-t.p-4.pt-2 {
|
|
border-top-width: 0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Top nav — quiet, ruled bottom
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
#nd-nav,
|
|
#nd-subnav {
|
|
border-bottom: 1px solid var(--border);
|
|
background: var(--card);
|
|
}
|
|
|
|
#nd-nav a,
|
|
#nd-subnav a {
|
|
font-size: 0.875rem;
|
|
color: var(--muted-foreground);
|
|
transition: color 150ms;
|
|
}
|
|
|
|
#nd-nav a:hover,
|
|
#nd-subnav a:hover {
|
|
color: var(--foreground);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* TOC (right rail) — quiet sans, brand-color when active
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
#nd-toc a {
|
|
font-size: 0.84375rem;
|
|
color: var(--muted-foreground);
|
|
padding-block: 0.3125rem;
|
|
letter-spacing: -0.005em;
|
|
transition: color 150ms;
|
|
}
|
|
|
|
#nd-toc a:hover {
|
|
color: var(--foreground);
|
|
}
|
|
|
|
#nd-toc a[data-active="true"] {
|
|
color: var(--primary);
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* TOC heading (Fumadocs renders "On this page" as an h3 / first p) */
|
|
#nd-toc h3,
|
|
#nd-toc > p:first-child {
|
|
font-family: var(--font-sans), system-ui, sans-serif;
|
|
font-size: 0.6875rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--muted-foreground);
|
|
margin-bottom: 0.625rem;
|
|
padding-bottom: 0.375rem;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Code blocks — warm beige (light) / warm dark (dark), NOT pinned
|
|
*
|
|
* Removes the previous "always-dark hero black" treatment. Code surface
|
|
* now follows page theme so it harmonizes with the warm-paper background
|
|
* in light mode and warm-dark in dark mode. Terminal-style blocks
|
|
* (handled by the custom <Terminal> component, not here) stay pinned to
|
|
* the deeper warm dark for the "shell session" feel.
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
article figure.shiki {
|
|
background: var(--docs-code-bg) !important;
|
|
border: 1px solid var(--docs-code-border) !important;
|
|
border-radius: 4px !important;
|
|
box-shadow: none !important;
|
|
margin-block: 1.25rem !important;
|
|
color: var(--foreground);
|
|
}
|
|
|
|
article figure.shiki pre {
|
|
background: transparent !important;
|
|
border: none !important;
|
|
border-radius: 0 !important;
|
|
color: inherit !important;
|
|
margin: 0 !important;
|
|
}
|
|
|
|
article figure.shiki > div[class*="overflow-auto"] {
|
|
font-size: 0.84375rem !important;
|
|
line-height: 1.7;
|
|
padding: 1rem 1.125rem !important;
|
|
}
|
|
|
|
/* Header bar (filename via ```lang filename="x.ts") */
|
|
article figure.shiki > div[class*="border-b"] {
|
|
border-bottom-color: var(--docs-code-border) !important;
|
|
background: var(--muted) !important;
|
|
color: var(--muted-foreground) !important;
|
|
font-family: var(--font-mono), ui-monospace, monospace;
|
|
font-size: 0.75rem;
|
|
letter-spacing: -0.005em;
|
|
}
|
|
|
|
/* Shiki tokens — pick the palette that matches page theme.
|
|
* Default (light): use --shiki-light. Override under .dark to --shiki-dark.
|
|
* Specificity: article figure.shiki code span (0,1,4) beats fumadocs's
|
|
* default, so no !important needed for the light path. */
|
|
article figure.shiki code span {
|
|
color: var(--shiki-light);
|
|
}
|
|
|
|
.dark article figure.shiki code span {
|
|
color: var(--shiki-dark);
|
|
}
|
|
|
|
/* Copy button on code blocks */
|
|
article figure.shiki button {
|
|
color: var(--muted-foreground) !important;
|
|
background: transparent !important;
|
|
}
|
|
article figure.shiki button:hover {
|
|
color: var(--foreground) !important;
|
|
background: var(--muted) !important;
|
|
}
|