Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions apps/docs/app/[lang]/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { notFound } from 'next/navigation'
import { PageFooter } from '@/components/docs-layout/page-footer'
import { PageNavigationArrows } from '@/components/docs-layout/page-navigation-arrows'
import { LLMCopyButton } from '@/components/page-actions'
import { PageTypeBadge } from '@/components/page-type-badge'
import { StructuredData } from '@/components/structured-data'
import { CodeBlock } from '@/components/ui/code-block'
import { Heading } from '@/components/ui/heading'
Expand All @@ -23,6 +22,21 @@ import { DOCS_BASE_URL } from '@/lib/urls'
const SUPPORTED_LANGUAGES: Set<string> = new Set(i18n.languages)
const BASE_URL = DOCS_BASE_URL

/**
* Most pages close with a `## Next` / `## Next steps` grid of onward links.
* That heading is navigation, not content, so it is kept out of the table of
* contents — the ToC should say what the page covers, not where to go after it.
* The heading itself still renders above the cards.
*
* Matched on the slug rather than the rendered title because a ToC title is a
* `ReactNode`; the trailing group tolerates the slugger's dedupe suffix.
*/
const ONWARD_NAV_SLUG = /^#next(-steps)?(-\d+)?$/i

function isContentHeading(item: { url: string }): boolean {
return !ONWARD_NAV_SLUG.test(item.url)
}

const OG_LOCALE_MAP: Record<string, string> = {
en: 'en_US',
es: 'es_ES',
Expand Down Expand Up @@ -171,7 +185,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
breadcrumb={breadcrumbs}
/>
<DocsPage
toc={data.toc}
toc={data.toc.filter(isContentHeading)}
breadcrumb={{
enabled: false,
}}
Expand Down Expand Up @@ -218,7 +232,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
breadcrumb={breadcrumbs}
/>
<DocsPage
toc={data.toc}
toc={data.toc.filter(isContentHeading)}
full={data.full || isAcademy}
breadcrumb={{
enabled: false,
Expand All @@ -244,7 +258,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
</div>
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
</div>
{data.pageType && <PageTypeBadge type={data.pageType} className='mb-3' />}
<DocsTitle className='mb-2'>{data.title}</DocsTitle>
</div>
<DocsBody>
Expand Down
122 changes: 73 additions & 49 deletions apps/docs/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@
@source "../../../packages/emcn/src";
@source "../../../packages/workflow-renderer/src";

/* Every @sim/emcn component expresses hover through `hover-hover:` so touch
devices never latch a sticky hover state. The app registers it as a plugin
variant in apps/sim/tailwind.config.ts; docs is CSS-first Tailwind v4 with no
config, so without this declaration the variant compiles to nothing and every
emcn hover state silently no-ops here. */
@custom-variant hover-hover {
@media (hover: hover) and (pointer: fine) {
&:hover {
@slot;
}
}
}

/* Prevent overscroll bounce effect on the page */
html,
body {
Expand Down Expand Up @@ -114,11 +127,24 @@ body {
--badge-error-text: #dc2626;
--badge-gray-bg: #e7e5e4;
--badge-gray-text: #57534e;
--code-bg: #f5f5f5;
--code-foreground: #1a1a1a;
--code-line-number: #737373;
--selection-bg: #add6ff;
--selection-dark: #264f78;
--highlight-search-active: #f6ad55;
--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-overlay: 0 10px 30px rgba(0, 0, 0, 0.11);
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.04);
/* z-index scale — poppers sit above the modal so menus stay clickable over
the modal overlay. Mirrors apps/sim/app/_styles/globals.css. */
--z-toast: 150;
--z-modal: 200;
--z-popover: 300;
--z-tooltip: 400;
}

.dark {
Expand Down Expand Up @@ -185,8 +211,13 @@ body {
--badge-error-text: #fca5a5;
--badge-gray-bg: #3a3a3a;
--badge-gray-text: #a8a8a8;
--code-bg: #1f1f1f;
--code-foreground: #eeeeee;
--code-line-number: #a8a8a8;
--selection-bg: #264f78;
--scrollbar-thumb-color: #5a5a5a;
--scrollbar-thumb-hover-color: #6a6a6a;
--shadow-overlay: 0 10px 30px rgba(0, 0, 0, 0.3);
}

/* Scrollbars — platform thumb tokens, transparent track */
Expand Down Expand Up @@ -358,10 +389,10 @@ aside#nd-sidebar [data-radix-scroll-area-viewport] {
}
}

/* Sidebar spacing — left padding aligns item text with navbar content (item has 14px internal padding) */
/* Sidebar spacing — left padding aligns item text with navbar content (item has 8px internal padding) */
[data-sidebar-viewport],
#nd-sidebar > div {
padding: 0 14px 0 calc(var(--nav-inset) - 14px) !important;
padding: 0 14px 0 calc(var(--nav-inset) - 8px) !important;
background: transparent !important;
}

Expand All @@ -386,15 +417,20 @@ html #nd-sidebar [data-radix-scroll-area-viewport] > div {
/* 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 */
/* NEVER set `display` here. This selector scores (1,2,2) and carries
`!important`, so it out-specifies every rule below that hides the language
selector, theme toggle, and search button — a `display` declaration here
forces all of them back into the sidebar. The 30px chip height therefore
comes from line-height + padding (20 + 5 + 5), not from flex centering. */
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;
line-height: 20px !important;
padding: 5px 0.5rem !important; /* 30px tall overall — the app's chip pill, at its px-2 */
font-weight: 400 !important;
border-radius: 0.5rem !important; /* platform rounded-lg */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
sans-serif !important;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
}

/* Sidebar text — platform --text-body */
Expand Down Expand Up @@ -428,28 +464,24 @@ html #nd-sidebar button:not([aria-label*="ollapse"]):not([aria-label*="xpand"])
margin-bottom: 0 !important;
}

/* Section separator headers — styling handled by component, override fumadocs defaults */
/* Section separator headers — match the app sidebar's group label: 12px,
sentence case, normal weight. Only the fumadocs defaults are overridden here;
size and color stay on the component. */
[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;
font-weight: 400 !important;
text-transform: none !important;
letter-spacing: normal !important;
margin: 0 !important;
padding: 0 !important;
}

/* First separator has no top margin or divider; subsequent ones show the divider on desktop */
/* The first group sits flush against the top padding — it has nothing to be
separated from. */
[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"] {
Expand Down Expand Up @@ -709,7 +741,7 @@ main article h4,
[data-content] h4,
#nd-page h3,
#nd-page h4 {
font-size: 1rem !important;
font-size: var(--text-md) !important;
font-weight: 470 !important;
letter-spacing: -0.01em;
color: var(--text-body);
Expand Down Expand Up @@ -751,18 +783,26 @@ main article li,
border-left: none !important;
}

/* The clerk TOC draws its active range twice: a track segment, plus a dot that
animates along the track to the current heading. The dot is redundant with the
segment and reads as a stray artifact, so hide it and keep the segment. It is
the only node fumadocs gives an inline offset-path, which is what anchors it. */
#nd-toc [style*="offset-path"] {
display: none !important;
}

/* TOC heading — align weight with landing */
#nd-toc h3,
#nd-toc [class*="title"] {
font-weight: 480 !important;
font-size: 0.8125rem !important;
font-size: var(--text-small) !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;
font-size: var(--text-small) !important;
color: var(--text-muted);
transition: color 0.2s;
}
Expand Down Expand Up @@ -905,7 +945,7 @@ html #nd-sidebar a:has(span.font-mono.font-medium) {
align-items: center;
justify-content: center;
width: 2.625rem;
font-size: 0.625rem !important;
font-size: var(--text-micro) !important;
line-height: 1 !important;
padding: 0.15625rem 0.25rem;
border-radius: 0.375rem;
Expand All @@ -917,7 +957,7 @@ html #nd-sidebar a:has(span.font-mono.font-medium) {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.625rem !important;
font-size: var(--text-micro) !important;
line-height: 1 !important;
padding: 0.15625rem 0.375rem;
border-radius: 0.375rem;
Expand Down Expand Up @@ -984,7 +1024,7 @@ div.flex.flex-row.items-start.bg-fd-secondary.border.rounded-lg.text-xs {
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;
font-size: var(--text-xs) !important;
letter-spacing: 0.025em;
text-transform: uppercase;
padding: 0.125rem 0.5rem !important;
Expand Down Expand Up @@ -1033,7 +1073,7 @@ div.flex.flex-row.items-start.bg-fd-secondary.border.rounded-lg.text-xs {
background: none !important;
border: none !important;
padding: 0 !important;
font-size: 0.8125rem !important;
font-size: var(--text-small) !important;
}
/* Inline code in API pages — neutral color aligned with sim design.
Exclude code inside the method+path bar (handled above). */
Expand Down Expand Up @@ -1213,7 +1253,7 @@ div.flex.flex-row.items-start.bg-fd-secondary.border.rounded-lg.text-xs {
color: var(--text-secondary);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
font-size: var(--text-xs);
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
Expand All @@ -1223,7 +1263,6 @@ html.dark
.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 */
Expand All @@ -1242,7 +1281,7 @@ html.dark
color: var(--badge-error-text);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
font-size: var(--text-xs);
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
Expand Down Expand Up @@ -1284,7 +1323,7 @@ html.dark
> .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;
font-size: var(--text-xs);
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
Expand All @@ -1301,7 +1340,6 @@ html.dark
> .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 */
Expand All @@ -1314,7 +1352,7 @@ html.dark
color: var(--text-secondary);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
font-size: var(--text-xs);
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
Expand All @@ -1324,7 +1362,6 @@ html.dark
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 */
Expand All @@ -1337,7 +1374,7 @@ html.dark
color: var(--badge-error-text);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
font-size: var(--text-xs);
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
Expand Down Expand Up @@ -1389,7 +1426,7 @@ html.dark
color: var(--text-secondary);
padding: 0.1875rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.6875rem;
font-size: var(--text-xs);
line-height: 1.125rem;
font-weight: 500;
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
Expand All @@ -1400,7 +1437,6 @@ html.dark
.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 */
Expand Down Expand Up @@ -1562,10 +1598,6 @@ div > figure.shiki {
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;
Expand Down Expand Up @@ -1610,19 +1642,11 @@ 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"] {
Expand Down Expand Up @@ -1690,7 +1714,7 @@ figure.shiki > div:has(> button[aria-label]) {
.grid > a[data-card] p,
.grid > a[data-card] div {
color: var(--text-muted) !important;
font-size: 0.8125rem !important;
font-size: var(--text-small) !important;
}

/* Steps — lighter styling aligned with platform */
Expand Down
Loading
Loading