Skip to content
CodeAtlas UI

Docs kit

Docs primitives

Components for developer documentation sites, from the page shell and navigation to code tabs, callouts and prose typography.

This site is built with these primitives. The kit covers DocsShell (header, sidebar, page index and a mobile Sheet menu), DocsNav, TableOfContents, DocsSearch, CodeTabs, Callout, Steps, LinkCard, CardGrid, MethodBadge and the ca-prose typography.

<script>
  import { DocsShell } from "@codeatlasdev/ui";
</script>

<DocsShell
  {sections} active="guide" {groups} {pathname} {headings}
  searchIndex={() => fetch("/search.json").then((r) => r.json())}
  onnavigate={(href) => (location.href = href)}
>
  {#snippet brand()}<Logo /> Acme Docs{/snippet}
  <article class="ca-prose">...</article>
</DocsShell>

DocsShell is a full-page frame, so it is not embedded here. The page you are reading is the reference integration: see SiteDocsShell.svelte in the site/ workspace. On desktop (1024px and up) it is a sticky glass header over a three-column grid: sidebar, content and a “On this page” rail from 1280px. Below 1024px it becomes one compact header, the menu opens a bottom Sheet and the page index turns into an inline disclosure. Cmd+K, Ctrl+K and / open the search.

ca-prose, Callout and Steps

Wrap rendered markdown in .ca-prose. Callout is an Alert vocabulary (note, tip, caution, danger); keep two per page. Steps turns an ordered list into a numbered rail.

Prerequisites

You need an API key and a connected session. The x-api-key header identifies the organization and x-instance picks the session.

Authentication

The x-api-key header

Send the key in the x-api-key header on every request.

  1. Create a session

    POST /sessions returns the session id.

  2. Connect with a QR code

    Scan it from Linked devices in the mobile app.

Send a message

FieldTypeDescription
tostringNumber with country code, no symbols. Example: 5511999990000
textstringMessage body, up to 4096 characters.
Code
svelte
<script lang="ts">
  import { Callout, Steps } from '@codeatlasdev/ui';
</script>

<div class="ca-prose prose">
  <h2 id="demo-prerequisites">Prerequisites</h2>
  <p>
    You need an <strong>API key</strong> and a connected session. The <code>x-api-key</code> header
    identifies the organization and <code>x-instance</code> picks the session.
  </p>
  <h2 id="demo-auth">Authentication</h2>
  <h3 id="demo-header">The x-api-key header</h3>
  <p>Send the key in the <code>x-api-key</code> header on every request.</p>
  <Callout type="tip">Use one key per environment so you can revoke one without touching production.</Callout>
  <Callout type="caution">Never expose the key in front-end code.</Callout>
  <Steps>
    <ol>
      <li><p><strong>Create a session</strong></p><p><code>POST /sessions</code> returns the session <code>id</code>.</p></li>
      <li><p><strong>Connect with a QR code</strong></p><p>Scan it from Linked devices in the mobile app.</p></li>
    </ol>
  </Steps>
  <h2 id="demo-send">Send a message</h2>
  <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
  <div class="ca-table-wrap" tabindex="0" role="region" aria-label="Request fields">
    <table>
      <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
      <tbody>
        <tr><td><code>to</code></td><td>string</td><td>Number with country code, no symbols. Example: 5511999990000</td></tr>
        <tr><td><code>text</code></td><td>string</td><td>Message body, up to 4096 characters.</td></tr>
      </tbody>
    </table>
  </div>
</div>

<style>
  .prose { width: 100%; max-width: 100%; }
</style>

CodeTabs

Language tabs with a copy button. Groups that share a sync key follow each other and persist the choice in localStorage. Highlighting stays with you: wrap each block in .ca-code.

curl -X POST https://api.example.com/messages/text \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-instance: my-session" \
  -d '{"to":"5511999990000","text":"Hello"}'
curl https://api.example.com/sessions -H "x-api-key: YOUR_API_KEY"
Code
svelte
<script lang="ts">
  import { CodeTab, CodeTabs } from '@codeatlasdev/ui';

  const curl = `curl -X POST https://api.example.com/messages/text \\
  -H "x-api-key: YOUR_API_KEY" \\
  -H "x-instance: my-session" \\
  -d '{"to":"5511999990000","text":"Hello"}'`;
  const node = `await fetch("https://api.example.com/messages/text", {
  method: "POST",
  headers: { "x-api-key": process.env.API_KEY, "x-instance": "my-session" },
  body: JSON.stringify({ to: "5511999990000", text: "Hello" }),
});`;
  const curlList = `curl https://api.example.com/sessions -H "x-api-key: YOUR_API_KEY"`;
  const nodeList = `await fetch("https://api.example.com/sessions", { headers: { "x-api-key": KEY } });`;
</script>

<div class="ca-prose wrap">
  <CodeTabs sync="demo-lang">
    <CodeTab label="cURL">
      <div class="ca-code"><pre><code>{curl}</code></pre></div>
    </CodeTab>
    <CodeTab label="Node.js">
      <div class="ca-code"><pre><code>{node}</code></pre></div>
    </CodeTab>
  </CodeTabs>
  <CodeTabs sync="demo-lang">
    <CodeTab label="cURL">
      <div class="ca-code"><pre><code>{curlList}</code></pre></div>
    </CodeTab>
    <CodeTab label="Node.js">
      <div class="ca-code"><pre><code>{nodeList}</code></pre></div>
    </CodeTab>
  </CodeTabs>
</div>

<style>
  .wrap { width: 100%; max-width: 100%; }
</style>

LinkCard, CardGrid and MethodBadge

Code
svelte
<script lang="ts">
  import { CardGrid, LinkCard, MethodBadge } from '@codeatlasdev/ui';
</script>

<div class="wrap">
  <CardGrid>
    <LinkCard title="Quick start" description="From install to your first request in two minutes." href="#top" />
    <LinkCard title="Webhooks" description="Real-time events with retries and signatures." href="#top" />
  </CardGrid>
  <div class="methods">
    <MethodBadge method="GET" />
    <MethodBadge method="POST" />
    <MethodBadge method="PUT" />
    <MethodBadge method="PATCH" />
    <MethodBadge method="DELETE" />
  </div>
</div>

<style>
  .wrap { width: 100%; display: grid; gap: 16px; }
  .methods { display: flex; flex-wrap: wrap; gap: 12px; }
</style>

DocsNav and TableOfContents

The sidebar tree and the page index. The inline variant is shown; DocsShell uses the rail variant on desktop.

Code
svelte
<script lang="ts">
  import { DocsNav, TableOfContents, type DocsHeading, type DocsNavGroup } from '@codeatlasdev/ui';

  const headings: DocsHeading[] = [
    { id: 'demo-prerequisites', text: 'Prerequisites', depth: 2 },
    { id: 'demo-auth', text: 'Authentication', depth: 2 },
    { id: 'demo-header', text: 'The x-api-key header', depth: 3 },
    { id: 'demo-send', text: 'Send a message', depth: 2 },
  ];

  const groups: DocsNavGroup[] = [
    {
      label: 'Get started',
      items: [
        { href: '/docs/docs-kit/docs/', title: 'Introduction' },
        { href: '/x/quick-start', title: 'Quick start' },
      ],
    },
    {
      label: 'Messages',
      items: [
        { href: '/x/send-text', title: 'Send text', badge: 'POST' },
        { href: '/x/list', title: 'List messages', badge: 'GET' },
        { href: '/x/edit', title: 'Edit message', badge: 'PUT' },
        { href: '/x/delete', title: 'Delete message', badge: 'DELETE' },
      ],
    },
  ];
</script>

<div class="pair">
  <DocsNav {groups} pathname="/docs/docs-kit/docs/" onNavigate={() => {}} />
  <TableOfContents {headings} variant="inline" />
</div>

<style>
  .pair {
    width: 100%;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 24px;
    align-items: start;
  }
</style>

DocsSearch

Full-text search. A dialog on desktop and a full-height sheet with a 16px input on phones. Pass entries or an async loader.

Code
svelte
<script lang="ts">
  import { Button, DocsSearch, type DocsSearchEntry } from '@codeatlasdev/ui';

  let open = $state(false);
  let picked = $state('');

  const index: DocsSearchEntry[] = [
    {
      href: '/x/send-text',
      title: 'Send text',
      section: 'API',
      description: 'Sends a text message to a number or group.',
      headings: [{ text: 'Request body', id: 'body' }],
      text: 'POST /messages/text with x-api-key and x-instance.',
    },
    {
      href: '/x/webhooks',
      title: 'Webhooks',
      section: 'Guide',
      description: 'Receive events in real time.',
      headings: [{ text: 'HMAC signature', id: 'hmac-signature' }],
      text: 'Retries with backoff and an HMAC signature.',
    },
  ];
</script>

<Button variant="outlined" onclick={() => (open = true)}>Open search</Button>
{#if picked}<span class="picked" aria-live="polite">Selected: <code>{picked}</code></span>{/if}
<DocsSearch bind:open {index} onselect={(href) => (picked = href)} />

<style>
  .picked { font-size: var(--ca-font-size-body2); color: var(--ca-text-secondary); }
  .picked code { font-family: var(--ca-font-mono); color: var(--ca-brand-text); }
</style>

DocsShell

PropTypeDefaultDescription
sectionsDocsSection[]–{ id, label, href } entries shown as header tabs and in the mobile Sheet.
activestring–id of the current section.
groupsDocsNavGroup[][]Sidebar tree of the current section. Empty hides the sidebar and the page index.
pathnamestring–Current path, used to mark aria-current in the tree.
headingsDocsHeading[][]Page index: a rail from 1280px and an inline disclosure below.
searchIndexDocsSearchEntry[] | () => Promise<DocsSearchEntry[]>–Required. Entries, or a lazy loader called on first open.
onnavigate(href: string) => void–Required. Called when a search result is picked (pass goto).
brandHrefstring'/'Link target of the brand.
brandLabelstring–Accessible label of the brand link.
brandSnippet–Required. Brand mark and name inside the header link.
actionsSnippet–Extra header actions on the right. Keep to one or two icon buttons.
menuFooterSnippet–Content below the sidebar tree in the mobile Sheet, such as external links.
childrenSnippet–Required. Page content.

DocsNav

PropTypeDefaultDescription
groupsDocsNavGroup[]–Labelled groups of { href, title, badge? } links.
pathnamestring–Current path, marks the current page with aria-current.
labelstring–Accessible label of the nav landmark.
onNavigate() => void–Called after a link is followed, to close the mobile Sheet.

TableOfContents

PropTypeDefaultDescription
headingsDocsHeading[]–{ id, text, depth: 2 | 3 } entries. Renders nothing for fewer than 2 headings.
variant'rail' | 'inline''rail'A sticky list, or a collapsible disclosure that shows the current section.
labelstring–Accessible label of the nav landmark.
classstring''Extra CSS classes.

DocsSearch

PropTypeDefaultDescription
openbooleanfalseBindable open state.
indexDocsSearchEntry[] | () => Promise<DocsSearchEntry[]>–Required. Entries, or a loader called once on first open.
onselect(href: string) => void–Required. Called with the picked result's href.
placeholderstring–Input placeholder. Defaults to the localized string.
hintstring–Footer hint text. Defaults to the localized string.

Prose blocks

PropTypeDefaultDescription
CodeTabs.syncstring'lang'Groups with the same key follow the chosen label. An empty string disables sync.
CodeTab.labelstring–Tab text and sync key, for example 'cURL' or 'Node.js'.
Callout.type'note' | 'tip' | 'caution' | 'danger''note'Maps to Alert severity (info, success, warning, error).
Callout.titlestring–Overrides the default localized title.
StepsSnippet–Wraps an ordered list; each top-level item becomes a numbered step.
LinkCard.title / description / hrefstring–Whole-card link. title and href are required; the minimum height is 44px.
CardGridSnippet–Responsive auto-fill grid for LinkCard: 16rem minimum, one column on phones.
MethodBadge.methodstring–HTTP method label: GET, POST, PUT, PATCH or DELETE.
MethodBadge.variant'pill' | 'plain''pill'plain drops the tinted pill, for tight lists like a sidebar.