Sidebar

Displays the mobile sidebar.

Command palette

Search for a command to run…

Docs
Installation
Theming
Typography
Testing
I18n
Stimulus
Form Builder
Pagination
Deferred Regions
Optimistic Forms
Recipes
Page Agent
Agent Skills
Editors
API Reference
Data Table
Caching
Stable IDs
Rails Engines
Accessibility
MCP Server
WebMCP
AG-UI Relay
A2UI Surfaces
Libraries
Core
UI
Charts
Agent
Simple Form
Extract
Icons
Lucide
Components
Accordion
Alert
Alert Dialog
Aspect Ratio
Attachment
Autocomplete
Avatar
Badge
Breadcrumb
Bubble
Button
Button Group
Calendar
Card
Carousel
Checkbox
Clipboard Text
Code Block
Collapsible
Combobox
Command
Command Dialog
Context Menu
Data Table
Date Field
Date Picker
Date Time Field
Deferred
Dialog
Drawer
Dropdown Menu
Empty
Field
Field Group
Field Separator
Fieldset
File Input
Hover Card
Icon
Input
Input Group
Input Otp
Item
Kbd
Label
Link
Marker
Menubar
Message
Message Scroller
Metadata List
Meter
Native Select
Navigation Menu
Number Field
Pagination
Popover
Progress
Questionnaire
Radio Group
Resizable
Scroll Area
Search Field
Select
Sensitive Input
Separator
Sheet
Sidebar
Skeleton
Slider
Spinner
Stat
Switch
Table
Tabs
Tag Group
Textarea
Time Field
Timeline
Toast
Toast Trigger
Toaster
Toggle
Toggle Group
Toolbar
Tooltip
Tree
Typeset
Charts
Adapter Chart
Area Chart
Bar Chart
Composed Chart
Line Chart
Pie Chart
Radar Chart
Radial Bar Chart
Scatter Chart
Demos
Chat Replay
AG-UI Relay
A2UI Surface
Interactive Filter
Live Streaming
Synced Tooltips
Brush & Zoom
Reference
Installation — Install
Installation — Upgrade an existing app
Installation — The three ownership tiers
Installation — Copy-ins: poetry:diff
Installation — Scaffold templates
Theming — The nine themes
Theming — Installing a theme
Theming — Owning the design outright
Theming — How this site renders all nine
Theming — The font pairing
Theming — Overriding the theme
Theming — DESIGN.md interop
Theming — What stays out of the theme
Theming — Consumer utilities
Theming — Color scheme (dark mode)
Theming — Portals and scoped themes
Typography — Default
Typography — Headings
Typography — Inline code
Typography — Large
Typography — Lead
Typography — Muted
Typography — Small
Testing — The doctrine
Testing — Lint first: Poetry check
Testing — Tier 1: wiring tests
Testing — Tier 2: behavior
Testing — Tier 3: the browser pass
Testing — The system-test helpers
Testing — What to assert, in one line
I18n — The shipped catalogue
I18n — Overriding keys and adding locales
I18n — Visible copy is yours
I18n — Forms localize through the model
I18n — Dates
Stimulus — The model
Stimulus — 1. Attach your controller to a component
Stimulus — 2. React to what a component does
Stimulus — 3. Drive a component from your markup
Stimulus — 4. Declare it in Ruby
Stimulus — 5. Change what a Poetry controller does
Stimulus — Checked at every rung
Form Builder — Coming from Simple Form?
Form Builder — Default
Form Builder — Errors
Form Builder — Inference
Form Builder — Layout
Pagination — Installation
Pagination — Wire your controller
Pagination — kaminari
Pagination — pagy
Pagination — will_paginate
Pagination — The window, labels, and one page
Pagination — Kaminari
Pagination — Pagy
Pagination — Will paginate
Deferred Regions — Error retry
Deferred Regions — In tabs
Deferred Regions — Lazy
Optimistic Forms — How it works
Optimistic Forms — Setup
Optimistic Forms — Usage
Optimistic Forms — The server contract
Optimistic Forms — The submitted value
Optimistic Forms — When the prediction can be wrong
Optimistic Forms — Authoring streams directly
Optimistic Forms — When to reach for it
Optimistic Forms — Default
Optimistic Forms — Reconciliation
Recipes — How it works
Recipes — In-page agent embed
Recipes — poetry scaffold templates
Recipes — Data index screen
Recipes — Settings screen
Recipes — poetry skill bundle
Recipes — poetry-component skill bundle
Recipes — poetry-design skill bundle
Page Agent — What this is
Page Agent — Or hand the agent tools
Page Agent — Activate
Page Agent — Task list
Page Agent — Sample implementation
Page Agent — Provenance & limits
Agent Skills — How it works
Agent Skills — poetry
Agent Skills — In a Rails app with Poetry
Editors — One generator
Editors — The MCP server
Editors — The check hook
Editors — Snippets
Editors — HTML+ERB tooling
Editors — Design tools
Data Table — The controller
Data Table — The view
Data Table — Sticky headers
Data Table — Row selection
Data Table — Scoped updates (optional)
Data Table — Filter as you type (optional)
Data Table — Accessibility
Data Table — Row mutations belong to another tier
Caching — The digest blind spot
Caching — The recipe: version-keyed cache keys
Caching — Rules for markup that can be cached at all
Caching — Components you copied in
Caching — Ids inside cached fragments
Caching — HTTP caching, ETags, and CSRF
Stable IDs — The identity ladder
Stable IDs — When you must pass identity
Stable IDs — Keeping primary keys out of the DOM
Stable IDs — What morph identity does and does not preserve
Stable IDs — The sequence mode (opt-in, experimental)
Stable IDs — ETags and CSRF: what ids do and don't buy
Rails Engines — Why this works
Rails Engines — The engine recipe
Rails Engines — Page-level utilities: the one wiring step
Rails Engines — Retheming the whole application
Rails Engines — Customizing a component from an engine
Rails Engines — Keep one theme contract
Accessibility — What Poetry guarantees
Accessibility — What automation cannot see
Accessibility — Set up a pass
Accessibility — The universal checklist
Accessibility — Pattern checklists
Accessibility — Form controls
Accessibility — Overlays
Accessibility — Menus and command palettes
Accessibility — Disclosure and navigation
Accessibility — Data, feedback, and streaming
Accessibility — Automated checks in your app
Accessibility — Recording findings
MCP Server — The server
MCP Server — The loop it closes
MCP Server — The ten tools
MCP Server — Over HTTP
MCP Server — Routing a brief
MCP Server — The check verdict
MCP Server — Skills and guidance at runtime
MCP Server — One source, every surface
WebMCP — What it is
WebMCP — Live: the tools on this page
WebMCP — Search the docs: a declarative tool
WebMCP — Two paths
WebMCP — Designing tools for your app
WebMCP — Declaring tools in your own components
WebMCP — Setup
WebMCP — Safety by construction
AG-UI Relay — What it is
AG-UI Relay — Run an agent
AG-UI Relay — Render the transcript
AG-UI Relay — Frontend tools
AG-UI Relay — Interrupts
AG-UI Relay — A2UI surfaces in the stream
AG-UI Relay — Deterministic development
AG-UI Relay — Setup
A2UI Surfaces — Two catalogs
A2UI Surfaces — Fold the envelope
A2UI Surfaces — Render a surface
A2UI Surfaces — Stream updates
A2UI Surfaces — Actions
A2UI Surfaces — Checks
A2UI Surfaces — Functions
A2UI Surfaces — State across updates
A2UI Surfaces — Transports
A2UI Surfaces — Setup
Accordion — Installation
Accordion — Default
Accordion — Borders
Accordion — Card
Accordion — Disabled
Accordion — Multiple
Accordion — Non collapsible
Accordion — Wiring
Alert — Installation
Alert — Default
Alert — Action
Alert — Custom colors
Alert — Destructive
Alert — Dismissible
Alert — With action
Alert — With icon
Alert Dialog — Installation
Alert Dialog — Default
Alert Dialog — Small
Alert Dialog — Small with media
Alert Dialog — With media
Alert Dialog — Wiring
Aspect Ratio — Installation
Aspect Ratio — Default
Aspect Ratio — Portrait
Aspect Ratio — Square
Aspect Ratio — With image
Attachment — Installation
Attachment — Default
Attachment — Group
Attachment — Idle dropzone
Attachment — Sizes
Attachment — States
Attachment — Vertical with actions
Attachment — With trigger
Autocomplete — Installation
Autocomplete — Default
Autocomplete — Value override
Autocomplete — Wiring
Avatar — Installation
Avatar — Default
Avatar — Badge
Avatar — Badge with icon
Avatar — Dropdown
Avatar — Group
Avatar — Group with icon
Avatar — Sizes
Avatar — With image
Badge — Installation
Badge — Default
Badge — Custom colors
Badge — Link
Badge — Status
Badge — Variants
Badge — With icon
Badge — With spinner
Breadcrumb — Installation
Breadcrumb — Default
Breadcrumb — Collapsed
Breadcrumb — Custom separator
Breadcrumb — Dropdown
Breadcrumb — Link component
Bubble — Installation
Bubble — Default
Bubble — Collapsible
Bubble — Group
Bubble — Links and buttons
Bubble — Popover
Bubble — Quick replies
Bubble — Reactions
Bubble — Tooltip
Bubble — Variants
Button — Installation
Button — Default
Button — As link
Button — Button group
Button — Icon buttons
Button — Link
Button — Loading
Button — Rounded
Button — Sizes
Button — With icon
Button Group — Installation
Button Group — Default
Button Group — Input action
Button Group — Input group
Button Group — Nested
Button Group — Popover
Button Group — Separator
Button Group — Sizes
Button Group — Split
Button Group — Text prefix
Button Group — Vertical
Button Group — With dropdown
Button Group — With select
Calendar — Installation
Calendar — Default
Calendar — Bounded
Calendar — Custom cell size
Calendar — Date and time
Calendar — Month and year selector
Calendar — Range
Calendar — Week numbers
Calendar — Wiring
Card — Installation
Card — Default
Card — Login form
Card — Terms of service
Card — With action and footer
Card — With media
Carousel — Installation
Carousel — Default
Carousel — Sizes
Carousel — Spacing
Carousel — Vertical
Carousel — Wiring
Checkbox — Installation
Checkbox — Default
Checkbox — Card
Checkbox — Disabled
Checkbox — Group
Checkbox — In a field
Checkbox — In a table
Checkbox — States
Checkbox — With description
Checkbox — Wiring
Clipboard Text — Installation
Clipboard Text — Default
Clipboard Text — Truncated copy
Clipboard Text — Wiring
Code Block — Installation
Code Block — Default
Code Block — Numbered and highlighted
Code Block — Wiring
Collapsible — Installation
Collapsible — Default
Collapsible — File tree
Collapsible — Initially open
Collapsible — Settings panel
Collapsible — With icon
Collapsible — Wiring
Combobox — Installation
Combobox — Default
Combobox — Auto highlight
Combobox — Clear button
Combobox — Custom items
Combobox — Disabled
Combobox — Disabled options
Combobox — Grouped
Combobox — Invalid
Combobox — Multiple
Combobox — Valued
Combobox — Wide
Combobox — With icon
Combobox — Agent tools
Combobox — Wiring
Command — Installation
Command — Default
Command — Close button
Command — Dialog
Command — Initial value
Command — Scrollable
Command — With icons
Command — Zero match
Command — Wiring
Command Dialog — Installation
Command Dialog — Default
Command Dialog — Empty state
Command Dialog — Hotkey
Command Dialog — Scrollable
Command Dialog — With icons
Command Dialog — Wiring
Context Menu — Installation
Context Menu — Default
Context Menu — Checkboxes
Context Menu — Destructive
Context Menu — Disabled surface
Context Menu — Focusable surface
Context Menu — Groups
Context Menu — Radio
Context Menu — Sides
Context Menu — Submenu
Context Menu — With icon
Context Menu — Wiring
Data Table — Installation
Data Table — Default
Data Table — Cell formatting
Data Table — Empty
Data Table — Filtered and paginated
Data Table — Row actions
Data Table — Selectable
Data Table — Wiring
Date Field — Installation
Date Field — Default
Date Field — With range
Date Field — Year first
Date Field — Wiring
Date Picker — Installation
Date Picker — Default
Date Picker — Date of birth
Date Picker — Input
Date Picker — Preselected
Date Picker — Range
Date Picker — With time
Date Picker — Wiring
Date Time Field — Installation
Date Time Field — Default
Date Time Field — With seconds
Date Time Field — Wiring
Deferred — Installation
Deferred — Default
Deferred — Wiring
Dialog — Installation
Dialog — Default
Dialog — Confirmation
Dialog — No close button
Dialog — Scrollable content
Dialog — Sticky footer
Dialog — Agent tools
Dialog — Wiring
Drawer — Installation
Drawer — Default
Drawer — Edit form
Drawer — Nested
Drawer — Non modal
Drawer — Position
Drawer — Responsive
Drawer — Snap points
Drawer — Swipe handle
Drawer — Agent tools
Drawer — Wiring
Dropdown Menu — Installation
Dropdown Menu — Default
Dropdown Menu — Avatar
Dropdown Menu — Checkbox with icons
Dropdown Menu — Checkboxes
Dropdown Menu — Complex
Dropdown Menu — Inset and disabled
Dropdown Menu — Radio group
Dropdown Menu — Radio with icons
Dropdown Menu — With icons
Dropdown Menu — Wiring
Empty — Installation
Empty — Default
Empty — Background
Empty — Outline
Empty — With avatar
Empty — With avatar group
Empty — With icon and actions
Empty — With input
Field — Installation
Field — Default
Field — Checkbox
Field — Choice card
Field — Fieldset
Field — Group
Field — Horizontal switch
Field — Responsive
Field — With error
Field — With hint
Field — With radio group
Field — With select
Field — With slider
Field — With textarea
Field Group — Installation
Field Group — Default
Field Group — Choices
Field Separator — Installation
Field Separator — Default
Field Separator — With caption
Fieldset — Installation
Fieldset — Default
Fieldset — Label legend
File Input — Installation
File Input — Default
File Input — Dropzone
File Input — Wiring
Hover Card — Installation
Hover Card — Default
Hover Card — Custom delays
Hover Card — Sides
Hover Card — Wiring
Icon — Installation
Icon — Default
Icon — Animated
Icon — Colors
Icon — Labeled
Icon — Sizes
Icon — Stroke width
Icon — With tooltip
Input — Installation
Input — Default
Input — Button group
Input — Disabled
Input — Field
Input — File
Input — Form
Input — Grid
Input — Inline
Input — Invalid
Input — Required
Input — With badge
Input — With value
Input Group — Installation
Input Group — Default
Input Group — Align
Input Group — Button
Input Group — Custom
Input Group — Dropdown
Input Group — Icon
Input Group — Kbd
Input Group — Spinner
Input Group — Text
Input Group — Textarea block
Input Group — Textarea header
Input Otp — Installation
Input Otp — Default
Input Otp — Alphanumeric
Input Otp — Form
Input Otp — Four digits
Input Otp — Groupings
Input Otp — In a field
Input Otp — States
Input Otp — Wiring
Item — Installation
Item — Default
Item — Avatar
Item — Dropdown
Item — Group
Item — Image
Item — Link
Item — Outline with icon and actions
Item — Sizes
Item — Variants
Item — With header and footer
Kbd — Installation
Kbd — Default
Kbd — Chord
Kbd — Command
Kbd — Group
Kbd — In button
Kbd — In tooltip
Kbd — Search hint
Label — Installation
Label — Default
Label — Disabled
Label — Form
Label — With checkbox
Label — With description
Link — Installation
Link — Default
Link — Current
Link — External
Link — Underline
Marker — Installation
Marker — Default
Marker — Border
Marker — Links and buttons
Marker — Separator
Marker — Shimmer
Marker — Status
Marker — Variants
Marker — With icon
Menubar — Installation
Menubar — Default
Menubar — Checkbox
Menubar — Disabled menu
Menubar — Icons
Menubar — Radio
Menubar — Submenu
Menubar — Wiring
Message — Installation
Message — Default
Message — Actions
Message — Align end
Message — Attachment
Message — Conversation
Message — Grouped
Message — Header and footer
Message — Markdown
Message Scroller — Installation
Message Scroller — Default
Message Scroller — Anchored turns
Message Scroller — Commands
Message Scroller — Group chat
Message Scroller — Open at start
Message Scroller — Scroll state
Message Scroller — Streaming
Message Scroller — Without jump button
Message Scroller — Wiring
Metadata List — Installation
Metadata List — Default
Metadata List — Two columns
Meter — Installation
Meter — Default
Meter — With value text
Native Select — Installation
Native Select — Default
Native Select — Grouped
Native Select — Invalid
Native Select — Small disabled
Navigation Menu — Installation
Navigation Menu — Default
Navigation Menu — Viewport
Navigation Menu — With descriptions
Navigation Menu — With icon
Navigation Menu — Wiring
Number Field — Installation
Number Field — Default
Number Field — Currency
Number Field — Decimal steps
Number Field — Wiring
Pagination — Installation
Pagination — Adapters
Pagination — Default
Pagination — First page
Pagination — Icons only
Pagination — Long range
Pagination — Short range
Pagination — Simple
Popover — Installation
Popover — Default
Popover — Align
Popover — Basic
Popover — Modal
Popover — Open
Popover — Side top
Popover — With anchor
Popover — With form
Popover — Wiring
Progress — Installation
Progress — Default
Progress — Complete
Progress — Controlled
Progress — Custom max without value
Progress — Label
Questionnaire — Installation
Questionnaire — Default
Questionnaire — Animated
Questionnaire — Card
Questionnaire — Custom progress
Questionnaire — Dialog
Questionnaire — Shortcuts
Questionnaire — Skip
Questionnaire — Validation
Questionnaire — Wiring
Radio Group — Installation
Radio Group — Default
Radio Group — Cards
Radio Group — Disabled
Radio Group — Fieldset
Radio Group — Horizontal layout
Radio Group — In a field
Radio Group — Invalid
Radio Group — With description
Radio Group — Wiring
Resizable — Installation
Resizable — Default
Resizable — Handle
Resizable — Nested
Resizable — Three panels
Resizable — Vertical
Resizable — Wiring
Scroll Area — Installation
Scroll Area — Default
Scroll Area — Horizontal
Search Field — Installation
Search Field — Default
Search Field — With value
Search Field — Wiring
Select — Installation
Select — Default
Select — Align item with trigger
Select — Disabled
Select — Groups
Select — Invalid
Select — Scrollable
Select — Small
Select — Valued
Select — Wiring
Sensitive Input — Installation
Sensitive Input — Default
Sensitive Input — With copy
Sensitive Input — With copy and tooltip
Sensitive Input — Wiring
Separator — Installation
Separator — Default
Separator — List
Separator — Menu
Separator — Vertical semantic
Sheet — Installation
Sheet — Default
Sheet — Form
Sheet — No close button
Sheet — Sides
Sheet — Agent tools
Sheet — Wiring
Sidebar — Installation
Sidebar — Default
Sidebar — Loading
Sidebar — Offcanvas collapsed
Sidebar — Submenu
Sidebar — User menu
Sidebar — Workspace switcher
Sidebar — Wiring
Skeleton — Installation
Skeleton — Default
Skeleton — Avatar
Skeleton — Block
Skeleton — Card
Skeleton — Composed
Skeleton — Form
Skeleton — Table
Skeleton — Text
Slider — Installation
Slider — Default
Slider — Controlled
Slider — Disabled
Slider — Multiple thumbs
Slider — Range
Slider — Range with gap
Slider — Vertical
Slider — Wiring
Spinner — Installation
Spinner — Default
Spinner — Button
Spinner — Custom
Spinner — Empty state
Spinner — In badge
Spinner — In input group
Spinner — Size
Spinner — With context label
Stat — Installation
Stat — Default
Stat — With description
Switch — Installation
Switch — Default
Switch — Card
Switch — Description
Switch — In a field
Switch — Invalid
Switch — Sizes
Switch — States
Switch — Wiring
Table — Installation
Table — Default
Table — Actions
Table — Footer
Table — Minimal
Table — Sticky
Tabs — Installation
Tabs — Default
Tabs — Disabled
Tabs — Line
Tabs — Vertical
Tabs — With icon
Tabs — Agent tools
Tabs — Wiring
Tag Group — Installation
Tag Group — Default
Tag Group — As form value
Tag Group — Wiring
Textarea — Installation
Textarea — Default
Textarea — Auto grown
Textarea — Button
Textarea — Disabled
Textarea — Field
Textarea — Invalid
Textarea — With hint
Textarea — With label
Time Field — Installation
Time Field — Default
Time Field — Twenty four hour
Time Field — Wiring
Timeline — Installation
Timeline — Default
Timeline — Horizontal
Timeline — With icons
Toast — Installation
Toast — Default
Toast — Promise
Toast — Variants
Toast — With action
Toast — Wiring
Toast Trigger — Installation
Toast Trigger — Default
Toast Trigger — Wiring
Toaster — Installation
Toaster — Default
Toaster — Positions
Toaster — Wiring
Toggle — Installation
Toggle — Default
Toggle — Outline
Toggle — Sizes
Toggle — States
Toggle — With text
Toggle — Wiring
Toggle Group — Installation
Toggle Group — Default
Toggle Group — Custom
Toggle Group — Disabled
Toggle Group — Outline
Toggle Group — Single
Toggle Group — Sizes
Toggle Group — Spaced
Toggle Group — Vertical
Toggle Group — Wiring
Toolbar — Installation
Toolbar — Default
Tooltip — Installation
Tooltip — Default
Tooltip — Disabled trigger
Tooltip — Keyboard
Tooltip — Open
Tooltip — Provider row
Tooltip — Side bottom
Tooltip — Sides
Tooltip — With label
Tooltip — Wiring
Tree — Installation
Tree — Default
Tree — With links
Tree — Wiring
Typeset — Installation
Typeset — Default
Typeset — Chat preset
Typeset — Docs preset
Typeset — Not typeset
Typeset — Overrides
Typeset — Responsive table
Adapter Chart — Installation
Adapter Chart — Bring your own engine
Adapter Chart — Default
Adapter Chart — Wiring
Area Chart — Installation
Area Chart — Default
Area Chart — Legend toggle
Area Chart — Wiring
Bar Chart — Installation
Bar Chart — Default
Bar Chart — Active
Bar Chart — Horizontal
Bar Chart — Label
Bar Chart — Mixed
Bar Chart — Multiple
Bar Chart — Negative
Bar Chart — Stacked
Bar Chart — Wiring
Composed Chart — Installation
Composed Chart — Default
Composed Chart — Full mix
Composed Chart — Wiring
Line Chart — Installation
Line Chart — Default
Line Chart — Dots
Line Chart — Dots colors
Line Chart — Label
Line Chart — Linear
Line Chart — Multiple
Line Chart — References and errors
Line Chart — Step
Line Chart — Wiring
Pie Chart — Installation
Pie Chart — Default
Pie Chart — Donut
Pie Chart — Donut active
Pie Chart — Donut text
Pie Chart — Label list
Pie Chart — Legend
Pie Chart — Stacked
Pie Chart — Wiring
Radar Chart — Installation
Radar Chart — Default
Radar Chart — Dots
Radar Chart — Grid circle
Radar Chart — Grid fill
Radar Chart — Grid none
Radar Chart — Legend
Radar Chart — Lines only
Radar Chart — Multiple
Radar Chart — Wiring
Radial Bar Chart — Installation
Radial Bar Chart — Default
Radial Bar Chart — Grid
Radial Bar Chart — Label
Radial Bar Chart — Shape
Radial Bar Chart — Stacked
Radial Bar Chart — Text
Radial Bar Chart — Wiring
Scatter Chart — Installation
Scatter Chart — Default
Scatter Chart — Bubbles
Scatter Chart — Multiple
Scatter Chart — Reference zones
Scatter Chart — Wiring
Action bar — Installation
Action bar — Default
Action bar — Styling
App shell — Installation
App shell — Default
App shell — Styling
Data index — Installation
Data index — Default
Data index — Styling
Destructive panel — Installation
Destructive panel — Default
Destructive panel — Revoke deploy token
Destructive panel — Styling
Page header — Installation
Page header — Default
Page header — Styling
Section card — Installation
Section card — Default
Section card — Styling
Stepper — Installation
Stepper — Default
Stepper — Styling
Top nav — Installation
Top nav — Default
Top nav — Styling
Chat Replay — Default
AG-UI Relay — Default
A2UI Surface — Default
Interactive Filter — Default
Live Streaming — Default
Synced Tooltips — Default
Brush & Zoom — Default
poetry-core — ActiveModel
poetry-core — ActiveModel::Type
poetry-core — ActiveModel::Type::List
poetry-core — ActiveModel::Type::Symbol
poetry-core — Poetry
@poetry/controllers — IncompleteDate
poetry-ui — Poetry
poetry-charts — Poetry
poetry-agent — Poetry
poetry-simple_form — Poetry
poetry-extract — Poetry
Core — What it is
Core — Install
Core — Build your own component library
Core — The component class
Core — The style dictionary
Core — Behavior from the primitives
Core — The registry
Core — Two styling paths: Tailwind or BEM
Core — Design tokens
Core — The check
Core — Box and Wrapper
Core — Box, the polymorphic element
Core — Wrapper, conditional outer HTML
UI — What it is
UI — Install and first components
UI — The form builder handoff
UI — Nine themes ship in the gem
UI — Own the code when you want to
UI — Blocks and recipes
UI — The agent surface
UI — Testing helpers
Charts — What it is
Charts — Install
Charts — Basic usage
Charts — Works wherever HTML renders
Charts — Live updates
Charts — Bring your own engine
Charts — The theme palette
Agent — What it is
Agent — Install
Agent — The MCP server
Agent — The WebMCP runtime
Agent — The AG-UI relay
Agent — The A2UI catalog and renderer
Agent — What's in the gem
Simple Form — How it works
Simple Form — Install
Simple Form — What restyles
Simple Form — The end state
Extract — Install and run
Extract — The pipeline
Extract — What it emits
Extract — From extract to theme
Lucide — Install
Lucide — Names, misses, and the fallback
Lucide — The pin

poetry-charts API

The charts gem's Ruby surface: the chart helpers, the shared chart chassis, and the geometry layer (scales, curves, ticks, paths).

Generated from the gem's source documentation. Component classes are documented on their own gallery pages — this page carries the framework surface.

Poetry::Charts::ComponentsHelper

module · app/helpers/poetry/charts/components_helper.rb

The poetry_chart_* view helpers - the agent-facing chart surface, from the chart-root dispatcher down to the per-family helpers.

Constant Description
CHART_TYPES Chart type symbols mapped to their component class names - the dispatch table behind poetry_chart.

#poetry_area_chart(**, &)

Renders an area chart - filled trends over an ordered axis, optionally stacked.

<%= poetry_area_chart(data: data, config: config) do |c| %>
  <% c.with_area data_key: :desktop %>
<% end %>

#poetry_bar_chart(**, &)

Renders a bar chart - grouped or stacked rectangles per category.

<%= poetry_bar_chart(data: data, config: config) do |c| %>
  <% c.with_bar data_key: :desktop %>
<% end %>

#poetry_chart(type, engine: nil, **, &block)

Renders a chart of the given type through one dispatcher; the block receives the chart component for slot composition. Unknown types raise with the list of known ones.

  • type (Symbol, String) — one of the CHART_TYPES keys
  • engine (Symbol, nil) — when set, routes the call to the client-side adapter mount, which takes series:/axes: arguments (the closed spec) instead of slots
<%= poetry_chart(:area, data: data, config: config) do |c| %>
  <% c.with_area data_key: :desktop %>
<% end %>

#poetry_chart_container(**, &)

Renders the chart container - the sized, theme-scoped wrapper that emits var(--color-<key>) for every configured series and hosts the chart plus its tooltip and legend.

<%= poetry_chart_container(config: config, class: "h-64") do %>
  <%= poetry_area_chart(data: data, config: config) %>
<% end %>

#poetry_chart_legend_content(**, &)

Renders a standalone legend for the configured series - a swatch plus label per entry.

<%= poetry_chart_legend_content(config: config) %>

#poetry_chart_tooltip_content(**, &)

Renders the tooltip panel a chart's hover layer positions and fills - place it inside the container alongside the chart.

<%= poetry_chart_tooltip_content(indicator: :line) %>

#poetry_line_chart(**, &)

Renders a line chart - one stroked curve per series.

<%= poetry_line_chart(data: data, config: config) do |c| %>
  <% c.with_line data_key: :desktop %>
<% end %>

Poetry::Charts::Config

class · lib/poetry/charts/config.rb

The chart config contract: series key -> { label:, icon:, color: } or { label:, icon:, theme: { light:, dark: } }. The config is the SINGLE place series get their human labels and colors; the container's <style> emission, the tooltip chrome, and the legend chrome all resolve through it.

Values land inside a <style> element and inline style attributes, so every color (and key) is validated against a conservative character set at wrap time - a config can never smuggle CSS out of its block.

config = Poetry::Charts::Config.new(
  revenue: { label: "Revenue", color: "var(--chart-1)" }
)
config.label_for("revenue") # => "Revenue"
Constant Description
COLOR Any CSS color form (var(--chart-1), #hex, oklch(...), named...) minus everything that could terminate a declaration or escape the block.
KEY A series key becomes a --color-<key> custom property.
THEMES The theme map's allowed modes.

.wrap(source)

Accepts a Config (pass-through) or a Hash keyed by series name.

  • source (Config, Hash, nil)

#[](key)

The entry for a series key.

  • key (String, Symbol)

#color_entries

The entries that carry a color (flat or themed) - the set the container's <style> emission covers, in config order.

#entries

Returns the value of attribute entries.

#initialize(hash)

Builds and validates entries from a Hash keyed by series name; a CSS-unsafe key or color raises here, at wrap time.

  • hash (Hash)

Returns (Config) — a new instance of Config

#keys

Every series key, in config order.

#label_for(key, fallback = nil)

The label for a series key, falling back to the key itself - the tooltip/legend resolution rule.

  • key (String, Symbol) — the series key
  • fallback (String, nil) — preferred over the key when no label is set

#to_h

The config back as a plain Hash (label/color/theme per key), compacted.

Poetry::Charts::Spec

class · lib/poetry/charts/spec.rb

The chart-spec: the CLOSED, VERSIONED description every poetry chart compiles to. The server engine consumes it Ruby-side; swappable adapters consume the same spec JSON-side through the duck-typed protocol (render(el, spec) / update / destroy). No engine-specific key ever enters this schema - engine styling lives in the adapter, declared; a pass-through options bag would tie call sites to one engine and the spec could never close.

Keys ride the wire camelCased (dataKey, stackId) - one vocabulary on both sides of the seam.

Poetry::Charts::Spec.new(
  type: :bar, data: rows, series: [{ data_key: :revenue }]
).to_json
Constant Description
AXIS_KEYS The closed key set an axis entry may carry.
SERIES_KEYS The closed key set a series entry may carry.
TYPES The chart types the spec can describe.
VERSION The spec schema version stamped into every payload.

#axes

Returns the value of attribute axes.

#config

Returns the value of attribute config.

#data

Returns the value of attribute data.

#initialize(type:, data:, series:, axes: {}, config: nil)

  • type (Symbol, String) — one of TYPES
  • data (Array<Hash>) — the rows, one hash per data point
  • series (Array<Hash>) — series entries; data_key: is required
  • axes (Hash) — per-axis settings (SERIES_KEYS/AXIS_KEYS closed sets)
  • config (Config, Hash, nil) — label/color config, wrapped via Config.wrap

Returns (Spec) — a new instance of Spec

#series

Returns the value of attribute series.

#to_h

The wire form: string keys, camelCased entry keys, the version stamped in.

#to_json(...)

The wire form serialized - what the spec <script> embeds.

#type

Returns the value of attribute type.

Poetry

module · lib/poetry/charts.rb

The poetry component family's shared root namespace.

Poetry::Charts

module · lib/poetry/charts.rb

poetry's chart tier: charts as server-rendered SVG. Ruby runs the whole geometry pipeline - data -> domains -> scales -> ticks -> points -> paths, with decimal-exact nice ticks - and the finished chart ships in the initial HTML: no-JS/print/email valid, themed by CSS variables (--chart-1..5 + per-chart --color-<key>), dark mode with zero re-render. Stimulus chrome adds tooltip/legend/active interactivity by reading SERVER-EMBEDDED coordinates - no chart math in the browser.

Engines stay swappable (three doors): the container contract is engine-agnostic; every chart also compiles to a closed, VERSIONED chart-spec consumed by duck-typed adapters (render/update/destroy - a canvas adapter ships as the reference); client-rendered chart libraries remain reachable through a Stimulus-mounted island.

poetry_chart :bar, data: rows, series: [{ data_key: :revenue }]
Constant Description
CURVES The curve interpolation whitelist every series slot validates against.
POSITIONAL_PARAM_KINDS Parameter kinds that count toward a helper's positional arity.
VERSION The gem version.

.display_value(value)

The tooltip display string shared by every chart family (matches TooltipContent's Row: delimited numerics from RAW values so integers stay integers, verbatim strings, nil for missing).

.registry

The registry builder this gem commits from (the poetry-ui shared- builder rule: rake registry:generate/verify and the sync test share ONE construction). helper_args carries each poetry_* helper's max positional arity from its real signature - poetry_chart(type, ...) legitimately takes one, which is exactly why arity is an emitted per-helper fact and never a convention.

.registry_descriptions

The editorial per-chart descriptions merged into the registry (component_path => one-liner, from config/component_descriptions.yml). Absent file -> nil, so the registry stays lint-identical without it.

.registry_items

The installable-item projection, boot-free from the COMMITTED registry - the docs site aggregates this with poetry-ui's for /r/*.json.

.root

Gem root (the directory containing lib/, app/, config/).

Poetry::Charts::AdapterChart

module · app/components/poetry/charts/adapter_chart/component.rb

The bring-your-own-engine chart mount.

Poetry::Charts::AreaChart

module · app/components/poetry/charts/area_chart/component.rb

The area chart family.

Poetry::Charts::AxisConfig

class < Data · lib/poetry/charts.rb

The axis capture shape the cartesian family slots accumulate into (scatter carries its own - both axes numeric, different fields).

#data_key

Returns the value of attribute data_key

Returns (Object) — the current value of data_key

#tick_count

Returns the value of attribute tick_count

Returns (Object) — the current value of tick_count

#tick_formatter

Returns the value of attribute tick_formatter

Returns (Object) — the current value of tick_formatter

#tick_margin

Returns the value of attribute tick_margin

Returns (Object) — the current value of tick_margin

Poetry::Charts::BarChart

module · app/components/poetry/charts/bar_chart/component.rb

The bar chart family.

Poetry::Charts::Cartesian

class · lib/poetry/charts/cartesian.rb

The server-side cartesian layout pipeline: data + series -> plot rectangle, scales, ticks, and per-series pixel points - computed top-down in one pass, everything a renderer needs before writing a path.

Layout conventions: default margin 5 on every side, x-axis strip height 30 at the bottom when shown, category x positions from a zero-padding point scale (first/last categories AT the plot edges - add left/right margin 12 when edge labels need room), numeric y domain [0, auto] niced decimal-exactly (implicit tick count 5), stacking per stack id with :none/:expand offsets.

plot = Poetry::Charts::Cartesian.new(
  data: rows, series: series, width: 600, height: 300
)
plot.points(series.first) # => [{x:, y0:, y1:, value:}, ...]
Constant Description
DEFAULT_MARGIN The default plot margin - a slim, even inset on all sides.
LAYOUTS The chart orientations: :vertical columns, or :horizontal bars.
OFFSETS The stack baseline modes: raw values, or 100%-normalized.
X_AXIS_HEIGHT The bottom strip reserved for the category axis when shown.
Y_AXIS_WIDTH The reserved left strip - where a visible value axis's labels (or the horizontal layout's category labels) live.

#band_width

One category band's width (0 on a point scale).

#baseline

The area baseline: the y pixel of 0, clamped into the domain so an all-positive domain pins the baseline at its bottom edge.

#categories

The category values: the x_key column, else bare row indexes.

#category_range

Point for line/area (categories AT the edges); band for bars (a zero-padding band - the bar gaps come from the gap math inside the band, not scale padding). Horizontal layout runs the category scale down the Y side.

#coordinates

Compact per-series pixel coordinates the tooltip controller reads - no chart math in the browser.

#height

Returns the value of attribute height.

#horizontal?

Whether categories run down the Y side (bars growing rightward).

#initialize(data:, series:, width:, height:, x_key: nil, margin: {}, category_axis: true, value_axis: false, y_tick_count: 5, offset: :none, x_scale_type: :point, layout: :vertical)

  • data (Array<Hash>) — the rows, one hash per category
  • series (Array<#key>) — series entries (key + optional stack id)
  • width (Numeric) — outer SVG width in pixels
  • height (Numeric) — outer SVG height in pixels
  • x_key (String, Symbol, nil) — the category key; nil indexes rows
  • margin (Hash) — per-side overrides merged over DEFAULT_MARGIN
  • category_axis (Boolean) — reserve the category-axis strip
  • value_axis (Boolean) — a visible value axis reserves the left strip
  • y_tick_count (Integer) — requested tick count for the niced domain
  • offset (Symbol) — :none, or :expand for 100%-stacked
  • x_scale_type (Symbol) — :point (line/area) or :band (bars)
  • layout (Symbol) — :vertical (values up y) or :horizontal

Returns (Cartesian) — a new instance of Cartesian

#layout

Returns the value of attribute layout.

#margin

Returns the value of attribute margin.

#offset

Returns the value of attribute offset.

#plot_bottom

The plot rect's bottom edge, inset for the category axis strip.

#plot_left

The plot rect's left edge, inset for a reserved left strip.

#plot_right

The plot rect's right edge.

#plot_top

The plot rect's top edge.

#points(entry)

[{x:, y0:, y1:, value:}] for one series entry - stacked entries ride their stack group's offsets; independent entries base on the baseline. NaN values (missing data) flow through as NaN, which the generators' defined-gap machinery turns into path gaps.

  • entry (#key) — a series entry (key + optional stack id)

Returns (Array<Hash>) — one orientation-aware point hash per category

#width

Returns the value of attribute width.

#x_centers

The per-category CENTER - where ticks, vertical grid lines, and the tooltip's hit columns sit (band centers; point positions verbatim).

#x_positions

Each category's scale position (a band's leading edge).

#x_scale

The category scale over the category range (point or band).

#y_domain

The niced value domain - the first and last tick.

#y_scale

The value scale: y in the vertical layout (inverted - SVG y grows down), x in the horizontal one.

#y_tick_count

Returns the value of attribute y_tick_count.

#y_ticks

The niced value ticks ([0, 1] when 100%-stacked).

Poetry::Charts::CartesianFamily

module · app/components/poetry/charts/cartesian_family.rb

The shared cartesian slot grammar (area/line/bar/composed): the axis/grid/legend/tooltip slots plus the readers that force their lazy evaluation. Lambda slots accumulate config into ivars and return nil (the breadcrumb pattern - slot wrappers do not delegate to lambda return values); readers force evaluation via the slot predicate (slots evaluate lazily).

Slots register at the include site because declaration order IS the registry's slot order - include this AFTER the family's mark slots. The value axis is a class-method hook: a family whose Y side diverges (bar - the horizontal orientation moves the category axis there) defines its own value_axis_slot before including.

class StepChart::Component < Poetry::Core::Component
  include Poetry::Charts::ChartFamily

  # Mark slots first - declaration order is the registry's slot order.
  renders_many :steps, lambda { |data_key:| ... }

  include Poetry::Charts::CartesianFamily
end

.included(base)

Poetry::Charts::CartesianFamily::ClassMethods

module · app/components/poetry/charts/cartesian_family.rb

The class-level hooks the include site drives.

#value_axis_slot

Declares the default numeric Y axis slot (tick count 3, no data key). Families whose value axis diverges override this before including the concern.

Poetry::Charts::ChartFamily

module · app/components/poetry/charts/chart_family.rb

The identity chassis every chart family shares: the data-chart id scope, the wrapped config, the accessible SVG name, and the SVG number formatter. Families supply svg_label_prefix (the accessible name's chart-type lead-in); a family whose default name reads from a different surface (scatter: series keys through the config) overrides svg_label itself.

class Sparkline::Component < Poetry::Core::Component
  include Poetry::Charts::ChartFamily

  option :config, ActiveModel::Type::Value.new, required: true
  option :id, :string
  option :label, :string

  # The accessible name's chart-type lead-in.
  def svg_label_prefix = "Sparkline"
end

#chart_config

The config: option wrapped as a {Poetry::Charts::Config} - series entries with labels and colors.

#chart_id

The data-chart scope: explicit id when given (stable for tests / multiple charts), else unique per render.

#svg_label

The accessible name for the role=img SVG: explicit label: or a sensible default from the configured series.

Poetry::Charts::ComposedChart

module · app/components/poetry/charts/composed_chart/component.rb

The composed chart family.

Poetry::Charts::Config::Entry

class < Data · lib/poetry/charts/config.rb

One validated config entry: a series key with its label, icon, and flat or themed color.

#color

Returns the value of attribute color

Returns (Object) — the current value of color

#color_for(theme_name)

The per-theme color: the flat color, or the theme map's value.

#colored?

Whether the entry carries any color (flat or themed).

#icon

Returns the value of attribute icon

Returns (Object) — the current value of icon

#key

Returns the value of attribute key

Returns (Object) — the current value of key

#label

Returns the value of attribute label

Returns (Object) — the current value of label

#theme

Returns the value of attribute theme

Returns (Object) — the current value of theme

Poetry::Charts::Container

module · app/components/poetry/charts/container/component.rb

The chart container.

Poetry::Charts::Engine

class < Rails::Engine · lib/poetry/charts/engine.rb

The Rails engine: wires poetry-charts into the host app - component autoload paths, the Stimulus controllers manifest, the view helper, preview and asset paths, and the importmap pins. Loading the gem is the only integration step.

gem "poetry-charts"

Poetry::Charts::Geometry

module · lib/poetry/charts/geometry.rb

The geometry core: the tick, scale, and shape math the charts stand on - array ticks, linear/band/point scales, line/area/curve generators, stack layout, and decimal-exact nice-ticks. Every piece is oracle-tested: committed fixtures (test/support/generate_geometry_fixtures.mjs) pin the expected outputs byte-for-byte, alongside translated spec cases.

JS number semantics are part of the contract - path strings must match the fixture output byte-for-byte - so rounding and stringification go through js_round / js_number below, never through Ruby defaults (Ruby rounds half away from zero and prints "80.0"; JS floors x+0.5 and prints "80").

Poetry::Charts::Geometry.js_number(80.0) # => "80"

.js_number(value)

JS Number#toString for the values that appear in SVG path data: integral doubles print bare ("80", not "80.0"), -0 prints "0", and everything else uses shortest round-trip decimal (Ruby and V8 agree on shortest-repr in the post-rounding magnitude range; the exponent guard covers the sub-1e-4 corner where Ruby switches early).

.js_round(value)

JS Math.round: floor(x + 0.5) - differs from Float#round at negative halves (JS rounds -2.5 to -2; Ruby to -3).

.js_truthy?(value)

JS truthiness for the curve state machines (the line state flag runs nil | 0 | 1 | NaN): nil, 0, and NaN are falsy.

Poetry::Charts::Geometry::Area

class · lib/poetry/charts/geometry/area.rb

The area generator: the filled band between a top line (x/x1, y1) and a baseline (x0, y0), walked forward along the top and BACKWARD along the buffered baseline per defined-segment (the x0z/y0z buffers). Stacked areas feed y0/y1 from Stack series; simple areas use a constant y0 (the axis line).

Poetry::Charts::Geometry::Area.new(y0: 250.0).path(points)

#initialize(x: nil, x1: nil, y0: nil, y1: nil, curve: :linear, defined: nil, digits: 3)

Returns (Area) — a new instance of Area

#path(data)

The SVG path for the data (nil when nothing was defined).

  • data (Enumerable)

Poetry::Charts::Geometry::Curve

module · lib/poetry/charts/geometry/curve.rb

The curve state machines - including the _line undefined/0/1/NaN dance that decides where subpaths close (JS truthiness via Geometry.js_truthy?; 1 - undefined becomes NaN via js_flip). The set matches the families' curve: whitelist: linear, step (+before/after), natural, and monotone_x.

Poetry::Charts::Geometry::Curve.build(:natural, Path.new)
Constant Description
REGISTRY Curve name -> builder for its state machine.

.build(name, context)

A curve state machine writing into the given path context.

.js_flip(line)

JS 1 - line where line may be undefined (nil) or NaN.

Poetry::Charts::Geometry::Line

class · lib/poetry/charts/geometry/line.rb

The line generator: data -> SVG path string through a curve state machine, with defined-gaps starting new subpaths (a single toggle loop). Accessors are lambdas (d, i), symbols/strings (hash key lookup), or numeric constants; x/y default to the [x, y] pair convention.

Poetry::Charts::Geometry::Line.new(curve: :monotone_x).path(points)

#initialize(x: nil, y: nil, curve: :linear, defined: nil, digits: 3)

Returns (Line) — a new instance of Line

#path(data)

The SVG path for the data (nil when nothing was defined).

  • data (Enumerable)

Poetry::Charts::Geometry::Line::Accessor

module · lib/poetry/charts/geometry/line.rb

Accessor coercion shared by the generators.

.wrap(value, &default)

A (d, i) lambda from a lambda, key, constant, or the default.

Poetry::Charts::Geometry::NiceTicks

module · lib/poetry/charts/geometry/nice_ticks.rb

Adapted from an MIT-licensed source (source and license in THIRD_PARTY_NOTICES.md).

The nice-ticks algorithm on BigDecimal, so the tick values stay decimal-exact. Every operation is decimal, not binary: values construct from the double's shortest decimal string, remainders truncate with the dividend's sign (#remainder), division carries 20 significant digits (PRECISION), and digit counts come from BigDecimal#exponent (exactly floor(log10) + 1).

Oracle: translated spec cases pin the expected tick values.

Poetry::Charts::Geometry::NiceTicks.nice_ticks([0, 97], 5)
Constant Description
PRECISION Significant digits carried through every division.
SNAP_STEPS The step multiples the :snap125 mode snaps to.

.adaptive_step(rough_step, allow_decimals, correction_factor)

The default step function: amend the rough step to a value that reads well at its order of magnitude.

.calculate_step(min, max, tick_count, allow_decimals, correction_factor = 0, step_fn: method(:adaptive_step))

The step + tick bounds for an interval (recursive: a correction factor grows the step until tickCount ticks cover the interval).

.dec(value)

Coerce to BigDecimal via the double's shortest decimal string.

.digit_count(value)

Digit count: 1 for [1,10), 0 for [0.1,1), -1 for [0.01,0.1)... BigDecimal#exponent IS floor(log10(|v|)) + 1, exactly.

.fixed_domain_ticks(domain, tick_count, allow_decimals: true, mode: :auto)

Nice-stepped ticks CONSTRAINED to [min, max] - the domain boundary always closes the list.

.nice_ticks(domain, tick_count = 6, allow_decimals: true, mode: :auto)

Nice ticks for [min, max] - ticks may run OUTSIDE the interval to stay round.

.range_step(start, stop, step)

[start, end) with a fixed decimal step.

.snap125_step(rough_step, allow_decimals, correction_factor)

The opt-in snap125 step: snap to 1 / 2 / 2.5 / 5 at each order of magnitude.

.step_function(mode)

The step function a mode selects.

.ticks_of_single_value(value, tick_count, allow_decimals)

Ticks when min == max: center a window of tickCount steps on the value.

.valid_interval(min, max)

The interval sorted ascending.

Poetry::Charts::Geometry::Path

class · lib/poetry/charts/geometry/path.rb

The path buffer the shape generators write into: move_to / line_to / bezier_curve_to / quadratic_curve_to / close_path. Numbers are rounded to digits decimals with JS Math.round semantics and stringified as JS does (digits defaults to 3) - the contract that keeps poetry's path strings byte-equal to the geometry fixtures. Internal cursor state keeps FULL precision (rounding is output-formatting only).

The arc/sector verbs are deliberately absent - polar sector paths are built by Polar, not through this class.

path = Path.new
path.move_to(0, 0)
path.line_to(10, 20.5)
path.to_s # => "M0,0L10,20.5"

#bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y)

A cubic curve to (x, y) with two control points.

#close_path

Closes the current subpath back to its start (a no-op before any move).

#empty?

Whether nothing has been written yet.

#initialize(digits: 3)

  • digits (Integer, nil) — output rounding decimals; nil disables rounding (full-precision output)

Returns (Path) — a new instance of Path

#line_to(x, y)

A straight segment to (x, y).

#move_to(x, y)

Starts a new subpath at (x, y).

#quadratic_curve_to(cpx, cpy, x, y)

A quadratic curve to (x, y) with one control point.

#to_s

The accumulated SVG path data.

Poetry::Charts::Geometry::Scale

module · lib/poetry/charts/geometry/scale/band.rb

The scale namespace: Linear, Band, and Point.

Poetry::Charts::Geometry::Scale::Band

class · lib/poetry/charts/geometry/scale/band.rb

A band scale: categorical domain -> evenly stepped positions with inner/outer padding and alignment. Point is band with padding_inner = 1 (bandwidth 0), where padding: drives the outer padding - the axis shape line/area charts position categories with.

Poetry::Charts::Geometry::Scale::Band
  .new(domain: %w[a b c], range: [0, 300]).positions

.padded(domain:, range:, padding: 0.0, align: 0.5, round: false)

Convenience for the common single padding: knob - one value sets inner AND outer padding.

#call(value)

The band's leading-edge position for a category (nil when the category is unknown).

#align

Returns the value of attribute align.

#bandwidth

Returns the value of attribute bandwidth.

#call(value)

The band's leading-edge position for a category (nil when the category is unknown).

#domain

Returns the value of attribute domain.

#initialize(domain:, range:, padding_inner: 0.0, padding_outer: 0.0, align: 0.5, round: false)

Returns (Band) — a new instance of Band

#padding_inner

Returns the value of attribute padding_inner.

#padding_outer

Returns the value of attribute padding_outer.

#positions

Returns the value of attribute positions.

#range

Returns the value of attribute range.

#step

Returns the value of attribute step.

Poetry::Charts::Geometry::Scale::Linear

class · lib/poetry/charts/geometry/scale/linear.rb

A linear scale reduced to the closed poetry surface: a two-point numeric domain/range with bimap normalization (descending domains supported), ticks via Geometry::Ticks, and the nice() domain extension. Degenerate domains map every input to the range midpoint.

Poetry::Charts::Geometry::Scale::Linear
  .new(domain: [0, 100], range: [300, 5]).call(50)

#call(value)

The range value for a domain value.

  • value (Numeric)

#call(value)

The range value for a domain value.

  • value (Numeric)

#domain

Returns the value of attribute domain.

#initialize(domain: [0.0, 1.0], range: [0.0, 1.0])

  • domain (Array<Numeric>) — the two-point input interval
  • range (Array<Numeric>) — the two-point output interval

Returns (Linear) — a new instance of Linear

#invert(value)

The domain value for a range value - the inverse map.

  • value (Numeric)

#nice(count = 10)

Extends the domain to tick-increment boundaries, iterating until the increment is stable. Returns a NEW scale (poetry immutability).

#range

Returns the value of attribute range.

#ticks(count = 10)

Float-exact ticks across the domain.

Poetry::Charts::Geometry::Scale::Point

class < Poetry::Charts::Geometry::Scale::Band · lib/poetry/charts/geometry/scale/band.rb

A point scale: a band with padding_inner pinned to 1 - every category is a zero-width position, padding: is the outer padding.

Poetry::Charts::Geometry::Scale::Point
  .new(domain: %w[a b c], range: [0, 300]).positions

#initialize(domain:, range:, padding: 0.0, align: 0.5, round: false)

Returns (Point) — a new instance of Point

Poetry::Charts::Geometry::Stack

class · lib/poetry/charts/geometry/stack.rb

The stack layout (declaration order): rows x series keys -> per-series [base, top] pairs. Missing values behave as JS +undefined = NaN (the offsets carry the exact NaN fallbacks). :expand normalizes each row to sum 1; :diverging routes negatives below the axis.

Poetry::Charts::Geometry::Stack.new(keys: %w[a b]).series(rows)
Constant Description
OFFSETS The stack baseline modes.

#initialize(keys:, value: nil, offset: :none)

Returns (Stack) — a new instance of Stack

#series(data)

The stacked series for the rows, offsets applied.

  • data (Enumerable) — the rows

Poetry::Charts::Geometry::Stack::Series

class < Struct · lib/poetry/charts/geometry/stack.rb

One stacked series: its key, order index, and [base, top] points.

#[](index)

The [base, top] pair at one row index.

#index

Returns the value of attribute index

Returns (Object) — the current value of index

#index=(value)

Sets the attribute index

  • value (Object) — the value to set the attribute index to.

Returns (Object) — the newly set value

#key

Returns the value of attribute key

Returns (Object) — the current value of key

#key=(value)

Sets the attribute key

  • value (Object) — the value to set the attribute key to.

Returns (Object) — the newly set value

#length

The number of rows stacked.

#points

Returns the value of attribute points

Returns (Object) — the current value of points

#points=(value)

Sets the attribute points

  • value (Object) — the value to set the attribute points to.

Returns (Object) — the newly set value

Poetry::Charts::Geometry::Ticks

module · lib/poetry/charts/geometry/ticks.rb

Float-exact tick generation: ticks / tick_increment / tick_step. The 1-2-5-10 step selection runs against square-root thresholds, and the inverted-increment encoding (negative inc = divisor) keeps tick values float-exact - a sub-1 step divides by an integer instead of multiplying by a fraction.

Poetry::Charts::Geometry::Ticks.ticks(0, 10, 5) # => [0, 2, 4, 6, 8, 10]
Constant Description
E10 The step-10 selection threshold: sqrt(50).
E2 The step-2 selection threshold: sqrt(2).
E5 The step-5 selection threshold: sqrt(10).

.tick_increment(start, stop, count)

The raw increment for the run - negative encodes a divisor.

.tick_step(start, stop, count)

The absolute step size for the run (the increment decoded, signed by direction).

.ticks(start, stop, count)

About count evenly stepped, float-exact values covering [start, stop].

Poetry::Charts::GridConfig

class < Data · lib/poetry/charts.rb

The grid capture shape - which rule directions render (radar carries its own, with polygon/circle rings).

#horizontal

Returns the value of attribute horizontal

Returns (Object) — the current value of horizontal

#vertical

Returns the value of attribute vertical

Returns (Object) — the current value of vertical

Poetry::Charts::LegendContent

module · app/components/poetry/charts/legend_content/component.rb

The chart legend chrome.

Poetry::Charts::LineChart

module · app/components/poetry/charts/line_chart/component.rb

The line chart family.

Poetry::Charts::Live

module · app/components/poetry/charts/live.rb

Live mode: a chart that opts in with live: true embeds a {spec, frame} payload the client renderer recomputes geometry from when data changes too often to round-trip to the server. spec is the FROZEN spec v1 built by the same Poetry::Charts::Spec the adapter seam uses (the spec stays closed); frame is a PRIVATE engine envelope carrying the geometry-affecting knobs the spec deliberately omits. Everything else about the chart stays server-rendered.

Live charts cannot carry Ruby lambdas to the browser: tick formatters and label slots raise a teaching error - format data host-side (pre-formatted category strings) instead.

Constant Description
BRUSH_GAP Pixels between the plot's bottom margin and the brush strip.
CONTROLLER The live controller's full-string identifier.
WINDOW_CONTROLLER The window controller's full-string identifier.

.included(base)

Poetry::Charts::Live::ClassMethods

module · app/components/poetry/charts/live.rb

The class-level macro families call to opt into live mode.

#live_option

Declares the live-mode surface: the live: and zoom: options plus the with_brush slot.

Poetry::Charts::Motion

module · app/components/poetry/charts/motion.rb

Shared animation surface for the chart families: chart-level options (animate / animation_duration / animation_easing / animation_begin, defaults set per family), emitted as data-animate plus --poetry-motion-* custom properties on the SVG. The animations themselves are CSS (the motion stylesheet) and the motion controller - the server computes all geometry; the client only interpolates between server-computed states.

Constant Description
CONTROLLER The motion controller's full-string identifier.
EASINGS The allowed animation_easing keywords.

.included(base)

#motion_style_extras

Hook: families append extra custom properties (radar ships the polar center so CSS can scale from it).

Poetry::Charts::Motion::ClassMethods

module · app/components/poetry/charts/motion.rb

The class-level macro families call to declare their animation options.

#motion_options(duration: 1500, delay: 0, easing: :ease)

Declares the animation options with the family's defaults (bar 400ms, pie delayed 400ms, scatter 400ms linear, everything else 1500ms ease with no delay).

Poetry::Charts::PieChart

module · app/components/poetry/charts/pie_chart/component.rb

The pie chart family.

Poetry::Charts::Polar

module · lib/poetry/charts/polar.rb

The polar geometry: pie-sector accumulation, tangent-circle corner rounding, and wedge/ring paths. Angles are degrees COUNTERCLOCKWISE from 3 o'clock, negated into SVG's y-down plane by polar_to_cartesian; pies start at angle 0 and sweep to 360.

Poetry::Charts::Polar.pie_sectors([3, 1], padding_angle: 2)
Constant Description
RADIAN Degrees-to-radians factor.

.max_radius(width, height)

The largest radius fitting the plot: half the shorter side.

.percent_value(value, total, default = 0)

"80%" of the max radius, or a plain number.

.pie_sectors(values, start_angle: 0, end_angle: 360, padding_angle: 0, min_angle: 0)

The pie accumulation: values -> per-slice angles. Zero values collapse (and skip padding); paddings live BETWEEN non-zero slices (full circles pad after the last slice too, closing the ring).

.polar_to_cartesian(cx, cy, radius, angle)

The [x, y] point at (radius, angle) from the center, in SVG's y-down plane.

.sector_path(cx:, cy:, inner_radius:, outer_radius:, start_angle:, end_angle:, fmt: nil)

The wedge/ring path. The delta clamps at 359.999 so a full circle's endpoints never coincide.

.sector_path_with_corners(cx:, cy:, inner_radius:, outer_radius:, start_angle:, end_angle:, corner_radius:, fmt: nil)

The ring segment with all four corners rounded by tangent circles. Falls back to the plain path when the sweep is too small to fit the corners.

.sign(value)

-1, 0, or 1 by the value's sign.

.tangent_circle(cx:, cy:, radius:, angle:, sign:, corner_radius:, external: false)

The corner circle tangent to an arc (at radius) and a radial edge (at angle) - the rounded-corner primitive for the radial bar's corner_radius.

Poetry::Charts::PolarFamily

module · app/components/poetry/charts/polar_family.rb

The polar family chassis (pie/radar/radial): the shared margin + plot/center geometry, and the per-sector pointer hit - polar marks are hit by pointerover on the marked sector/wedge, not bisect, so the svg gains the enter action after TooltipWiring's pointer/ keyboard set (include order is emission order - include this after TooltipWiring).

class GaugeChart::Component < Poetry::Core::Component
  include Poetry::Charts::ChartFamily
  include Poetry::Charts::TooltipWiring
  include Poetry::Charts::PolarFamily
  include Poetry::Charts::PolarFamily::SingleSeriesTooltip
end
Constant Description
MARGIN The default polar margin - a slim, even inset on all sides.

.included(base)

Poetry::Charts::PolarFamily::SingleSeriesTooltip

module · app/components/poetry/charts/polar_family.rb

The single-series polar tooltip (pie/radial): the FIRST series drives the chrome, and per-index names/colors retint the one row. Families supply polar_items (the sector geometry), polar_anchor (where the tooltip anchors on one item), and polar_value_rows (the rows the values read from). Radar keeps TooltipWiring's multi-series chrome and its own payload, so it includes PolarFamily alone.

Poetry::Charts::RadarChart

module · app/components/poetry/charts/radar_chart/component.rb

The radar chart family.

Poetry::Charts::RadialBarChart

module · app/components/poetry/charts/radial_bar_chart/component.rb

The radial bar chart family.

Poetry::Charts::ReferenceMarks

module · app/components/poetry/charts/reference_marks.rb

Reference marks - annotation lines, areas, and dots - for every cartesian family. Values speak the chart's own axes - categories on the category axis (the band/point center), numbers on the value axis (scatter overrides both to numeric) - and render as a single group painted ABOVE the series so annotations stay readable over the marks. Labels are strings (the live rule: no lambdas).

Hosts provide ref_x_pixel/ref_y_pixel (the concern's defaults speak cartesian), plot edges via cartesian, css(:reference_line/:reference_area/:tick), and fnum. Vertical layouts only (a horizontal bar raises - a declared limit).

.included(base)

Poetry::Charts::ScatterChart

module · app/components/poetry/charts/scatter_chart/component.rb

The scatter chart family.

Poetry::Charts::ThemeStyle

class · lib/poetry/charts/theme_style.rb

Turns a chart's Config into the scoped per-series custom properties, one block per theme -

[data-chart=chart-revenue] { --color-desktop: var(--chart-1); } .dark [data-chart=chart-revenue] { --color-desktop: oklch(...); }

so series markup (SVG fills, tooltip indicators, legend swatches) can reference var(--color-<key>) and follow theme flips with ZERO re-render. Emission is safe by construction: Config validated every key and color at wrap time.

Poetry::Charts::ThemeStyle.new(id: "chart-revenue", config: config).css
Constant Description
THEMES Theme name -> selector prefix (the .dark class convention).

#css

The stylesheet text, or nil when no entry carries a color - a colorless config renders no <style> element at all.

#initialize(id:, config:)

  • id (String) — the container's data-chart identifier
  • config (Config, Hash) — the chart config the colors come from

Returns (ThemeStyle) — a new instance of ThemeStyle

Poetry::Charts::TooltipContent

module · app/components/poetry/charts/tooltip_content/component.rb

The chart tooltip chrome.

Poetry::Charts::TooltipLayer

module · app/components/poetry/charts/tooltip_layer/component.rb

The chart families' internal hover-tooltip mount.

Poetry::Charts::TooltipWiring

module · app/components/poetry/charts/tooltip_wiring.rb

Shared tooltip wiring for the chart families: the frame wrapper carries the controller, the SVG carries targets/actions plus the accessibilityLayer floor (focusable, role=application, arrows walk categories), and the hidden chrome pre-renders per-series rows the controller text-swaps - zero chart math in the browser.

Constant Description
CONTROLLER Full-string controller identifier - the bare :tooltip shorthand would be ambiguous with the core tooltip controller.
LEGEND_OPTIONS The with_legend options forwarded to the LegendContent child.

.included(base)

The wiring is declared, not hand-built: the frame carries the controller + sync value, the SVG carries the target and the pointer/keyboard actions (the accessibilityLayer floor), and the coordinates <script> is the data target the controller reads.