Reference

# Components

Every DocsUI component — shown live where it makes sense, with its calling code and args.

## Shell

The whole HTML document you're looking at right now.

`DocsUI::Shell` is the top-level page: the topbar (brand + `ThemeSwitcher`), the `Sidebar`, the content column, and the auto TOC. It yields the page body. `Page` renders it for you — you rarely construct it directly.

```ruby
DocsUI::Shell(title: "My guide", on_page: :panel) do
  # page body
end
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | String, nil | nil | Document + topbar title. Falls back to the site brand. |
| `on_page` | Symbol, false | false | TOC placement — :panel / :toggle / :sidebar / false. |

## Page

The base class you subclass for every docs page — including this one.

Subclass `DocsUI::Page`, set `title`/`eyebrow` with the class-level DSL, and implement `#content` (and optionally `#lead`). The page wraps your content in the `Shell` with a `Header` masthead and auto TOC.

```ruby
class Views::Docs::Pages::Guide < DocsUI::Page
  title   "My guide"
  eyebrow "Reference"
  on_page :toggle          # :panel | :toggle | :sidebar | false

  def lead = "One-sentence summary."
  def content = DocsUI::Section("Hello") { prose { p { "..." } } }
end
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | String (class DSL) | — | Sets the document + masthead title. |
| `eyebrow` | String (class DSL) | nil | Small kicker above the h1 (e.g. the group). |
| `on_page` | Symbol (class DSL) | config default | TOC placement — :panel / :toggle / :sidebar / false. |
| `#lead` | instance method | nil | Muted summary paragraph under the h1. |
| `#content` | instance method | — | The page body — call kit components here. |

## Header

The masthead: eyebrow + h1 + optional lead.

The block at the top of this page — kicker, heading, summary — is a `DocsUI::Header`. `Page` builds it from your `title`/`eyebrow`/`#lead`, so you seldom render it yourself.

```ruby
DocsUI::Header("My guide", eyebrow: "Reference") do
  plain "An optional lead paragraph."
end
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | String (positional) | — | The h1 text. Legacy title: kwarg still accepted. |
| `eyebrow` | String, nil | nil | Small kicker above the h1. |
| `block` | Phlex block | nil | Optional lead paragraph rendered under the h1. |

## Section

An anchored section wrapper — this description is its description: arg.

Every block on this page is a `DocsUI::Section`. It renders a `<section id>` with an `<h2>`, an optional muted `description:` lead, then your block. The `id` auto-slugs from the title (feeding the TOC), or pass `id:` to override it.

```ruby
DocsUI::Section("Getting started", id: "start", description: "Read me first.") do
  prose { p { "Section body." } }
end
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | String | — | The h2 text; auto-slugs into the anchor id. |
| `id` | String, nil | slug of title | Override the section anchor. |
| `description` | String, callable, nil | nil | Muted lead paragraph under the h2. |

## Prose

A typographic wrapper for hand-authored HTML.

Prose gives hand-authored text a consistent reading rhythm without a typography plugin.

- lists,
- inline code,
- links — all styled.

The call that produced the block above:

```ruby
prose do
  p { "Prose gives hand-authored text a consistent reading rhythm." }
  ul { li { "lists," }; li { "inline code," }; li { "links." } }
end
```

> **Tip:** On a `DocsUI::Page` use the lowercase `prose do … end` helper — a method call, no parens needed. The kit form `DocsUI::Prose() do … end` also works, but bare `DocsUI::Prose do` is a SyntaxError, so it needs the empty `()`.

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `block` | Phlex block | — | Hand-authored HTML — p, ul/li, code, strong, a, plain text. |

## Code

A Rouge-highlighted code block with an optional filename bar.

```ruby
class User < ApplicationRecord
  has_many :posts
end
```

The call that produced the block above:

```ruby
DocsUI::Code(source, lexer: :ruby, filename: "app/models/user.rb")
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | String | — | The code to highlight. |
| `lexer` | Symbol | inferred | Any Rouge language — :shell, :yaml, :erb, :python, :go, etc. Overrides the filename guess; ruby when neither is given. |
| `filename` | String, nil | nil | Optional filename bar above the block. Also selects the language (*.yml → yaml, Dockerfile → docker, *.sh → shell). |

## Example

Multi-language tabbed code with a sticky, global language choice.

Ruby

Python

```ruby
Anthropic::Client.new.messages.create(model: "claude-opus-4-8", messages: msgs)
```

```python
anthropic.Anthropic().messages.create(model="claude-opus-4-8", messages=msgs)
```

The call that produced the tabs above:

```ruby
example do |ex|
  ex.code(:ruby, filename: "client.rb")   { ruby_source }
  ex.code(:python, filename: "client.py") { python_source }
end
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `block` | Phlex block | — | Yields an object with #code — one call per language. |
| `ex.code lang` | Symbol | — | The Rouge language for this tab. |
| `ex.code filename:` | String, nil | nil | Optional filename bar for this tab. |
| `ex.code lexer:` | Symbol | lang | Override the Rouge lexer if it differs from the tab label. |

## Table & PropTable

Reference tables — generic headers+rows, and a name/type/default/description preset.

`DocsUI::Table` renders headers + rows in the kit's daisyUI look. A cell is a `String` (plain, escaped), a `[:code, "x"]` pair (inline `<code>`), or a `[:md, "…"]` pair (inline Markdown). `DocsUI::PropTable` is the preset every args table on this page uses — the same shape, first column auto code-styled, default `Option/Type/Default/Description` headers.

| Cell | Renders as |
| --- | --- |
| brand | plain, escaped text |
| `%w[dark light]` | inline code |
| a **bold** note | inline markdown |

The call that produced the table above:

```ruby
DocsUI::Table(
  [ "Cell", "Renders as" ],
  [
    [ "brand", "plain, escaped text" ],
    [ [ :code, "%w[dark light]" ], "inline code" ],
    [ [ :md, "a **bold** note" ], "inline markdown" ]
  ]
)
```

> **Tip:** Every args table on this page is a `DocsUI::PropTable` — pass just the rows; the headers default to `Option/Type/Default/Description` (override with `headers:`).

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `DocsUI::Table headers` | Array | — | Header labels — one per column. |
| `DocsUI::Table rows` | Array | — | Rows; each a cell array (String / [:code, x] / [:md, …]). |
| `DocsUI::PropTable rows` | Array | — | Rows; the first cell is auto-wrapped in <code>. |
| `DocsUI::PropTable headers:` | Array | Option/Type/Default/Description | Override the header labels. |

## Endpoint, FieldTable & ErrorTable

The API-reference kit — a method+path line, a fields table, and an error table.

`DocsUI::Endpoint` renders an HTTP method badge (coloured per verb) plus a monospace path, inline — so it drops straight into a `Section` description. `FieldTable` and `ErrorTable` are keyword-schema presets over `Table` for an object's fields and an endpoint's errors.

## Create a webhook endpoint

`POST` `/api/webhook_endpoints`

Registers a destination URL for outbound event notifications.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | ✓ | HTTPS destination URL. |
| `description` | string | — | Optional internal label. |
| `events` | array | ✓ | Event types, e.g. `payment_link.paid`. |

| Scenario | Status | Type | Param |
| --- | --- | --- | --- |
| Missing or invalid API key | 401 | `authentication_error` | — |
| Non-HTTPS URL | 422 | `validation_error` | `url` |
| Unknown event name | 422 | `validation_error` | `events` |

The calls that produced the block above:

```ruby
DocsUI::Section("Create a webhook endpoint",
  description: DocsUI::Endpoint.new(:post, "/api/webhook_endpoints")) do
  render DocsUI::FieldTable.new([
    { name: "url", type: "string", required: true, description: "HTTPS destination URL." },
    { name: "events", type: "array", required: true, description: [:md, "e.g. `payment_link.paid`."] }
  ])
  render DocsUI::ErrorTable.new([
    { scenario: "Non-HTTPS URL", status: "422", type: "validation_error", param: "url" }
  ])
end
```

> **Tip:** Verb → colour is a frozen Hash of literal badge classes (`GET` → success, `POST` → primary, `PATCH/PUT` → warning, `DELETE` → error). An unknown verb renders a neutral badge — no raise.

| Call | Type | Default | Description |
| --- | --- | --- | --- |
| `DocsUI::Endpoint.new(method, path)` | Symbol/String, String | — | Method badge + monospace path; renders inline. |
| `DocsUI::FieldTable.new(fields)` | Array<Hash> | — | Each: { name:, type:, required: false, description: }. |
| `DocsUI::ErrorTable.new(errors)` | Array<Hash> | — | Each: { scenario:, status:, type:, param: nil }; Param column auto-hidden. |
| `Section(description:)` | String, proc, or component | nil | Now also accepts a Phlex component instance. |

## RequestExample & JsonResponse

The API-docs kit — declare a request once, get every client tab; render a Ruby hash as a JSON response.

`DocsUI::RequestExample` turns one structured request declaration (`method:`/`path:`/`body:`) into a `DocsUI::Example` with one tab per configured client — `curl`, `javascript`, `ruby`, `python` by default (a site adds its own, e.g. a `cli` tab). `DocsUI::JsonResponse` renders a Ruby Hash as pretty-printed JSON — no hand-rolled `deep_stringify`.

## Create a payment link

`POST` `/v1/payment_links`

Creates a shareable payment link for a fixed amount.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `amount` | integer | ✓ | Amount in the smallest currency unit. |
| `currency` | string | ✓ | ISO 4217 currency code. |
| `description` | string | — | Shown to the payer at checkout. |

cURL

JavaScript

Ruby

Python

```console
curl -X POST 'https://api.example.com/v1/payment_links' \
  -H "Content-Type: application/json" \
  -d '{
  "amount": 4900,
  "currency": "usd",
  "description": "Pro plan"
}'
```

```javascript
const response = await fetch("https://api.example.com/v1/payment_links", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({"amount":4900,"currency":"usd","description":"Pro plan"}),
});
const data = await response.json();
```

```ruby
require "net/http"
require "json"

uri = URI("https://api.example.com/v1/payment_links")
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request.body = {
  "amount": 4900,
  "currency": "usd",
  "description": "Pro plan"
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end
```

```python
import requests

response = requests.post(
    "https://api.example.com/v1/payment_links",
    headers={"Content-Type": "application/json"}, json={
  "amount": 4900,
  "currency": "usd",
  "description": "Pro plan"
},
)
data = response.json()
```

A successful response:

```json
{
  "id": "plink_1a2b3c",
  "object": "payment_link",
  "amount": 4900,
  "currency": "usd",
  "url": "https://pay.example.com/plink_1a2b3c",
  "active": true
}
```

The calls that produced the block above:

```ruby
render DocsUI::RequestExample.new(
  method: :post,
  path: "/v1/payment_links",
  body: { amount: 4900, currency: "usd", description: "Pro plan" }
)
render DocsUI::JsonResponse.new(
  { id: "plink_1a2b3c", object: "payment_link", amount: 4900,
    currency: "usd", url: "https://pay.example.com/plink_1a2b3c", active: true }
)
```

> **Tip:** The client set, base URL, and example auth header are config: `c.api_clients` (defaults + your overrides), `c.api_base_url`, and `c.api_auth_header`. Override a default token to swap in an SDK-flavored snippet; add a new token (e.g. `cli`) to append a tab.

| Call | Type | Default | Description |
| --- | --- | --- | --- |
| `RequestExample method:/path:` | Symbol/String, String | — | The HTTP verb and path (path is appended to c.api_base_url). |
| `RequestExample body:` | Hash, nil | nil | Request payload; deep-stringified into each snippet. Omit for a GET. |
| `RequestExample query:/headers:` | Hash | {} | Query params and extra headers merged into every snippet. |
| `RequestExample clients:` | Array<Symbol>, nil | all configured | Filter/order the tabs (e.g. [:curl, :ruby]). |
| `JsonResponse.new(body)` | Hash or String | — | Hash → pretty JSON with string keys; String → passed through. |
| `JsonResponse filename:` | String | "response.json" | The code block's title-bar filename. |

## Callout

note / tip / warning — a daisyUI alert with a lucide icon.

> **Note:** This is a note callout.

> **Tip:** A tip callout — for handy asides.

> **Warning:** A warning callout — for gotchas.

The calls that produced the boxes above:

```ruby
DocsUI::Callout(:note)    { "This is a note callout." }
DocsUI::Callout(:tip)     { "A tip callout." }
DocsUI::Callout(:warning) { "A warning callout." }
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `level` | Symbol | :note | Alert style — :note / :tip / :warning. |
| `title` | String, nil | nil | Optional heading above the body. |
| `block` | Phlex block | — | The callout body. |

## Icon

A lucide icon by name; extra attributes pass through.

The calls that produced the icons above:

```ruby
DocsUI::Icon("rocket", class: "size-6")
DocsUI::Icon("book-open", class: "size-6")
DocsUI::Icon("paintbrush", class: "size-6")
```

> **Note:** Icons no-op gracefully if `rails_icons` isn't configured — nothing renders, no error.

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | String | — | The lucide icon name, e.g. "rocket". |
| `**attributes` | Hash | {} | Extra HTML attributes (class:, etc.) passed to the icon. |

## OnThisPage

The auto-TOC — the panel Shell renders from your on_page setting.

The TOC is built from the page's `Section` anchors. You don't construct it — `Shell` renders it based on the page's `on_page` setting. Set the mode per page or as a config default.

```ruby
class Views::Docs::Pages::Api < DocsUI::Page
  on_page :toggle   # :panel | :toggle | :sidebar | false
end
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | Symbol | :panel | Placement — :panel (aside) / :toggle (button) / :sidebar. |
| `title` | String | "On this page" | The TOC heading. |

## Sidebar

The left nav — built from your config, rendered by Shell.

The sidebar is driven entirely by `DocsKit.configuration.nav` — a callable returning grouped `DocsKit::NavItem`s. `Shell` renders it, so you configure it rather than construct it.

```ruby
DocsKit.configure do |c|
  c.nav = -> { { "Docs" => Doc.grouped } }
end
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `(none)` | — | — | No args — reads DocsKit.configuration.nav. |

## ThemeSwitcher

The theme dropdown — built from your config, rendered by Shell.

The dropdown in the topbar is a `DocsUI::ThemeSwitcher`. It lists `DocsKit.configuration.themes` — which must match the daisyUI `@plugin` block in your Tailwind entry. `Shell` renders it for you.

```ruby
DocsKit.configure do |c|
  c.themes = %w[dark light synthwave dracula night]
end
```

| Arg | Type | Default | Description |
| --- | --- | --- | --- |
| `(none)` | — | — | No args — reads DocsKit.configuration.themes. |