Files
multica/apps/docs/app/global.css
Anderson Shindy Oki 1aa742053b i18n: add japanese locale (MUL-2893) (#3538)
* i18n: add japanese locale

* fix: spacing issues

* refactor

* fix(desktop): set <html lang> before paint to avoid JA Kanji font flash

Switch the documentElement.lang sync from useEffect to useLayoutEffect so
lang is committed before the first paint. Otherwise Japanese desktop users
saw one frame of Kanji rendered with the Chinese-first fallback stack before
the html[lang|="ja"] CJK override applied. Also fix the stale selector in the
HTML_LANG comment (html[lang^="ja"] -> html[lang|="ja"]).

Addresses review nits on MUL-2893.

Co-authored-by: multica-agent <github@multica.ai>

* fix(docs): tokenize the ideographic iteration mark in JA search

Add U+3005 (々) to the Japanese search tokenizer character class. It sits just
below the kana blocks, so words like 様々 / 日々 / 個々 previously dropped the
mark and split awkwardly, hurting recall.

Addresses a review nit on MUL-2893.

Co-authored-by: multica-agent <github@multica.ai>

* fix(i18n): restore ja locale parity after merging main

Merging main brought new EN strings into agents/chat/onboarding/settings/
squads that the ja bundle (authored against an older snapshot) lacked, breaking
the locales parity test. Add the Japanese translations for the new keys
(workspace logo upload, agents runtime filter, chat session-history stop
dialog, onboarding social_github, squad archived status) and drop the two
renamed chat window keys (active_group / archived_group) that EN removed in
favour of history_group.

Fixes the failing @multica/views parity.test.ts on the FE CI for MUL-2893.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: J <j@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
2026-06-02 14:29:29 +08:00

710 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;
}
.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) {
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):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),
.prose div.shadow-md:has(> [role="none"]) a:not([data-card]):not(.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;
}
#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;
}