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.
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.
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.
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.
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:
prose do
p { "Prose gives hand-authored text a consistent reading rhythm." }
ul { li { "lists," }; li { "inline code," }; li { "links." } }
endDocsUI::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.
class User < ApplicationRecord
has_many :posts
endThe call that produced the block above:
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.
Anthropic::Client.new.messages.create(model: "claude-opus-4-8", messages: msgs)anthropic.Anthropic().messages.create(model="claude-opus-4-8", messages=msgs)The call that produced the tabs above:
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:
DocsUI::Table(
[ "Cell", "Renders as" ],
[
[ "brand", "plain, escaped text" ],
[ [ :code, "%w[dark light]" ], "inline code" ],
[ [ :md, "a **bold** note" ], "inline markdown" ]
]
)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:
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" }
])
endGET → 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 -X POST 'https://api.example.com/v1/payment_links' \
-H "Content-Type: application/json" \
-d '{
"amount": 4900,
"currency": "usd",
"description": "Pro plan"
}'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();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)
endimport 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:
{
"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:
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 }
)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.
The calls that produced the boxes above:
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:
DocsUI::Icon("rocket", class: "size-6")
DocsUI::Icon("book-open", class: "size-6")
DocsUI::Icon("paintbrush", class: "size-6")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.
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. |
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.
DocsKit.configure do |c|
c.themes = %w[dark light synthwave dracula night]
end| Arg | Type | Default | Description |
|---|---|---|---|
(none) | — | — | No args — reads DocsKit.configuration.themes. |