Files
sim/apps/docs/app/global.css
T
Waleed 7545391cb3 feat(docs): render workflow previews with the shared editor renderer (#5277)
* chore(workflow-renderer): declare @sim/emcn dep + wire the package into docs

Adds the missing @sim/emcn peer/dev dependency to @sim/workflow-renderer (it imports @sim/emcn in every View but resolved only via workspace hoisting). Wires apps/docs to consume @sim/workflow-renderer (dependency, transpilePackages, Tailwind @source) and adds remark-breaks (pulled transitively via the barrel's NoteBlockView export) — mirroring the @sim/emcn integration. Foundation for migrating the docs workflow-preview fork onto the shared Views. Build resolves the package/@source/remark-breaks cleanly.

* feat(docs): render loop/parallel containers with the shared SubflowNodeView

Replaces the forked PreviewContainerNode with a thin DocsContainerNode that maps the static preview data to SubflowNodeView's read-only (isPreview) props — no stores or hooks. Adds the block size to the preview node data so the view can size itself, and corrects the parallel example's start-edge handle id to 'parallel-start-source' (the view derives the handle id from kind). Deletes preview-container-node.tsx. Container colors/icons are now owned by the shared view (loop=blue, parallel=yellow).

* feat(docs): render block nodes with the shared WorkflowBlockView

Replaces the forked PreviewBlockNode with a thin DocsBlockNode that maps the static preview data to WorkflowBlockView's props — store-free, builds the subblock rows (condition/router Context+routes/default + tools + error) via SubBlockRowView, strips branch-id prefixes so the view's regenerated handle ids match, remaps router->router_v2, and keeps the framer-motion dim/stagger wrapper. Promotes resolveIcon into block-icons.tsx, adds the --workflow-edge token to docs global.css, deletes preview-block-node.tsx. The canvas diagrams now render with the real editor's view.

* refactor(workflow-renderer): make editor-only WorkflowBlockView props optional

The child-deploy, schedule, and webhook badge props (and their callbacks) only matter in the editor. Mark them optional and optional-chain the three callbacks so read-only consumers (docs, academy) can omit the whole group instead of passing ~18 explicit off-values. The editor still passes them, so its behavior is byte-identical (verified: apps/sim type-check clean). DocsBlockNode drops the off-props.

* feat(docs): replace how-it-runs static diagrams with live WorkflowPreview

Swaps the four static PNGs on the how-it-runs page for live, app-styled WorkflowPreview diagrams (concurrency, combination, condition+router branching, error path). Adds the four example workflows and renders error-port edges red to match the editor. The English page only; the translated execution/basics pages keep the PNGs.

* refactor(workflow-renderer): the view owns condition/router/error rows

Both the editor container and the docs adapter hand-built the condition/router/error summary rows in an order that had to stay in lockstep with the view's absolute handle-offset math — a three-way coupling with nothing enforcing it. The view now renders those rows itself from the conditionRows/routerRows it already receives (plus a routerContextValue prop for the router's Context row), so row order and handle geometry live together in one place. Both containers pass only data and their non-branch rows.

Editor is byte-identical: getDisplayValue moves to where conditionRows/routerRows are built; the no-subBlock SubBlockRow path is already an exact SubBlockRowView(title, value) passthrough; the error row stays gated on shouldShowDefaultHandles. Verified apps/sim type-check clean. Docs now also renders the error row on condition/router blocks, which the real editor already did (shouldShowDefaultHandles is true for them) — an alignment fix.

* refactor(docs): drop the parallel --wp-* token layer for the app/emcn tokens

The workflow-preview ran a 25-token --wp-* mirror (22 were pure aliases of app tokens docs already defines) plus a .wp-scope wrapper class. Replaces every var(--wp-X) with its canonical app/emcn token (--wp-edge->--workflow-edge, --wp-highlight->--brand-secondary, badges->--badge-*, etc.), adds the one missing token (--divider), and deletes the .wp-scope blocks + class. Visually identical (aliases resolve to the same values); the preview now inherits the same design tokens as the shared views and the rest of the app instead of a hand-rolled parallel set.

* refactor(docs): adopt emcn Badge + dedup resolveIcon in workflow-preview

output-bundle's hand-rolled type badge (BADGE_COLORS + a styled span) becomes the emcn Badge (its green/blue/orange/purple/gray variants use the identical --badge-* tokens). resolveIcon, which had three copies, is now imported once from block-icons by output-bundle and block-inspector.

* refactor(docs): rebuild the preview inspector on emcn chip primitives

The lightbox inspector was a hand-rolled facsimile (raw divs + a CONTROL class string + inline dashed borders). It now composes from the same @sim/emcn primitives the live editor's sub-block controls wrap — ChipSelect/ChipInput/ChipTextarea(viewOnly)/ChipSwitch/ChipTag/FieldDivider/Label — so it reads as the real editor panel, fed example data (read-only, full opacity via readOnly/viewOnly, not greyed). Slider stays minimal (no emcn equivalent) but on app tokens. Props API and embedded/standalone modes unchanged.

* refactor(docs): render the block-reference hero through the shared View

Retires the hand-rolled BlockCard (a parallel reimplementation of WorkflowBlockView) and the BlockDisplaySpec data model. Each block hero is now a single-block PreviewWorkflow (block-display-workflows.ts) rendered through the same toReactFlowElements -> DocsBlockNode -> WorkflowBlockView pipeline as the diagrams, mounted in a minimal fitView ReactFlow (maxZoom 1.3, no canvas chrome). A single block can no longer drift from the canvas.

* fix(docs): define sim's type scale + align the preview inspector to the editor

Docs Tailwind v4 never defined sim's custom font sizes (text-small/caption/md/micro), so emcn components (Label, Badge, the shared views) fell back to inherited sizes — the inspector labels rendered huge. Adds the type scale to the docs @theme. Also aligns the inspector header to the real editor panel (surface-4 bar, size-[18px] rounded-sm icon, text-sm name) and removes the Connections section (and its now-dead prop/wiring).

* fix(docs): inspector shows the full field list + dragged positions persist

Inspector: shows the block type's full field list (from the reference data) with the example's values overlaid, so it reads like the editor panel instead of only the canvas summary rows. Drag: selecting another block no longer relayouts the canvas — node positions the viewer dragged are preserved across highlight/selection changes (only a different workflow relayouts).

* feat(docs): highlight <> references + env vars; hide Ask AI over the lightbox; respace blocks

Inspector text fields render the value with <...> block references and {{...}} environment variables highlighted in brand-secondary (a lean read-only port of the editor's formatDisplayText), in the canonical chip field chrome. The floating Ask AI widget is hidden while a preview lightbox is open. Plus the example-data respacing so the editor-faithful Error row no longer makes stacked blocks overlap.

* fix(docs): make per-type field templates match the real block registry

Audited every block type's field list (the source the inspector + block-reference heroes render) against apps/sim/blocks/blocks/*. Corrected drift to the registry's default-visible fields, titles, and order: agent gains Temperature; router gains Model; wait gains Async; schedule rewritten (default is Daily, not minutes); webhook_trigger expanded to its real default-visible set; human_in_the_loop notification title fixed. Provider-credential and advanced-mode fields stay hidden, matching the editor. Canvas diagrams keep their clean curated rows; the inspector now shows the full, real field list per the chosen clean-canvas/full-inspector split.

* improvement(docs): taller default preview height so respaced diagrams aren't shrunk

Bumps the default WorkflowPreview height 260->300 (the respaced, editor-faithful blocks are taller, so fitView was shrinking diagrams that relied on the default). The tall how-it-runs routing diagram gets 400.

* improvement(docs): zoomable inline preview + taller default + themed controls

The inline preview is now zoomable outside the lightbox: adds react-flow zoom/fit Controls (themed to the dark canvas chrome) and enables pinch-zoom, while keeping scroll-zoom off so the page still scrolls over the diagram. Pan-drag and click-block-to-inspect already worked. Default height 300->340.

* improvement(docs): click canvas to expand; click empty lightbox to deselect

Clicking the inline preview canvas opens the full lightbox; clicking empty space in the lightbox clears the selection, matching the real editor.

* improvement(docs): reveal inline zoom controls on hover only

The always-visible zoom controls felt heavy on the inline preview; they now fade in on hover (matching the expand button) and stay visible in the lightbox.

* improvement(docs): drop zoom controls on the inline preview

Inline preview keeps pinch-zoom, pan, drag, and click-to-expand; zoom buttons stay in the lightbox only.

* improvement(docs): remove zoom controls from the lightbox too

Both previews zoom via scroll/pinch and pan via drag; no on-canvas zoom buttons. Drops the Controls import and its theming CSS.

* improvement(docs): match the real canvas — flat background + editor edge geometry

Closes the last faithfulness gaps the audit found: removes the dot grid (the real editor hides its background — flat bg), aligns PreviewEdge to the editor's smoothstep math (borderRadius 8, offset 30) and 2px stroke (default + error edges), the selection ring to 1.75px, and minZoom to 0.1. Structural parity (blocks/handles/containers/colors/tokens) was already shared. Kept PreviewEdge rather than swapping to WorkflowEdgeView, which would clobber the docs-only highlight/dim/animate for no visual gain.

* improvement(docs): rebrand the docs assistant as 'Ask Sim', styled like the real chat input

Renames the floating assistant from 'Ask AI' to 'Ask Sim' (matching the platform's voice — you talk to Sim) and restyles the composer to mirror the home chat input: a rounded-2xl bordered field with the toolbar inside, and the same 28px circular send/stop button (the home's exact active/disabled colors + white/black arrow). Updates the lightbox hide-selector to the new label.

* improvement(docs): match Ask Sim message styling to the mothership chat

Aligns the user bubble (rounded-[16px] surface-5, text-base/primary, leading-23, max-w-85%) and the assistant markdown (text-base, 600 headings/strong, text-primary dashed-underline links, surface-5 code blocks) to the real mothership chat's user-message + chat-content treatment, instead of the prior generic text-sm rendering. The composer already mirrors the home user-input (rounded-2xl field + 28px circular send button).

* improvement(docs): compact single-row Ask Sim composer

The two-row layout left a tall dead gap (the docs widget has no toolbar buttons to fill the second row). The composer is now a single row — textarea with the circular send button inline — so it sits at the natural input height.

* fix(docs): pass the router Context value to the shared view

DocsBlockNode never set routerContextValue, so the view (which renders the router's Context row from that prop, not from rows) showed a blank Context even when the preview data authored a value like <start.input>. Extract it from the block's Context row and pass it through.

* fix(docs): don't apply a block-type field template that doesn't match the block

inspectorFieldsFor keyed the full field template purely off block.type, but some types are reused across roles (a table action block vs the table trigger, a webhook trigger vs the webhook action), so the wrong template was applied. Only use the template when the block's authored rows are actually a subset of it; otherwise fall back to the block's own rows.

* fix(docs): connect preview edges to subflow container handles

toReactFlowElements hardcoded targetHandle to 'target' and defaulted source handles to 'source', but Loop/Parallel containers (SubflowNodeView) expose a 'loop-end-source'/'parallel-end-source' output handle and a left input handle with no id. Edges into and out of containers therefore failed to connect. Resolve each edge end to the block's real handle based on whether it's a container.

* fix(docs): don't expand the inspector template for blocks with no rows

block.rows.every(...) is vacuously true for an empty rows array, so a block defined only by branches (e.g. a router in ROUTING_WORKFLOW) inherited the type template's invented field defaults. Require non-empty authored rows before applying the template.

* fix(docs): render blank branch/router-context values as '-' like the editor

The editor maps condition/router branch values and the router Context through getDisplayValue, which renders '-' for a blank value. DocsBlockNode mapped them to an empty string, so else branches and unset routes looked blank instead of matching the editor. Mirror getDisplayValue's empty-value handling.

* fix(docs): show '-' for blank inspector branch values, matching the canvas

inspectorFieldsFor passed raw branch.value into the lightbox branch fields, so an unset else route read blank in the inspector while DocsBlockNode (and the editor's getDisplayValue) render '-' on the canvas. Normalize the same way; drop the now-redundant placeholder.
2026-06-29 20:27:33 -07:00

1689 lines
50 KiB
CSS

@import "tailwindcss";
@import "fumadocs-ui/css/neutral.css";
@import "fumadocs-ui/css/preset.css";
@import "fumadocs-openapi/css/preset.css";
@source "../../../packages/emcn/src";
@source "../../../packages/workflow-renderer/src";
/* Prevent overscroll bounce effect on the page */
html,
body {
overscroll-behavior: none;
}
/* Prevent modals/dialogs from shifting layout via scroll-lock compensation */
html,
body {
padding-right: 0 !important;
margin-right: 0 !important;
}
@theme {
--color-fd-primary: var(--color-fd-foreground);
/* Sim's custom type scale — emcn components (Label, Badge, the shared block
views) use these names, so they must resolve here or text falls back to the
inherited size. Mirrors apps/sim/tailwind.config.ts. */
--text-micro: 10px;
--text-xs: 11px;
--text-caption: 12px;
--text-small: 13px;
--text-base: 15px;
--text-md: 16px;
}
/* Hide the floating Ask Sim widget while a workflow-preview lightbox is open. */
body.wp-lightbox-open [aria-label="Ask Sim"] {
display: none;
}
/* Pure white light mode background */
:root:not(.dark) {
--color-fd-background: hsl(0, 0%, 100%) !important;
}
/* Match landing page dark background (#1b1b1b) */
.dark {
--color-fd-background: hsl(0, 0%, 10.6%) !important;
--color-fd-card: hsl(0, 0%, 13%) !important;
--color-fd-popover: hsl(0, 0%, 14%) !important;
--color-fd-secondary: hsl(0, 0%, 15.5%) !important;
--color-fd-muted: hsl(0, 0%, 18%) !important;
}
/*
* emcn workspace tokens — mirrored from apps/sim/app/_styles/globals.css so the
* docs chip chrome (components/ui/chip.tsx, dropdown-menu.tsx, badges) renders
* identically to the platform. Keep values in sync with the main app.
*/
:root {
--bg: #fefefe;
--workflow-edge: #e0e0e0;
--surface-1: #fbfbfb;
--surface-2: #ffffff;
--surface-3: #f7f7f7;
--surface-4: #f5f5f5;
--surface-5: #f3f3f3;
--surface-6: #e5e5e5;
--surface-active: #ececec;
--border: #dedede;
--border-1: #e0e0e0;
--divider: #ededed;
--text-primary: #1a1a1a;
--text-secondary: #525252;
--text-tertiary: #5c5c5c;
--text-body: #3b3b3b;
--text-muted: #707070;
--text-subtle: #8c8c8c;
--text-icon: #525252;
--text-inverse: #ffffff;
--text-error: #ef4444;
--text-muted-inverse: #a0a0a0;
--brand-accent: #33c482;
--brand-accent-hover: #2dac72;
--brand-secondary: #33b4ff;
--surface-inverted: #1b1b1b;
--surface-inverted-hover: #363636;
--border-inverted: #363636;
--border-muted: #e4e4e4;
--badge-success-bg: #bbf7d0;
--badge-success-text: #15803d;
--badge-blue-bg: #bfdbfe;
--badge-blue-text: #1d4ed8;
--badge-purple-bg: #e9d5ff;
--badge-purple-text: #7c3aed;
--badge-amber-bg: #fde68a;
--badge-amber-text: #a16207;
--badge-orange-bg: #fed7aa;
--badge-orange-text: #c2410c;
--badge-error-bg: #fecaca;
--badge-error-text: #dc2626;
--badge-gray-bg: #e7e5e4;
--badge-gray-text: #57534e;
--scrollbar-thumb-color: #c0c0c0;
--scrollbar-thumb-hover-color: #a8a8a8;
--shadow-subtle: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
--shadow-medium: 0 4px 12px rgba(0, 0, 0, 0.1);
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.dark {
--bg: #1b1b1b;
--workflow-edge: #454545;
--surface-1: #1e1e1e;
--surface-2: #232323;
--surface-3: #242424;
--surface-4: #292929;
--surface-5: #363636;
--surface-6: #454545;
--surface-active: #2c2c2c;
--border: #333333;
--border-1: #3d3d3d;
--divider: #393939;
--text-primary: #e6e6e6;
--text-secondary: #cccccc;
--text-tertiary: #b3b3b3;
--text-body: #cdcdcd;
--text-muted: #787878;
--text-subtle: #7d7d7d;
--text-icon: #a0a0a0;
--text-inverse: #1b1b1b;
--text-error: #ef4444;
--text-muted-inverse: #b3b3b3;
--brand-accent: #33c482;
--brand-accent-hover: #2dac72;
--brand-secondary: #33b4ff;
--surface-inverted: #242424;
--surface-inverted-hover: #363636;
--border-inverted: #3d3d3d;
--border-muted: #424242;
--badge-success-bg: rgba(34, 197, 94, 0.2);
--badge-success-text: #86efac;
--badge-blue-bg: rgba(59, 130, 246, 0.2);
--badge-blue-text: #93c5fd;
--badge-purple-bg: rgba(168, 85, 247, 0.2);
--badge-purple-text: #d8b4fe;
--badge-amber-bg: rgba(245, 158, 11, 0.2);
--badge-amber-text: #fcd34d;
--badge-orange-bg: rgba(249, 115, 22, 0.2);
--badge-orange-text: #fdba74;
--badge-error-bg: #551a1a;
--badge-error-text: #fca5a5;
--badge-gray-bg: #3a3a3a;
--badge-gray-text: #a8a8a8;
--scrollbar-thumb-color: #5a5a5a;
--scrollbar-thumb-hover-color: #6a6a6a;
}
/* Scrollbars — platform thumb tokens, transparent track */
* {
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb-color) transparent;
}
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-track {
background: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb-color);
border-radius: 9999px;
}
*::-webkit-scrollbar-thumb:hover {
background-color: var(--scrollbar-thumb-hover-color);
}
/* Font family utilities */
.font-sans {
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.font-mono {
font-family: var(--font-geist-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
}
/* Platform UI font — Season Sans, used by the chip chrome to match the main app */
.font-season {
font-family: var(--font-season), system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial,
"Noto Sans", sans-serif;
}
:root {
--fd-border-sidebar: transparent !important;
--fd-nav-height: 92px; /* Custom navbar height (52px top + 40px tabs) */
/* Content container width used to center main content */
--spacing-fd-container: 1400px;
/* Edge gutter = leftover space on each side of centered container */
--edge-gutter: max(1rem, calc((100vw - var(--spacing-fd-container)) / 2));
/* How far sidebar/TOC shift inward from the edge gutter */
--sidebar-shift: 50px;
--sidebar-offset: max(0px, calc(var(--edge-gutter) - var(--sidebar-shift)));
--toc-shift: 50px;
--toc-offset: max(0px, calc(var(--edge-gutter) - var(--toc-shift)));
/* Inner padding for navbar and sidebar — single source of truth */
--nav-inset: 44px;
/* Extra gap between sidebar/TOC and the main text content */
--content-gap: 2.25rem;
}
/* Light mode navbar background */
:root:not(.dark) nav {
background-color: hsla(0, 0%, 100%, 0.85) !important;
}
/* Dark mode navbar background */
:root.dark nav {
background-color: hsla(0, 0%, 10.6%, 0.92) !important;
}
/* Floating sidebar appearance - remove background */
[data-sidebar-container],
#nd-sidebar {
background: transparent !important;
border: none !important;
--color-fd-muted: transparent !important;
--color-fd-card: transparent !important;
--color-fd-secondary: transparent !important;
}
aside[data-sidebar],
aside#nd-sidebar {
background: transparent !important;
border: none !important;
}
/* Remove the fade-out mask on sidebar scroll viewport (Fumadocs applies inline maskImage) */
[data-sidebar-placeholder] [data-radix-scroll-area-viewport],
#nd-sidebar [data-radix-scroll-area-viewport],
#nd-sidebar [data-radix-scroll-area-viewport] > div,
aside#nd-sidebar [data-radix-scroll-area-viewport] {
mask: none !important;
mask-image: none !important;
-webkit-mask: none !important;
-webkit-mask-image: none !important;
}
/* Fumadocs v16: Add sidebar placeholder styling for grid area */
[data-sidebar-placeholder] {
background: transparent !important;
}
/* Fumadocs v16: Hide sidebar panel (floating collapse button) */
[data-sidebar-panel] {
display: none !important;
}
/* Mobile only: Reduce gap between navbar and content (custom navbar hidden on mobile) */
@media (max-width: 1023px) {
#nd-docs-layout {
margin-top: -25px;
}
}
/* Hide TOC popover on tablet/medium screens (768px - 1279px) */
/* Keeps it visible on mobile (<768px) for easy navigation */
/* Desktop (>=1280px) already hides it via fumadocs xl:hidden */
@media (min-width: 768px) and (max-width: 1279px) {
#nd-docs-layout {
--fd-toc-popover-height: 0px !important;
}
[data-toc-popover] {
display: none !important;
}
}
/* Desktop only: Apply custom navbar offset, sidebar width and margin offsets */
/* On mobile, let fumadocs handle the layout natively */
@media (min-width: 1024px) {
:root {
--fd-banner-height: 92px !important; /* 52px top + 40px tabs */
}
#nd-docs-layout {
/* Use 100dvh so sidebar height = 100dvh - 92px (banner) = full viewport below navbar */
--fd-docs-height: 100dvh !important;
--fd-sidebar-width: 300px !important;
margin-left: var(--sidebar-offset) !important;
margin-right: var(--toc-offset) !important;
min-height: var(--fd-docs-height) !important;
}
/* Sidebar divider line — fixed to viewport, full height from navbar to bottom */
#nd-docs-layout::before {
content: "";
position: fixed;
top: 92px; /* below navbar */
bottom: 0;
left: calc(var(--sidebar-offset) + var(--fd-sidebar-width));
width: 1px;
background-color: var(--surface-active);
pointer-events: none;
z-index: 21;
}
/* Hide fumadocs nav on desktop - we use custom navbar there */
#nd-docs-layout > header {
display: none !important;
}
}
/* Sidebar spacing — left padding aligns item text with navbar content (item has 14px internal padding) */
[data-sidebar-viewport],
#nd-sidebar > div {
padding: 0 14px 0 calc(var(--nav-inset) - 14px) !important;
background: transparent !important;
}
/* Override Fumadocs p-4 on scroll viewport — must beat Tailwind v4 utility layer specificity */
html #nd-sidebar [data-radix-scroll-area-viewport],
html #nd-sidebar [data-radix-scroll-area-viewport].p-4,
html aside#nd-sidebar [data-radix-scroll-area-viewport] {
padding: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
/* Top/bottom breathing room inside the scrollable sidebar content — scrollable, not clipped */
html #nd-sidebar [data-radix-scroll-area-viewport] > div {
padding-top: 20px !important;
padding-bottom: 16px !important;
}
/* Override sidebar item styling to match Raindrop */
/* Target Link and button elements in sidebar - override Fumadocs itemVariants */
/* Exclude the small chevron-only toggle buttons */
/* Using html prefix for higher specificity over Tailwind v4 utilities */
html #nd-sidebar a:not(:has(span.font-mono)),
html #nd-sidebar button:not([aria-label*="ollapse"]):not([aria-label*="xpand"]) {
font-size: 0.875rem !important; /* 14px to match navbar items */
line-height: 1.4 !important;
padding: 0.375rem 0.875rem !important;
font-weight: 450 !important;
border-radius: 0.5rem !important; /* platform rounded-lg */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
sans-serif !important;
}
/* Sidebar text — platform --text-body */
html #nd-sidebar a:not(:has(span.font-mono)),
html #nd-sidebar button:not([aria-label*="ollapse"]):not([aria-label*="xpand"]) {
color: var(--text-body) !important;
}
/* Chevron icons — platform --text-icon */
#nd-sidebar svg {
display: inline-block !important;
opacity: 1 !important;
color: var(--text-icon) !important;
flex-shrink: 0 !important;
}
/* Ensure the small chevron toggle buttons are visible */
#nd-sidebar button[aria-label*="ollapse"],
#nd-sidebar button[aria-label*="xpand"] {
display: flex !important;
opacity: 1 !important;
padding: 0.25rem !important;
}
/* Add tiny gap between nested items */
#nd-sidebar ul li {
margin-bottom: 0.0625rem !important;
}
#nd-sidebar ul li:last-child {
margin-bottom: 0 !important;
}
/* Section separator headers — styling handled by component, override fumadocs defaults */
[data-sidebar-viewport] [data-separator] p,
#nd-sidebar [data-separator] p {
font-size: 0.625rem !important; /* 10px */
font-weight: 600 !important;
text-transform: uppercase !important;
letter-spacing: 0.06em !important;
margin: 0 !important;
padding: 0 !important;
}
/* First separator has no top margin or divider; subsequent ones show the divider on desktop */
[data-separator]:not([data-separator] ~ [data-separator]) {
margin-top: 0;
}
@media (min-width: 1024px) {
[data-separator] ~ [data-separator] > .separator-divider {
display: block;
}
}
/* Active state — aligned with platform --surface-active */
#nd-sidebar a[data-active="true"]:not(:has(span.font-mono)),
#nd-sidebar button[data-active="true"] {
background-image: none !important;
}
html #nd-sidebar a[data-active="true"]:not(:has(span.font-mono)),
html #nd-sidebar button[data-active="true"] {
background-color: var(--surface-active) !important;
color: var(--text-body) !important;
}
/* Hover state — platform --surface-3 */
html #nd-sidebar a:not(:has(span.font-mono)):hover:not([data-active="true"]),
html #nd-sidebar button:hover:not([data-active="true"]) {
background-color: var(--surface-3) !important;
}
/* Active items don't change on hover */
html #nd-sidebar a[data-active="true"]:not(:has(span.font-mono)):hover,
html #nd-sidebar button[data-active="true"]:hover {
background-color: var(--surface-active) !important;
color: var(--text-body) !important;
}
/* Hide search, platform, and collapse button from sidebar completely */
[data-sidebar] [data-search],
[data-sidebar] .search-toggle,
#nd-sidebar [data-search],
#nd-sidebar .search-toggle,
[data-sidebar-viewport] [data-search],
[data-sidebar-viewport] button[data-search],
aside[data-sidebar] [role="button"]:has([data-search]),
aside[data-sidebar] > div > button:first-child,
#nd-sidebar > div > button:first-child,
[data-sidebar] a[href*="sim.ai"],
#nd-sidebar a[href*="sim.ai"],
[data-sidebar-viewport] a[href*="sim.ai"],
/* Hide search buttons (but NOT folder chevron buttons) */
aside[data-sidebar] > div:first-child
> button:not([aria-label="Collapse"]):not([aria-label="Expand"]),
#nd-sidebar > div:first-child > button:not([aria-label="Collapse"]):not([aria-label="Expand"]),
/* Hide sidebar collapse button (panel icon) - direct children only */
aside[data-sidebar] > button:first-of-type:not([aria-label="Collapse"]):not([aria-label="Expand"]),
[data-sidebar]
> button[type="button"]:first-of-type:not([aria-label="Collapse"]):not([aria-label="Expand"]),
button[data-collapse]:not([aria-label="Collapse"]):not([aria-label="Expand"]),
[data-sidebar-header] button,
/* Hide theme toggle from sidebar footer */
aside[data-sidebar] [data-theme-toggle],
[data-sidebar-footer],
[data-sidebar] footer,
footer button[aria-label*="heme"],
aside[data-sidebar] > div:last-child:has(button[aria-label*="heme"]),
aside[data-sidebar] button[aria-label*="heme"],
[data-sidebar] button[aria-label*="Theme"],
/* Additional theme toggle selectors */
aside[data-sidebar] > *:last-child
button,
[data-sidebar-viewport] ~ *,
aside[data-sidebar] > div:not([data-sidebar-viewport]),
/* Aggressive theme toggle hiding */
aside[data-sidebar] svg[class*="sun"],
aside[data-sidebar] svg[class*="moon"],
aside[data-sidebar] button[type="button"]:last-child,
aside button:has(svg:only-child),
[data-sidebar] div:has(> button[type="button"]:only-child:last-child),
/* Hide theme toggle and other non-content elements */
aside[data-sidebar] > *:not([data-sidebar-viewport]) {
display: none !important;
}
/* Desktop only: Hide sidebar toggle buttons and nav title/logo (keep visible on mobile) */
@media (min-width: 1025px) {
[data-sidebar-container] > button,
[data-sidebar-container] [data-toggle],
aside[data-sidebar] [data-sidebar-toggle],
button[data-sidebar-toggle],
nav button[data-sidebar-toggle],
button[aria-label="Toggle Sidebar"],
button[aria-label="Collapse Sidebar"],
/* Hide nav title/logo in sidebar on desktop - target all possible locations */
/* Lower specificity selectors first (attribute selectors) */
[data-sidebar-header],
[data-sidebar] [data-title],
aside[data-sidebar] a[href="/"],
aside[data-sidebar] a[href="/"] img,
aside[data-sidebar] > a:first-child,
aside[data-sidebar] > div > a:first-child,
aside[data-sidebar] img[alt="Sim"],
aside[data-sidebar] svg[aria-label="Sim"],
/* Higher specificity selectors (ID selectors) */
#nd-sidebar
a[href="/"],
#nd-sidebar a[href="/"] img,
#nd-sidebar a[href="/"] svg,
#nd-sidebar > a:first-child,
#nd-sidebar > div:first-child > a:first-child,
#nd-sidebar img[alt="Sim"],
#nd-sidebar svg[aria-label="Sim"],
/* Hide theme toggle at bottom of sidebar on desktop */
#nd-sidebar
> footer,
#nd-sidebar footer,
aside#nd-sidebar > *:last-child:not(div),
#nd-sidebar > button:last-child,
#nd-sidebar button[aria-label*="theme" i],
#nd-sidebar button[aria-label*="Theme"],
#nd-sidebar > div:last-child > button {
display: none !important;
}
}
/* Extra aggressive - hide everything after the viewport */
aside[data-sidebar] [data-sidebar-viewport] ~ * {
display: none !important;
}
/* Tighter spacing for sidebar content */
[data-sidebar-viewport] > * {
margin-bottom: 0.0625rem;
}
[data-sidebar-viewport] > *:last-child {
margin-bottom: 0;
}
[data-sidebar-viewport] ul {
margin: 0;
padding: 0;
}
/* Ensure sidebar starts with content immediately */
aside[data-sidebar] > div:first-child {
padding-top: 0;
}
/* Remove all sidebar borders and backgrounds (except the right divider) */
[data-sidebar-container],
aside[data-sidebar],
[data-sidebar],
[data-sidebar] *,
#nd-sidebar,
#nd-sidebar * {
border: none !important;
}
/* Sidebar border is handled via #nd-docs-layout::before pseudo-element (see desktop section below) */
/* Override fumadocs background colors for sidebar */
.dark #nd-sidebar,
.dark [data-sidebar-container],
.dark aside[data-sidebar] {
--color-fd-muted: transparent !important;
--color-fd-secondary: transparent !important;
background: transparent !important;
}
/* Force normal text flow in sidebar */
[data-sidebar],
[data-sidebar] *,
[data-sidebar-viewport],
[data-sidebar-viewport] * {
writing-mode: horizontal-tb !important;
}
/* Apply Geist Mono to code elements */
code,
pre,
pre code {
font-family: var(--font-geist-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
}
/* Inline code — neutral colors aligned with sim design system */
:not(pre) > code {
padding: 0.2em 0.4em;
border-radius: 0.375rem;
font-size: 0.875em;
font-weight: 500;
}
/* Inline code — platform surface-4 / text-body / border-1 */
:not(pre) > code {
background-color: var(--surface-4);
color: var(--text-body);
border: 1px solid var(--border-1);
}
/* Remove heavy shadows globally — aligned with sim design system (shadow-card: 0 1px 3px rgba(0,0,0,0.04)) */
figure.shiki,
figure[data-rehype-pretty-code-figure],
.shiki {
box-shadow: none !important;
}
/* Search dialog — lighter shadow + constrained size */
[data-radix-popper-content-wrapper] > div,
[role="dialog"][data-state] {
box-shadow: var(--shadow-medium) !important;
}
[role="dialog"][data-state] {
max-width: 480px !important;
}
/* Search dialog overlay + panel must cover the sticky navbar — both default to z-50,
and the navbar wins the tie by DOM order, leaving it unblurred above the overlay */
.bg-fd-overlay,
[role="dialog"][data-state] {
z-index: 60 !important;
}
pre {
font-size: 0.875rem;
line-height: 1.7;
tab-size: 2;
-webkit-overflow-scrolling: touch;
}
pre code {
display: block;
width: fit-content;
min-width: 100%;
}
/* Syntax highlighting adjustments for better readability */
pre code .line {
padding-left: 0;
padding-right: 0;
}
/* Content typography — align heading sizes and weights with platform */
main article h1,
[data-content] h1,
#nd-page h1 {
font-size: 1.5rem !important;
font-weight: 550 !important;
letter-spacing: -0.02em;
color: var(--text-primary);
}
main article h2,
[data-content] h2,
#nd-page h2 {
font-size: 1.25rem !important;
font-weight: 500 !important;
letter-spacing: -0.015em;
color: var(--text-primary);
}
main article h3,
main article h4,
[data-content] h3,
[data-content] h4,
#nd-page h3,
#nd-page h4 {
font-size: 1rem !important;
font-weight: 470 !important;
letter-spacing: -0.01em;
color: var(--text-body);
}
/* Body text — platform --text-secondary */
main article p,
[data-content] p,
[data-docs-page] p {
color: var(--text-secondary);
}
/* Muted text — platform --text-muted */
main article .text-muted-foreground,
[data-content] .text-muted-foreground,
[data-docs-page] .text-muted-foreground {
color: var(--text-muted);
}
/* List items — inherit body text color */
main article li,
[data-content] li,
[data-docs-page] li {
color: var(--text-secondary);
}
/* Custom text highlighting styles */
.text-highlight {
color: var(--color-fd-primary);
}
/* Override marker color for highlighted lists */
.highlight-markers li::marker {
color: var(--color-fd-primary);
}
/* Remove the thin border-left on nested TOC items (keeps main indicator only) */
#nd-toc a[style*="padding-inline-start"] {
border-left: none !important;
}
/* TOC heading — align weight with landing */
#nd-toc h3,
#nd-toc [class*="title"] {
font-weight: 480 !important;
font-size: 0.8125rem !important;
color: var(--text-muted);
}
/* TOC links — softer colors aligned with landing muted text */
#nd-toc a {
font-weight: 430 !important;
font-size: 0.8125rem !important;
color: var(--text-muted);
transition: color 0.2s;
}
#nd-toc a:hover {
color: var(--text-body);
}
#nd-toc a[data-active="true"] {
color: var(--text-primary) !important;
font-weight: 470 !important;
}
/* Add bottom spacing to prevent abrupt page endings */
[data-content] {
padding-top: 1.5rem !important;
padding-bottom: 4rem;
}
/* Alternative fallback for different Fumadocs versions */
main article,
.docs-page main {
padding-top: 1.5rem !important;
padding-bottom: 4rem;
}
/* Main content area - center and constrain like turborepo/raindrop */
/* Note: --sidebar-offset and --toc-offset are now applied at #nd-docs-layout level */
main[data-main] {
max-width: var(--spacing-fd-container, 1400px);
margin-left: auto;
margin-right: auto;
padding-top: 1rem;
padding-left: var(--content-gap);
padding-right: var(--content-gap);
order: 1 !important;
}
/* Adjust for smaller screens */
@media (max-width: 768px) {
main[data-main] {
padding-left: 1rem;
padding-right: 1rem;
}
#nd-page:has(.api-page-header) {
padding-left: 1rem;
padding-right: 1rem;
}
}
/* Ensure docs page content is properly constrained */
[data-docs-page] {
max-width: 1400px;
margin-left: auto;
margin-right: auto;
padding-top: 1.5rem !important;
}
/* Remove any unwanted outlines from video elements */
video {
outline: none !important;
}
/* API Reference Pages — Mintlify-style overrides */
/* OpenAPI pages: span main + TOC grid columns for wide two-column layout.
Use named grid lines from grid-template-areas so this works regardless
of whether the grid has 3 columns (production) or 5 columns (local dev). */
#nd-page:has(.api-page-header) {
grid-column: main-start / toc-end !important;
max-width: 1400px !important;
padding-left: calc(var(--content-gap) + 1rem);
padding-right: calc(var(--content-gap) + 1rem);
}
/* Hide the empty TOC aside on OpenAPI pages so it doesn't overlay content */
#nd-docs-layout:has(#nd-page:has(.api-page-header)) #nd-toc {
display: none;
}
/* Hide the default "Response Body" heading rendered by fumadocs-openapi */
.response-section-wrapper > .response-section-content > h2,
.response-section-wrapper > .response-section-content > h3 {
display: none !important;
}
/* Hide default accordion triggers (status code rows) — we show our own dropdown */
.response-section-wrapper [data-orientation="vertical"] > [data-state] > h3 {
display: none !important;
}
/* Ensure API reference pages use the same font as the rest of the docs */
#nd-page:has(.api-page-header),
#nd-page:has(.api-page-header) h2,
#nd-page:has(.api-page-header) h3,
#nd-page:has(.api-page-header) h4,
#nd-page:has(.api-page-header) p,
#nd-page:has(.api-page-header) span,
#nd-page:has(.api-page-header) div,
#nd-page:has(.api-page-header) label,
#nd-page:has(.api-page-header) button {
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
/* Method badge pills — shared background colors (page + sidebar) */
span.font-mono.font-medium[data-method="get"],
span.font-mono.font-medium[data-method="head"],
span.font-mono.font-medium[data-method="options"] {
background-color: var(--badge-success-bg);
}
span.font-mono.font-medium[data-method="post"] {
background-color: var(--badge-blue-bg);
}
span.font-mono.font-medium[data-method="put"] {
background-color: var(--badge-amber-bg);
}
span.font-mono.font-medium[data-method="patch"] {
background-color: var(--badge-orange-bg);
}
span.font-mono.font-medium[data-method="delete"] {
background-color: var(--badge-error-bg);
}
/* Sidebar links with method badges — match regular item padding */
html #nd-sidebar a:has(span.font-mono.font-medium) {
display: flex !important;
align-items: center !important;
gap: 0.375rem;
padding: 0.375rem 0.875rem !important;
font-size: 0.875rem !important;
line-height: 1.4 !important;
font-weight: 450 !important;
border-radius: 0.75rem !important;
}
/* Sidebar method badges — fixed-width for right-aligned labels */
#nd-sidebar a span.font-mono.font-medium {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.625rem;
font-size: 0.625rem !important;
line-height: 1 !important;
padding: 0.15625rem 0.25rem;
border-radius: 0.375rem;
flex-shrink: 0;
}
/* Footer navigation method badges — pill styling to match sidebar */
#nd-page span.font-mono.font-medium[data-method] {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.625rem !important;
line-height: 1 !important;
padding: 0.15625rem 0.375rem;
border-radius: 0.375rem;
}
/* API page code/card containers — transparent background */
#nd-page:has(.api-page-header) {
--color-fd-card: transparent !important;
}
#nd-page:has(.api-page-header) figure.shiki {
border-radius: 0.75rem !important;
background-color: transparent !important;
}
/* API page code example panels — transparent */
#nd-page:has(.api-page-header) .rounded-xl.border.bg-fd-card {
background-color: transparent !important;
}
#nd-page:has(.api-page-header) .rounded-xl.border.shadow-md.bg-fd-card {
background-color: transparent !important;
box-shadow: none !important;
}
/* Hide "Filter Properties" search bar everywhere — main page and popovers */
input[placeholder="Filter Properties"] {
display: none !important;
}
div:has(> input[placeholder="Filter Properties"]) {
display: none !important;
}
/* Remove top border on first visible property after hidden Filter Properties */
div:has(> input[placeholder="Filter Properties"]) + .text-sm.border-t {
border-top: none !important;
}
/* Hide "TypeScript Definitions" copy panel on API pages */
#nd-page:has(.api-page-header) div.not-prose.rounded-xl.border.p-3.mb-4 {
display: none !important;
}
#nd-page:has(.api-page-header) div.not-prose.rounded-xl.border.p-3:has(> div > p.font-medium) {
display: none !important;
}
/* Hide info tags (Format, Default, etc.) everywhere — main page and popovers */
div.flex.flex-row.gap-2.flex-wrap.not-prose:has(> div.bg-fd-secondary) {
display: none !important;
}
div.flex.flex-row.items-start.bg-fd-secondary.border.rounded-lg.text-xs {
display: none !important;
}
/* Method+path bar — cleaner, lighter styling like Gumloop.
Override bg-fd-card CSS variable directly for reliability. */
#nd-page:has(.api-page-header) div.flex.flex-row.items-center.rounded-xl.border.not-prose {
--color-fd-card: var(--surface-3) !important;
background-color: var(--surface-3) !important;
border-color: var(--border-1) !important;
}
/* Method badge inside path bar — cleaner sans-serif, softer colors */
#nd-page:has(.api-page-header)
div.flex.flex-row.items-center.rounded-xl.border.not-prose
span.font-mono.font-medium {
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif !important;
font-weight: 600 !important;
font-size: 0.6875rem !important;
letter-spacing: 0.025em;
text-transform: uppercase;
padding: 0.125rem 0.5rem !important;
border-radius: 0.375rem !important;
}
/* Path bar per-method colors (fumadocs renders these, so we match by class) */
/* GET */
#nd-page:has(.api-page-header)
div.flex.flex-row.items-center.rounded-xl.border.not-prose
span.font-mono.font-medium[class*="text-green"] {
color: var(--badge-success-text) !important;
background-color: var(--badge-success-bg) !important;
}
/* POST */
#nd-page:has(.api-page-header)
div.flex.flex-row.items-center.rounded-xl.border.not-prose
span.font-mono.font-medium[class*="text-blue"] {
color: var(--badge-blue-text) !important;
background-color: var(--badge-blue-bg) !important;
}
/* PUT */
#nd-page:has(.api-page-header)
div.flex.flex-row.items-center.rounded-xl.border.not-prose
span.font-mono.font-medium[class*="text-yellow"] {
color: var(--badge-amber-text) !important;
background-color: var(--badge-amber-bg) !important;
}
/* PATCH */
#nd-page:has(.api-page-header)
div.flex.flex-row.items-center.rounded-xl.border.not-prose
span.font-mono.font-medium[class*="text-orange"] {
color: var(--badge-orange-text) !important;
background-color: var(--badge-orange-bg) !important;
}
/* DELETE */
#nd-page:has(.api-page-header)
div.flex.flex-row.items-center.rounded-xl.border.not-prose
span.font-mono.font-medium[class*="text-red"] {
color: var(--badge-error-text) !important;
background-color: var(--badge-error-bg) !important;
}
/* Path text inside method+path bar — monospace, bright like Gumloop */
#nd-page:has(.api-page-header) div.flex.flex-row.items-center.rounded-xl.border.not-prose code {
color: var(--text-body) !important;
background: none !important;
border: none !important;
padding: 0 !important;
font-size: 0.8125rem !important;
}
/* Inline code in API pages — neutral color aligned with sim design.
Exclude code inside the method+path bar (handled above). */
#nd-page:has(.api-page-header) .prose :not(pre) > code {
color: var(--text-body) !important;
}
/* Response Section — custom dropdown-based rendering (Mintlify style) */
/* Hide divider lines between accordion items */
.response-section-wrapper [data-orientation="vertical"].divide-y > * {
border-top-width: 0 !important;
border-bottom-width: 0 !important;
}
.response-section-wrapper [data-orientation="vertical"].divide-y {
border-top: none !important;
}
/* Remove content type labels inside accordion items (we show one in the header) */
.response-section-wrapper [data-orientation="vertical"] p.not-prose:has(code.text-xs) {
display: none !important;
}
/* Hide the top-level response description (e.g. "Execution was successfully cancelled.")
but NOT field descriptions inside Schema which also use prose-no-margin.
The response description is a direct child of AccordionContent (role=region) with mb-2. */
.response-section-wrapper [data-orientation="vertical"] [role="region"] > .prose-no-margin.mb-2,
.response-section-wrapper
[data-orientation="vertical"]
[role="region"]
> div
> .prose-no-margin.mb-2 {
display: none !important;
}
/* Remove left padding on accordion content so it aligns with Path Parameters */
.response-section-wrapper [data-orientation="vertical"] [role="region"] {
padding-inline-start: 0 !important;
}
/* Response section header */
.response-section-header {
display: flex;
align-items: center;
gap: 1rem;
margin-top: 1.75rem;
margin-bottom: 0.25rem;
}
.response-section-title {
font-size: 1.25rem;
font-weight: 500;
letter-spacing: -0.015em;
margin: 0;
color: var(--text-primary);
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, -apple-system, sans-serif;
}
.response-section-meta {
display: flex;
align-items: center;
gap: 0.75rem;
margin-left: auto;
}
/* Status code dropdown */
.response-section-dropdown-wrapper {
position: relative;
}
.response-section-dropdown-trigger {
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.125rem 0.25rem;
font-size: 0.875rem;
font-weight: 500;
color: var(--color-fd-muted-foreground);
background: none;
border: none;
cursor: pointer;
border-radius: 0.375rem;
transition: color 0.15s;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
}
.response-section-dropdown-trigger:hover {
color: var(--color-fd-foreground);
}
.response-section-chevron {
width: 0.75rem;
height: 0.75rem;
transition: transform 0.15s;
}
.response-section-chevron-open {
transform: rotate(180deg);
}
.response-section-dropdown-menu {
position: absolute;
top: calc(100% + 0.25rem);
left: 0;
z-index: 50;
min-width: 5rem;
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 0.75rem;
box-shadow: var(--shadow-card);
padding: 0.375rem;
overflow: hidden;
}
.response-section-dropdown-item {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0.375rem 0.5rem;
font-size: 0.875rem;
color: var(--text-body);
background: none;
border: none;
cursor: pointer;
border-radius: 0.5rem;
transition:
background-color 0.1s,
color 0.1s;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
}
.response-section-dropdown-item:hover {
background-color: var(--surface-active);
color: var(--text-primary);
}
.response-section-dropdown-item-selected {
color: var(--color-fd-foreground);
}
.response-section-check {
width: 0.875rem;
height: 0.875rem;
}
.response-section-content-type {
font-size: 0.875rem;
color: var(--color-fd-muted-foreground);
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
}
/* Response schema container — remove border to match Path Parameters style */
.response-section-wrapper [data-orientation="vertical"] .border.px-3.py-2.rounded-lg {
border: none !important;
padding: 0 !important;
border-radius: 0 !important;
background-color: transparent;
}
/* Property row — reorder: name (1) → type badge (2) → required badge (3) */
#nd-page:has(.api-page-header) .flex.flex-wrap.items-center.gap-3.not-prose {
display: flex;
flex-wrap: wrap;
align-items: center;
}
/* Name span — order 1 */
#nd-page:has(.api-page-header)
.flex.flex-wrap.items-center.gap-3.not-prose
> span.font-medium.font-mono.text-fd-primary {
order: 1;
}
/* Type badge — order 2, grey pill */
#nd-page:has(.api-page-header)
.flex.flex-wrap.items-center.gap-3.not-prose
> span.text-sm.font-mono.text-fd-muted-foreground {
order: 2;
background-color: var(--surface-5);
color: var(--text-secondary);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
}
html.dark
#nd-page:has(.api-page-header)
.flex.flex-wrap.items-center.gap-3.not-prose
> span.text-sm.font-mono.text-fd-muted-foreground {
background-color: var(--surface-4);
color: var(--text-secondary);
}
/* Hide the "*" inside the name span — we'll add "required" as a ::after on the flex row */
#nd-page:has(.api-page-header) span.font-medium.font-mono.text-fd-primary > span.text-red-400 {
display: none;
}
/* Required badge — order 3, red pill */
#nd-page:has(.api-page-header)
.flex.flex-wrap.items-center.gap-3.not-prose:has(span.text-red-400)::after {
content: "required";
order: 3;
display: inline-flex;
align-items: center;
background-color: var(--badge-error-bg);
color: var(--badge-error-text);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
}
/* Optional "?" indicator — hide it */
#nd-page:has(.api-page-header)
span.font-medium.font-mono.text-fd-primary
> span.text-fd-muted-foreground {
display: none;
}
/* Hide the auth scheme type label (e.g. "apiKey") next to Authorization heading */
#nd-page:has(.api-page-header) .flex.items-start.justify-between.gap-2 > div.not-prose {
display: none !important;
}
/* Auth property — replace "<token>" with "string" badge, add "header" and "required" badges.
Auth properties use my-4 (vs py-4 for regular properties). */
/* Auth property flex row — name: order 1, type: order 2, ::before "header": order 3, ::after "required": order 4 */
#nd-page:has(.api-page-header)
div.my-4
> .flex.flex-wrap.items-center.gap-3.not-prose
> span.font-medium.font-mono.text-fd-primary {
order: 1;
}
#nd-page:has(.api-page-header)
div.my-4
> .flex.flex-wrap.items-center.gap-3.not-prose
> span.text-sm.font-mono.text-fd-muted-foreground {
order: 2;
font-size: 0;
padding: 0 !important;
background: none !important;
line-height: 0;
}
#nd-page:has(.api-page-header)
div.my-4
> .flex.flex-wrap.items-center.gap-3.not-prose
> span.text-sm.font-mono.text-fd-muted-foreground::after {
content: "string";
font-size: 0.6875rem;
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
background-color: var(--surface-5);
color: var(--text-secondary);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
display: inline-flex;
align-items: center;
}
html.dark
#nd-page:has(.api-page-header)
div.my-4
> .flex.flex-wrap.items-center.gap-3.not-prose
> span.text-sm.font-mono.text-fd-muted-foreground::after {
background-color: var(--surface-4);
color: var(--text-secondary);
}
/* "header" badge via ::before on the auth flex row */
#nd-page:has(.api-page-header) div.my-4 > .flex.flex-wrap.items-center.gap-3.not-prose::before {
content: "header";
order: 3;
display: inline-flex;
align-items: center;
background-color: var(--surface-5);
color: var(--text-secondary);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
}
html.dark
#nd-page:has(.api-page-header)
div.my-4
> .flex.flex-wrap.items-center.gap-3.not-prose::before {
background-color: var(--surface-4);
color: var(--text-secondary);
}
/* "required" badge via ::after on the auth flex row — red pill */
#nd-page:has(.api-page-header) div.my-4 > .flex.flex-wrap.items-center.gap-3.not-prose::after {
content: "required";
order: 4;
display: inline-flex;
align-items: center;
background-color: var(--badge-error-bg);
color: var(--badge-error-text);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
}
/* Hide "In: header" text below auth property — redundant with the header badge */
#nd-page:has(.api-page-header) div.my-4 .prose-no-margin p:has(> code) {
display: none !important;
}
/* Section dividers — bottom border after Authorization and Body sections. */
.api-section-divider {
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--surface-active);
}
/* Property rows — breathing room like Mintlify.
Regular properties use border-t py-4; auth properties use border-t my-4. */
#nd-page:has(.api-page-header) .text-sm.border-t.py-4 {
padding-top: 1.25rem !important;
padding-bottom: 1.25rem !important;
}
#nd-page:has(.api-page-header) .text-sm.border-t.my-4 {
margin-top: 1.25rem !important;
margin-bottom: 1.25rem !important;
padding-top: 1.25rem;
}
/* Divider lines between fields — very subtle like Mintlify */
#nd-page:has(.api-page-header) .text-sm.border-t {
border-color: var(--surface-active);
}
/* Body/Callback section "application/json" label — remove inline code styling */
#nd-page:has(.api-page-header) .flex.gap-2.items-center.justify-between p.not-prose code.text-xs,
#nd-page:has(.api-page-header) .flex.justify-between.gap-2.items-end p.not-prose code.text-xs {
background: none !important;
border: none !important;
padding: 0 !important;
color: var(--color-fd-muted-foreground) !important;
font-size: 0.875rem !important;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif !important;
}
/* Object/array type triggers in property rows — order 2 + badge chip styling */
#nd-page:has(.api-page-header) .flex.flex-wrap.items-center.gap-3.not-prose > button,
#nd-page:has(.api-page-header) .flex.flex-wrap.items-center.gap-3.not-prose > span:has(> button) {
order: 2;
background-color: var(--surface-5);
color: var(--text-secondary);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
}
html.dark #nd-page:has(.api-page-header) .flex.flex-wrap.items-center.gap-3.not-prose > button,
html.dark
#nd-page:has(.api-page-header)
.flex.flex-wrap.items-center.gap-3.not-prose
> span:has(> button) {
background-color: var(--surface-4);
color: var(--text-secondary);
}
/* Section headings (Authorization, Path Parameters, etc.) — consistent top spacing */
#nd-page:has(.api-page-header) .min-w-0.flex-1 h2 {
margin-top: 1.75rem !important;
margin-bottom: 0.25rem !important;
}
/* Code examples in right column — wrap long lines instead of horizontal scroll */
#nd-page:has(.api-page-header) pre {
white-space: pre-wrap !important;
word-break: break-all !important;
}
#nd-page:has(.api-page-header) pre code {
width: 100% !important;
word-break: break-all !important;
overflow-wrap: break-word !important;
}
/* Callout/alert — transparent background, no shadow, hide colored bar, add padding */
div.rounded-xl.border.bg-fd-card.shadow-md:has(> [role="none"]) {
background-color: transparent !important;
box-shadow: none !important;
border-color: var(--border-1) !important;
padding-left: 0.875rem !important;
align-items: center !important;
}
div.rounded-xl.border.bg-fd-card.shadow-md > [role="none"] {
display: none !important;
}
/* Files component — transparent background */
div.not-prose.rounded-md.border.bg-fd-card.p-2 {
background-color: transparent !important;
border-color: var(--border-1) !important;
}
/* Callout icon — outline style, text color, vertically centered */
div.rounded-xl.border.bg-fd-card.shadow-md:has(> [role="none"]) > svg {
fill: none !important;
color: var(--color-fd-foreground) !important;
stroke-width: 1.75 !important;
flex-shrink: 0;
width: 1rem !important;
height: 1rem !important;
}
/* API page header — constrain title/copy-page to left content column, not full width.
Only applies on OpenAPI pages (which have the two-column layout). */
@media (min-width: 1280px) {
.api-page-header {
max-width: calc(100% - 400px - 1.5rem);
}
}
/* Footer navigation — constrain to left content column on OpenAPI pages only.
Target pages that contain the two-column layout via :has() selector. */
#nd-page:has(.api-page-header) > div:last-child {
max-width: calc(100% - 400px - 1.5rem);
}
@media (max-width: 1024px) {
#nd-page:has(.api-page-header) > div:last-child {
max-width: 100%;
}
}
/* ─── Content Components — aligned with platform design system ─── */
/* Heading anchor links — no underline, inherit color */
#nd-page h1 a,
#nd-page h2 a,
#nd-page h3 a,
#nd-page h4 a,
#nd-page h5 a,
#nd-page h6 a,
.prose h1 a,
.prose h2 a,
.prose h3 a,
.prose h4 a,
.prose h5 a,
.prose h6 a {
text-decoration: none !important;
color: inherit !important;
border: none !important;
}
/* Tables — clean divider-based style matching chat markdown renderer */
.prose table,
[data-docs-page] table,
[data-content] table,
main article table {
border-collapse: collapse !important;
border-spacing: 0 !important;
background: none !important;
border: none !important;
border-radius: 0 !important;
overflow: visible !important;
font-size: 0.875rem !important;
}
.prose thead th,
[data-docs-page] thead th,
[data-content] thead th,
main article thead th {
background: none !important;
font-weight: 600 !important;
padding: 0.5rem 0.75rem !important;
border-bottom: 1px solid var(--border) !important;
border-left: none !important;
border-right: none !important;
border-top: none !important;
border-inline-start: none !important;
color: var(--text-primary);
text-align: left;
white-space: nowrap;
line-height: 1.5;
}
.prose td,
[data-docs-page] td,
[data-content] td,
main article td {
padding: 0.5rem 0.75rem !important;
border-bottom: 1px solid var(--surface-active) !important;
border-left: none !important;
border-right: none !important;
border-top: none !important;
border-inline-start: none !important;
color: var(--text-secondary);
line-height: 1.5;
}
/* Remove bottom border on last row */
.prose tbody tr:last-child td,
[data-docs-page] tbody tr:last-child td,
[data-content] tbody tr:last-child td,
main article tbody tr:last-child td {
border-bottom: none !important;
}
/* Code blocks — aligned with platform chat code block styling */
/* Standalone code block figure — border + rounded */
figure.shiki:not(div > figure.shiki),
figure[data-rehype-pretty-code-figure]:not(div > figure[data-rehype-pretty-code-figure]),
figure:has(pre):not(div > figure:has(pre)) {
border-radius: 0.5rem !important;
border: 1px solid var(--border) !important;
overflow: hidden !important;
background-color: transparent !important;
box-shadow: none !important;
}
/* Code block inside tabs (API examples) — no extra border, inherits from parent */
div > figure.shiki {
border: none !important;
background-color: transparent !important;
box-shadow: none !important;
}
html.dark div > figure.shiki {
background-color: transparent !important;
}
/* Tabbed code block container (cURL/JS/Go tabs) */
div:has(> [role="tablist"]):has(> div > figure.shiki) {
border-radius: 0.5rem !important;
border: 1px solid var(--border) !important;
overflow: hidden !important;
background-color: transparent !important;
box-shadow: none !important;
}
/* Tab list bar — match platform surface-4 */
div:has(> div > figure.shiki) > [role="tablist"] {
background: none !important;
border-bottom: 1px solid var(--surface-active) !important;
}
/* Tab triggers — muted text, active gets foreground */
div:has(> div > figure.shiki) > [role="tablist"] button {
color: var(--text-muted) !important;
font-weight: 470 !important;
}
div:has(> div > figure.shiki) > [role="tablist"] button:hover {
color: var(--text-body) !important;
}
div:has(> div > figure.shiki) > [role="tablist"] button[data-state="active"] {
color: var(--text-primary) !important;
}
/* Code block title header (figcaption with filename) */
figure.shiki > div:first-child:has(figcaption) {
background-color: var(--surface-4) !important;
border-bottom: 1px solid var(--surface-active) !important;
}
figure.shiki figcaption {
color: var(--text-muted) !important;
}
/* Code block pre element — transparent bg */
figure.shiki pre {
background-color: transparent !important;
}
html.dark figure.shiki pre {
background-color: transparent !important;
}
/* Code viewport scroll area — transparent bg */
figure.shiki > div[role="region"] {
background-color: transparent !important;
}
html.dark figure.shiki > div[role="region"] {
background-color: transparent !important;
}
/* Copy button — minimal ghost style */
figure.shiki button[aria-label="Copy Text"],
figure.shiki button[aria-label="Copied Text"] {
color: var(--text-muted) !important;
background: none !important;
border: none !important;
box-shadow: none !important;
backdrop-filter: none !important;
}
figure.shiki button[aria-label="Copy Text"]:hover {
color: var(--text-icon) !important;
}
figure.shiki button[aria-label="Copied Text"] {
color: var(--brand-accent) !important;
}
/* Copy button container — remove backdrop blur bg */
figure.shiki > div:has(> button[aria-label]) {
background: none !important;
backdrop-filter: none !important;
}
/* Cards container — vertical list with line dividers instead of grid boxes */
.prose .grid:has(> a[data-card]),
#nd-page .grid:has(> a[data-card]) {
display: flex !important;
flex-direction: column !important;
gap: 0 !important;
border-top: 1px solid var(--border) !important;
border-bottom: 1px solid var(--border) !important;
}
/* Card items — line-separated rows with hover bg (scoped to grid container only) */
.grid > a[data-card] {
display: flex !important;
flex-direction: column !important;
gap: 0.125rem !important;
background: none !important;
border: none !important;
border-bottom: 1px solid var(--border) !important;
border-radius: 0 !important;
padding: 0.875rem 1rem !important;
transition: background-color 0.15s !important;
box-shadow: none !important;
}
.grid > a[data-card]:last-child {
border-bottom: none !important;
}
.grid > a[data-card]:hover {
background-color: var(--surface-3) !important;
}
/* Card title */
.grid > a[data-card] h3 {
font-weight: 470 !important;
font-size: 0.875rem !important;
color: var(--text-primary) !important;
}
/* Card description */
.grid > a[data-card] p,
.grid > a[data-card] div {
color: var(--text-muted) !important;
font-size: 0.8125rem !important;
}
/* Steps — lighter styling aligned with platform */
.fd-steps {
border-left-color: var(--surface-active) !important;
}
.fd-step::before {
background-color: var(--surface-4) !important;
color: var(--text-muted) !important;
font-weight: 500 !important;
}
/* Blockquotes — subtle left border, matching platform divider */
.prose blockquote,
[data-docs-page] blockquote,
[data-content] blockquote,
main article blockquote {
border-left: 3px solid var(--border-1) !important;
padding: 0.25rem 0 0.25rem 1rem !important;
color: var(--text-muted) !important;
font-style: italic;
background: none !important;
margin: 1.5rem 0 !important;
}
/* Remove all remaining box-shadows on content components */
.prose > *,
[data-docs-page] figure,
[data-docs-page] table,
[data-docs-page] [data-card] {
box-shadow: none !important;
}
/* Tailwind v4 content sources */
@source '../app/**/*.{js,ts,jsx,tsx,mdx}';
@source '../components/**/*.{js,ts,jsx,tsx,mdx}';
@source '../content/**/*.{js,ts,jsx,tsx,mdx}';
@source '../mdx-components.tsx';
@source '../node_modules/fumadocs-ui/dist/**/*.js';