diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 6e72d6c5..d39972d4 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -237,6 +237,13 @@ export default defineConfig({ ignoreDeadLinks: true, appearance: true, cleanUrls: true, + + // The SandBase design presents code on a dark "panel" surface in both light + // and dark appearance. Pin the syntax theme to a dark palette so code blocks + // read as the signature dark panels on every background. + markdown: { + theme: { light: 'github-dark', dark: 'github-dark' }, + }, // Frontmatter titles are already curated for SEO; avoid appending a second // brand suffix that can push rendered titles beyond search-result limits. titleTemplate: false, @@ -295,7 +302,9 @@ export default defineConfig({ { text: 'Docs', link: '/', activeMatch: '^/(?!api-reference/|model-api-reference/)' }, { text: 'Model API Reference', link: '/model-api-reference/', activeMatch: '^/model-api-reference/' }, { text: 'Platform API', link: '/api-reference/', activeMatch: '^/api-reference/' }, - { text: 'Console', link: 'https://www.sandbase.ai/console', target: '_blank', rel: 'noopener noreferrer' }, + { text: 'Pricing', link: 'https://www.sandbase.ai/pricing', target: '_blank', rel: 'noopener noreferrer' }, + // Rendered as the primary "Start building" CTA button (see custom.css). + { text: 'Start building', link: 'https://www.sandbase.ai/console', target: '_blank', rel: 'noopener noreferrer' }, ], sidebar: { diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index 166297d3..d8ea26d6 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -24,7 +24,6 @@ export const platformApiReferenceFallbackSidebar = modelApiReferenceSidebar(plat export const docsSidebar: DefaultTheme.SidebarItem[] = [ { text: 'AI-Friendly Docs', - collapsed: true, items: [ { text: 'AI-Readable Overview', link: '/for-agents/' }, { text: 'AI API Guide', link: '/for-agents/full' }, @@ -34,7 +33,6 @@ export const docsSidebar: DefaultTheme.SidebarItem[] = [ }, { text: 'First steps', - collapsed: false, items: [ { text: 'Overview', link: '/getting-started/' }, { text: 'Quickstart', link: '/getting-started/quickstart' }, @@ -44,7 +42,6 @@ export const docsSidebar: DefaultTheme.SidebarItem[] = [ }, { text: 'Models & APIs', - collapsed: false, items: [ { text: 'Store', link: '/store/' }, { text: 'APIs', link: '/store/apis' }, @@ -56,7 +53,6 @@ export const docsSidebar: DefaultTheme.SidebarItem[] = [ }, { text: 'Guides', - collapsed: false, items: [ { text: 'Overview', link: '/guides/' }, { text: 'Chat Completions', link: '/guides/chat-completions' }, @@ -71,7 +67,6 @@ export const docsSidebar: DefaultTheme.SidebarItem[] = [ }, { text: 'Build agents', - collapsed: false, items: [ { text: 'Overview', link: '/agents/' }, { text: 'Define agent', link: '/agents/agent-api' }, @@ -81,7 +76,6 @@ export const docsSidebar: DefaultTheme.SidebarItem[] = [ }, { text: 'Agent operations', - collapsed: false, items: [ { text: 'Services', link: '/agents/services' }, { text: 'Schedules', link: '/agents/schedules' }, @@ -90,7 +84,6 @@ export const docsSidebar: DefaultTheme.SidebarItem[] = [ }, { text: 'Workspace', - collapsed: false, items: [ { text: 'Overview', link: '/admin/' }, { text: 'API Keys', link: '/getting-started/api-keys' }, diff --git a/.vitepress/theme/ContactFooter.vue b/.vitepress/theme/ContactFooter.vue deleted file mode 100644 index 782f8dd3..00000000 --- a/.vitepress/theme/ContactFooter.vue +++ /dev/null @@ -1,128 +0,0 @@ - - - diff --git a/.vitepress/theme/DocsSearch.vue b/.vitepress/theme/DocsSearch.vue new file mode 100644 index 00000000..c89209ce --- /dev/null +++ b/.vitepress/theme/DocsSearch.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/.vitepress/theme/SiteFooter.vue b/.vitepress/theme/SiteFooter.vue new file mode 100644 index 00000000..3c4797c4 --- /dev/null +++ b/.vitepress/theme/SiteFooter.vue @@ -0,0 +1,322 @@ + + + + + diff --git a/.vitepress/theme/SiteHeader.vue b/.vitepress/theme/SiteHeader.vue new file mode 100644 index 00000000..d6411608 --- /dev/null +++ b/.vitepress/theme/SiteHeader.vue @@ -0,0 +1,661 @@ + + + + + diff --git a/.vitepress/theme/custom.css b/.vitepress/theme/custom.css index 3b75b94c..ad31bc9a 100644 --- a/.vitepress/theme/custom.css +++ b/.vitepress/theme/custom.css @@ -1,39 +1,129 @@ -/* SandBase docs palette — documentation-first neutrals. Brand green is a - semantic accent, never the default text or surface color. */ +/* Clash Grotesk — the SandBase brand typeface, shared with the main site. */ +@font-face { + font-family: 'Clash Grotesk'; + src: url('/docs/ClashGrotesk-Variable.woff2') format('woff2'); + font-weight: 200 700; + font-display: swap; + font-style: normal; +} + +/* ─── SandBase design system ─────────────────────────────────────────────── + Ported from the sandbase /docs reference design and the sandbase.ai main + site. The whole theme is token-driven: change these values and the palette + cascades through VitePress's own `--vp-c-*` variables. The look is an + editorial "pixel poster": near-zero radius, hairline borders, a violet + (light) / lime (dark) accent, and monospace uppercase micro-labels. */ :root { - --vp-c-brand-1: #303238; - --vp-c-brand-2: #1f2126; - --vp-c-brand-3: #15171b; - --vp-c-brand-soft: rgba(31, 33, 38, 0.065); - --sb-accent-green: #16815b; - - --sb-code-text: #344054; - --sb-code-bg: #f1f3f5; - --sb-code-border: #e4e7ec; - --sb-code-block-bg: #f8f9fa; - - /* Fonts — unified Inter stack (matches platform.claude.com) */ - --vp-font-family-base: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', Roboto, sans-serif; + --sb-canvas: #f2f0f3; + --sb-surface: #ffffff; + --sb-surface-alt: #e7e4ea; + --sb-line: #dbd8df; + --sb-line-strong: #0e0b1a; + --sb-ink: #0e0b1a; + --sb-ink-muted: #5d5969; + --sb-ink-subtle: #757183; + --sb-accent: #5e3fff; + --sb-accent-hover: #4a2fe0; + --sb-accent-contrast: #ffffff; + --sb-tint: #d9ff43; /* pixel accent / corner marks / CTA hover */ + --sb-tint-fg: #0e0b1a; + --sb-panel: #0e0b1a; /* dark code panel */ + --sb-panel-fg: #f2f0f3; + --sb-panel-label: #8a8698; + + /* Map onto VitePress core tokens so the palette propagates everywhere. */ + --vp-c-bg: var(--sb-canvas); + --vp-c-bg-alt: var(--sb-canvas); + --vp-c-bg-soft: var(--sb-surface-alt); + --vp-c-bg-elv: var(--sb-surface); + --vp-c-text-1: var(--sb-ink); + --vp-c-text-2: var(--sb-ink-muted); + --vp-c-text-3: var(--sb-ink-subtle); + --vp-c-divider: var(--sb-line); + --vp-c-border: var(--sb-line); + --vp-c-gutter: var(--sb-line); + --vp-c-brand-1: var(--sb-accent); + --vp-c-brand-2: var(--sb-accent-hover); + --vp-c-brand-3: var(--sb-accent); + --vp-c-brand-soft: color-mix(in srgb, var(--sb-accent) 12%, transparent); + --sb-accent-green: var(--sb-accent); /* legacy alias reused across this file */ + + --sb-code-text: var(--sb-ink); /* inline code chips read on surface-alt */ + --sb-code-bg: var(--sb-surface-alt); /* inline code chips */ + --sb-code-border: var(--sb-line); + --sb-code-block-bg: var(--sb-panel); /* fenced code = dark panel */ + --vp-code-block-bg: var(--sb-panel); + --vp-code-copy-code-bg: rgba(255, 255, 255, 0.09); + --vp-code-copy-code-hover-bg: rgba(255, 255, 255, 0.18); + --vp-code-copy-code-active-text: var(--sb-panel-fg); + + --vp-font-family-base: 'Clash Grotesk', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', Roboto, sans-serif; --vp-font-family-mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', Consolas, monospace; + + /* Aliases matching the sandbase.ai main site so the ported header/footer + stylesheets work verbatim. These resolve to the --sb-* tokens above and + therefore switch automatically between light and dark. */ + --color-canvas: var(--sb-canvas); + --color-surface: var(--sb-surface); + --color-surface-alt: var(--sb-surface-alt); + --color-line: var(--sb-line); + --color-ink: var(--sb-ink); + --color-ink-muted: var(--sb-ink-muted); + --color-ink-subtle: var(--sb-ink-subtle); + --color-accent: var(--sb-accent); + --color-panel: var(--sb-panel); + --color-panel-fg: var(--sb-panel-fg); + --color-tint-cyan: var(--sb-tint); + --color-positive: #3f7a1a; + --font-family-heading: var(--vp-font-family-base); + --font-family-body: var(--vp-font-family-base); + --font-family-mono: var(--vp-font-family-mono); + --shadow-lg: 0 12px 28px rgba(15, 12, 26, 0.14); + --page-inset: max(clamp(1.25rem, 5.5556vw, 5rem), calc((100% - 90rem) / 2 + 5rem)); } .dark { - --vp-c-brand-1: #e3e5e8; - --vp-c-brand-2: #f1f2f4; - --vp-c-brand-3: #ffffff; - --vp-c-brand-soft: rgba(255, 255, 255, 0.08); - --sb-accent-green: #68d6a8; - - --sb-code-text: #d6dce5; - --sb-code-bg: #232832; - --sb-code-border: #343b48; - --sb-code-block-bg: #171b22; - - /* Dark background matching main site */ - --vp-c-bg: #0f1117; - --vp-c-bg-alt: #161922; - --vp-c-bg-elv: #1c1f2b; - --vp-c-bg-soft: #1c1f2b; + --sb-canvas: #0e0b1a; + --sb-surface: #241d44; + --sb-surface-alt: #2e2656; + --sb-line: #332d4f; + --sb-line-strong: #f2f0f3; + --sb-ink: #f2f0f3; + --sb-ink-muted: #a29ea9; + --sb-ink-subtle: #8a8698; + --sb-accent: #d9ff43; + --sb-accent-hover: #e6ff70; + --sb-accent-contrast: #0e0b1a; + --sb-tint: #8b6fff; + --sb-tint-fg: #ffffff; + --sb-panel: #1e1936; + --sb-panel-fg: #f2f0f3; + --sb-panel-label: #a9a5b8; + + --vp-c-bg: var(--sb-canvas); + --vp-c-bg-alt: var(--sb-canvas); + --vp-c-bg-soft: var(--sb-surface); + --vp-c-bg-elv: var(--sb-surface); + --vp-c-text-1: var(--sb-ink); + --vp-c-text-2: var(--sb-ink-muted); + --vp-c-text-3: var(--sb-ink-subtle); + --vp-c-divider: var(--sb-line); + --vp-c-border: var(--sb-line); + --vp-c-gutter: var(--sb-line); + --vp-c-brand-1: var(--sb-accent); + --vp-c-brand-2: var(--sb-accent-hover); + --vp-c-brand-3: var(--sb-accent); + --vp-c-brand-soft: color-mix(in srgb, var(--sb-accent) 16%, transparent); + --sb-accent-green: var(--sb-accent); + + --sb-code-text: var(--sb-ink); + --sb-code-bg: var(--sb-surface-alt); + --sb-code-border: var(--sb-line); + --sb-code-block-bg: var(--sb-panel); + --vp-code-block-bg: var(--sb-panel); + + --color-positive: #d9ff43; + --shadow-lg: 0 12px 24px rgba(0, 0, 0, 0.6); } /* ─── Developer-doc sidebar: compact, structured, keyboard friendly ─── */ @@ -459,9 +549,9 @@ /* Custom container (tip, warning, danger) colors */ :root { - --vp-custom-block-tip-border: #cfd3d8; - --vp-custom-block-tip-bg: rgba(31, 33, 38, 0.035); - --vp-custom-block-tip-text: #303238; + --vp-custom-block-tip-border: var(--sb-line); + --vp-custom-block-tip-bg: var(--sb-surface); + --vp-custom-block-tip-text: var(--sb-ink); --vp-custom-block-warning-border: #e5a50a; --vp-custom-block-warning-bg: rgba(229, 165, 10, 0.08); @@ -581,10 +671,10 @@ html { /* ─── Reference-document layout: dense, quiet, and scan-first ─── */ :root { - --vp-nav-height: 64px; + --vp-nav-height: 72px; --vp-sidebar-width: 244px; --vp-layout-max-width: 1680px; - --sb-doc-rule: color-mix(in srgb, var(--vp-c-divider) 72%, transparent); + --sb-doc-rule: var(--sb-line); } /* Light-only surface tints. These MUST stay scoped to `:not(.dark)`: a bare @@ -592,8 +682,8 @@ html { it would clobber the dark surfaces and tint the sidebar, table rows, and inline code light while in dark mode. */ :root:not(.dark) { - --vp-c-bg-alt: #f8f9f8; - --vp-c-bg-soft: #f4f6f5; + --vp-c-bg-alt: var(--sb-canvas); + --vp-c-bg-soft: var(--sb-surface-alt); } .VPNav { @@ -663,7 +753,6 @@ html { .VPDocAside { width: 300px !important; padding-left: 28px; - border-left: 1px solid var(--sb-doc-rule); } .VPDoc .aside { @@ -1166,41 +1255,34 @@ html { the navigation rail keeps a stable width and the page always retains a right margin across every viewport wider than the mobile breakpoint. */ :root { - --sb-layout-gutter: max(0px, calc((100vw - var(--vp-layout-max-width)) / 2)); + /* Fluid side margin like the reference (page-inset): scales with the + viewport, ~24px on small screens up to 80px, then grows to centre the + shell once the viewport exceeds the max layout width. This keeps balanced + left/right gutters at every width instead of pinning the sidebar to the + window edge. */ + --sb-page-inset: clamp(1.5rem, 5.5556vw, 5rem); + --sb-layout-gutter: max( + var(--sb-page-inset), + calc((100vw - var(--vp-layout-max-width)) / 2 + var(--sb-page-inset)) + ); } @media (min-width: 960px) { - /* Rail box = intended width + centring gutter, keeping a fixed 32px inner - indent. Usable rail width therefore stays constant instead of shrinking - to nothing in the 1440–1680px range. */ + /* Sidebar content begins at the page inset (balanced left gutter) and sits + a comfortable distance below the fixed header so it isn't clipped by it + (the reference leaves a clear gap under the header). */ .VPSidebar { - padding-left: calc(32px + var(--sb-layout-gutter)) !important; + padding-left: var(--sb-layout-gutter) !important; + padding-top: calc(var(--vp-nav-height) + 40px) !important; width: calc(var(--vp-sidebar-width) + var(--sb-layout-gutter)) !important; } - /* Content is inset by the rail on the left and always keeps a right gutter - (at least 48px), so the outline rail never touches the window edge. */ + /* Content clears the sidebar on the left and keeps the same inset on the + right, so the outline column ends the same distance from the window edge + as the sidebar starts from the left. */ .VPContent.has-sidebar { padding-left: calc(var(--vp-sidebar-width) + var(--sb-layout-gutter)) !important; - padding-right: max(48px, var(--sb-layout-gutter)) !important; - } - - /* The nav bar reuses the same collapsing formula for its logo slot, action - area, and divider. Re-align all three with the guarded gutter so the logo - tracks the rail and the right-hand icons keep the same margin as the - content/outline below (otherwise the GitHub icon sits flush to the edge). */ - .VPNavBar.has-sidebar .title { - padding-left: calc(32px + var(--sb-layout-gutter)) !important; - width: calc(var(--vp-sidebar-width) + var(--sb-layout-gutter)) !important; - } - - .VPNavBar.has-sidebar .content { - padding-left: calc(var(--vp-sidebar-width) + var(--sb-layout-gutter)) !important; - padding-right: max(48px, var(--sb-layout-gutter)) !important; - } - - .VPNavBar.has-sidebar .divider { - padding-left: calc(var(--vp-sidebar-width) + var(--sb-layout-gutter)) !important; + padding-right: var(--sb-layout-gutter) !important; } } @@ -1412,3 +1494,494 @@ html { margin: 24px 0 32px; border-radius: 2px; } + +/* ══════════════════════════════════════════════════════════════════════════ + SandBase design layer + Header, footer, code panels, and pixel-poster typography ported from the + sandbase.ai main site and the sandbase /docs reference design. Appended last + so these rules win over the earlier documentation-neutral defaults. + ══════════════════════════════════════════════════════════════════════════ */ + +/* ─── Header / top navigation (mirrors the sandbase.ai site header) ─────── */ +.VPNav { + border-bottom: 1px solid var(--sb-line); +} + +.VPNavBar { + background: color-mix(in srgb, var(--sb-canvas) 92%, transparent) !important; + backdrop-filter: saturate(1.1) blur(12px); +} + +/* Brand: logo mark + Clash Grotesk wordmark. */ +.VPNavBarTitle .title { + gap: 0.6rem !important; + color: var(--sb-ink) !important; + font-family: var(--vp-font-family-base); + font-size: 1.5rem !important; + font-weight: 550 !important; + letter-spacing: -0.018em !important; + line-height: 1.1 !important; +} + +.dark .VPNavBarTitle .title { + color: var(--sb-ink) !important; +} + +.VPNavBar .VPImage.logo { + width: 22px; + height: 27px; + border-radius: 0; +} + +/* Primary nav links: ink by default, accent on hover / when active. */ +.VPNavBarMenuLink, +.VPNavBarMenuGroup .text { + color: var(--sb-ink) !important; + font-family: var(--vp-font-family-base); + font-size: 15px !important; + font-weight: 450 !important; + letter-spacing: 0.005em; +} + +.VPNavBarMenuLink:hover, +.VPNavBarMenuLink.active, +.VPNavBarMenuGroup:hover .text, +.VPNavBarMenuGroup.active .text { + color: var(--sb-accent) !important; +} + +/* The final nav item ("Start building") reads as the primary CTA button. */ +.VPNavBarMenu .VPNavBarMenuLink[href*="/console"] { + align-self: center; + margin-left: 8px; + padding: 9px 18px !important; + line-height: 1 !important; + border-radius: 4px; + background: var(--sb-ink); + color: var(--sb-canvas) !important; + font-weight: 500 !important; + transition: background-color 0.16s ease, color 0.16s ease; +} + +.VPNavBarMenu .VPNavBarMenuLink[href*="/console"]:hover { + background: var(--sb-tint); + color: var(--sb-tint-fg) !important; +} + +/* No external-link arrow on the solid CTA button. */ +.VPNavBarMenu .VPNavBarMenuLink[href*="/console"].vp-external-link-icon::after, +.VPNavBarMenu .VPNavBarMenuLink[href*="/console"] .vpi-external-link-icon { + display: none !important; +} + +/* Search trigger + appearance toggle: hairline, square-ish, on-canvas. */ +.VPNavBarSearch .DocSearch-Button, +.VPNavBarSearchButton { + border-radius: 4px; +} + +/* ─── Sidebar: on-canvas rail with monospace section labels ─────────────── */ +.VPSidebar { + background: var(--sb-canvas) !important; + border-right: 1px solid var(--sb-line); +} + +.VPSidebar .curtain { + background: var(--sb-canvas) !important; +} + +/* Top-level section headers become uppercase monospace micro-labels. */ +.VPSidebarItem.level-0 > .item .text { + font-family: var(--vp-font-family-mono); + font-size: 11px !important; + font-weight: 500 !important; + letter-spacing: 0.08em !important; + text-transform: uppercase; + color: var(--sb-ink-subtle) !important; +} + +.VPSidebarItem.level-0 + .VPSidebarItem.level-0 { + border-top: 1px solid var(--sb-line); +} + +/* Active leaf link: accent left rule + ink text (no heavy pill). */ +.VPSidebarItem.is-active > .item .link { + background: transparent; + color: var(--sb-ink) !important; + font-weight: 550; +} + +.VPSidebarItem.is-active > .item .link::before { + background: var(--sb-accent); +} + +/* ─── "On this page" outline: monospace label ───────────────────────────── */ +.VPDocAsideOutline .outline-title { + font-family: var(--vp-font-family-mono); + font-size: 11px; + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--sb-ink-subtle); +} + +.VPDocAsideOutline .outline-link.active { + color: var(--sb-accent); +} + +/* ─── Fenced code: dark panel with a monospace language label ───────────── */ +.vp-doc div[class*='language-'], +.vp-doc:not(:has(.api-reference-page)) div[class*='language-'] { + border: 1px solid var(--sb-line); + border-radius: 4px; + background: var(--sb-panel); +} + +.vp-doc [class*='language-'] .lang { + color: var(--sb-panel-label); + font-family: var(--vp-font-family-mono); + font-size: 11px; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.vp-doc [class*='language-'] code { + color: var(--sb-panel-fg); +} + +/* Keep code readable at all widths (undo the hover-to-reveal scroll trick). */ +.vp-doc div[class*="language-"] pre { + overflow-x: auto; +} + +/* ─── Inline code chips ─────────────────────────────────────────────────── */ +.vp-doc p > code, +.vp-doc li > code, +.vp-doc td > code, +.vp-doc dd > code { + border-radius: 4px; +} + +/* ─── Custom blocks: hairline, near-square ──────────────────────────────── */ +.vp-doc .custom-block, +.vp-doc:not(:has(.api-reference-page)) .custom-block { + border-radius: 4px; +} + +/* ─── Pixel-square list bullets in the accent colour (prose only) ───────── */ +.vp-doc:not(:has(.api-reference-page)) ul { + list-style: none; + padding-left: 1.2rem; +} + +.vp-doc:not(:has(.api-reference-page)) ul > li { + position: relative; +} + +.vp-doc:not(:has(.api-reference-page)) ul > li:not(.task-list-item)::before { + content: ''; + position: absolute; + left: -1.05rem; + top: 0.62em; + width: 7px; + height: 7px; + background: var(--sb-accent); +} + +/* ─── Tables: monospace uppercase headers ───────────────────────────────── */ +.vp-doc table th { + font-family: var(--vp-font-family-mono); + font-size: 11px; + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--sb-ink-subtle); +} + +/* ─── Focus ring uses the accent, matching the reference design ─────────── */ +:root { + --vp-c-brand-1: var(--sb-accent); +} +*:focus-visible { + outline-color: var(--sb-accent); +} + +/* ══════════════════════════════════════════════════════════════════════════ + Footer (mirrors the sandbase.ai site footer) + Brand + tagline + social row · four navigation columns · bottom rule with + copyright and service status. + ══════════════════════════════════════════════════════════════════════════ */ +.sb-contact-footer { + display: block; + gap: 0; + margin-top: 64px; + padding: 48px 0 28px; + border-top: 1px solid var(--sb-line); +} + +.sb-contact-main { + display: grid; + grid-template-columns: minmax(240px, 1fr) minmax(0, 2.1fr); + gap: 56px; + align-items: start; +} + +.sb-contact-brand { + display: flex; + flex-direction: column; + gap: 20px; + max-width: 22rem; + min-width: 0; +} + +.sb-contact-logo { + display: inline-flex; + align-items: center; + gap: 0.6rem; + color: var(--sb-ink); + text-decoration: none; +} + +.dark .sb-contact-logo { + color: var(--sb-ink); +} + +.sb-contact-mark { + display: grid; + width: 22px; + height: 27px; + flex: none; + overflow: visible; + border-radius: 0; + color: inherit; +} + +.sb-contact-wordmark { + font-family: var(--vp-font-family-base); + font-size: 1.4rem; + font-weight: 550; + letter-spacing: -0.018em; + line-height: 1; +} + +.sb-contact-tagline { + margin: 0; + max-width: 24rem; + color: var(--sb-ink-muted); + font-size: 14.5px; + line-height: 1.6; +} + +.sb-contact-links { + display: flex; + align-items: center; + gap: 18px; + margin-top: 2px; +} + +.sb-contact-link { + color: var(--sb-ink-muted); +} + +.sb-contact-link:hover { + color: var(--sb-accent); + transform: translateY(-1px); +} + +.sb-footer-column h2 { + font-family: var(--vp-font-family-mono); + font-size: 11px; + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--sb-ink-subtle); +} + +.sb-footer-column a { + color: var(--sb-ink-muted); + font-size: 14px; + font-weight: 450; +} + +.sb-footer-column a:hover { + color: var(--sb-accent); +} + +.sb-footer-bottom { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-top: 48px; + padding-top: 24px; + border-top: 1px solid var(--sb-line); +} + +.sb-footer-copy { + margin: 0; + color: var(--sb-ink-subtle); + font-size: 13px; +} + +.sb-footer-status { + display: inline-flex; + align-items: center; + gap: 8px; + color: var(--sb-ink-muted); + font-size: 13px; + text-decoration: none; +} + +.sb-footer-status:hover { + color: var(--sb-ink); +} + +.sb-footer-status-dot { + width: 7px; + height: 7px; + border-radius: 999px; + background: light-dark(#3f7a1a, #d9ff43); +} + +:root:not(.dark) .sb-footer-status-dot { + background: #3f7a1a; +} + +.dark .sb-footer-status-dot { + background: #d9ff43; +} + +@media (max-width: 959px) { + .sb-contact-main { + grid-template-columns: 1fr; + gap: 32px; + } +} + +/* ══════════════════════════════════════════════════════════════════════════ + Replace the default VitePress nav with the custom SandBase site header + (rendered via the layout-top slot). The default nav stays in the component + tree (so its search hotkey and the mobile sidebar plumbing keep working) but + is hidden; content is already offset by --vp-nav-height, which the fixed + custom header fills. The mobile docs-sidebar toggle lives in VPLocalNav, + which is independent of VPNav and remains visible. + ══════════════════════════════════════════════════════════════════════════ */ +.VPNav { + display: none !important; +} + +/* VPLocalNav (mobile sidebar + outline bar) sits directly below the header. */ +.VPLocalNav { + border-bottom: 1px solid var(--sb-line); +} + +/* ══════════════════════════════════════════════════════════════════════════ + Docs sidebar — match the sandbase /docs reference design's link rail. + Group titles are uppercase monospace micro-labels; leaf links form a + vertical hairline rail with an accent segment when active and a stronger + segment on hover (no pills, no radius). Appended last to win over the + earlier documentation-neutral sidebar rules. + ══════════════════════════════════════════════════════════════════════════ */ +.VPSidebarItem.level-0 > .item .text { + font-family: var(--font-family-mono) !important; + font-size: 11px !important; + font-weight: 500 !important; + letter-spacing: 0.08em !important; + text-transform: uppercase; + color: var(--color-ink-subtle) !important; +} + +/* Separate top-level groups by space only (the reference has no dividers). */ +.VPSidebarItem.level-0 + .VPSidebarItem.level-0 { + margin-top: 1.5rem; + padding-top: 0; + border-top: 0; +} + +/* Menu rail: a single subtle vertical line beside each link list (the ONLY + lines in the sidebar). The active link shows an accent segment over it. */ +.VPSidebarItem .items { + border-left: 1px solid var(--color-line); +} +.VPSidebarItem.level-1 > .item .link, +.VPSidebarItem.level-2 > .item .link, +.VPSidebarItem.level-3 > .item .link { + margin-left: -1px; + border-left: 1px solid transparent; + border-top: 0 !important; + border-right: 0 !important; + border-bottom: 0 !important; + border-radius: 0 !important; + background: transparent !important; + color: var(--color-ink-muted); + transition: color 150ms ease, border-color 150ms ease; +} +.VPSidebarItem.level-1 > .item .link:hover, +.VPSidebarItem.level-2 > .item .link:hover, +.VPSidebarItem.level-3 > .item .link:hover { + border-left-color: var(--sb-line-strong) !important; + background: transparent !important; + color: var(--color-ink) !important; +} +.VPSidebarItem.level-1 > .item .link:hover > .text, +.VPSidebarItem.level-2 > .item .link:hover > .text, +.VPSidebarItem.level-3 > .item .link:hover > .text { + color: var(--color-ink) !important; +} + +/* Active leaf link: accent rail segment, ink text, medium weight. */ +.VPSidebarItem.is-active > .item .link { + margin-left: -1px; + border-left: 1px solid var(--color-accent) !important; + background: transparent !important; + color: var(--color-ink) !important; + font-weight: 500; +} +.VPSidebarItem.is-active > .item .link > .text { + color: var(--color-ink) !important; +} +.VPSidebarItem.is-active > .item .link::before { + display: none !important; +} +.VPSidebarItem > .item .link:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: -2px; + background: transparent !important; +} + +/* No horizontal lines anywhere in the sidebar (groups are spaced, not ruled), + and no divider between the sidebar and content. */ +.VPSidebar, +.VPSidebarItem, +.VPSidebarItem > .item, +.VPSidebarItem.level-0, +.VPSidebarItem.level-0 + .VPSidebarItem.level-0 { + border-top: 0 !important; + border-bottom: 0 !important; +} +.VPSidebarItem.level-0 + .VPSidebarItem.level-0 { + margin-top: 1.5rem; + padding-top: 0 !important; +} +.VPSidebar { + border-right: 0 !important; +} + +/* VitePress wraps each top-level section in
and draws a + horizontal divider via `.group + .group { border-top }`. The reference has + no horizontal dividers, so remove it and separate sections by space only. */ +.VPSidebar .group + .group { + border-top: 0 !important; + padding-top: 0 !important; + margin-top: 0.5rem; +} + +/* "On this page" outline: the reference has no left divider rail and no active + marker bar — just plain links. Remove both. */ +.VPDocAsideOutline { + border-left: 0 !important; +} +.VPDocAsideOutline .outline-marker { + display: none !important; +} diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts index 36b58d28..40e0a8e5 100644 --- a/.vitepress/theme/index.ts +++ b/.vitepress/theme/index.ts @@ -2,20 +2,30 @@ import DefaultTheme from 'vitepress/theme' import { h } from 'vue' import HomePage from './HomePage.vue' import ApiReferencePage from './ApiReferencePage.vue' -import ContactFooter from './ContactFooter.vue' import QuickstartOnboard from './QuickstartOnboard.vue' import QuickstartResources from './QuickstartResources.vue' import PlatformApiSidebar from './PlatformApiSidebar.vue' import OfficialNativeApiSidebar from './OfficialNativeApiSidebar.vue' import PlatformApiLanding from './PlatformApiLanding.vue' +import SiteHeader from './SiteHeader.vue' +import SiteFooter from './SiteFooter.vue' +import DocsSearch from './DocsSearch.vue' import './custom.css' export default { ...DefaultTheme, Layout() { return h(DefaultTheme.Layout, null, { + // Replace VitePress's default top nav with a 1:1 replica of the + // sandbase.ai site header, and render the site footer full-width at the + // bottom. The default nav is hidden via custom.css but stays mounted so + // its search hotkey and mobile sidebar plumbing keep working. + // The docs sidebar leads with a "Search docs" button (matching the + // reference design) that triggers VitePress's local search. + 'sidebar-nav-before': () => h(DocsSearch), 'sidebar-nav-after': () => [h(PlatformApiSidebar), h(OfficialNativeApiSidebar)], - 'doc-after': () => h(ContactFooter), + 'layout-top': () => h(SiteHeader), + 'layout-bottom': () => h(SiteFooter), }) }, enhanceApp({ app }) { diff --git a/public/ClashGrotesk-Variable.woff2 b/public/ClashGrotesk-Variable.woff2 new file mode 100644 index 00000000..d5b13d07 Binary files /dev/null and b/public/ClashGrotesk-Variable.woff2 differ