- name
- build-ui
- description
- Build UI components using the Crystal component system — knows all 94 HTML elements, 3 component tiers, composition patterns, and the builder DSL
- user-invocable
- true
# Build UI with the Crystal Component System
You are a front-end developer who builds server-rendered UIs using the asset pipeline's type-safe Crystal component system. All HTML is generated from Crystal classes — no templates needed.
## Core Patterns
### Creating Elements
```crystal
# Every HTML tag is a Crystal class under Components::Elements
div = Components::Elements::Div.new(class: "container")
div << "Hello, World!"
div.render # => <div class="container">Hello, World!</div>
```
### Builder DSL
```crystal
article = Components::Elements::Article.new(class: "post").build do |art|
header = Components::Elements::Header.new
h1 = Components::Elements::H1.new
h1 << "Title"
header << h1
art << header
p = Components::Elements::P.new
p << "Content here."
art << p
end
article.render
```
### Child Management
- `element << child` — append child (String, HTMLElement, Component, or RawHTML)
- `element.add_children(child1, child2)` — append multiple
- `element.build { |el| ... }` — block DSL for building content
- `Components::Elements::RawHTML.new(html_string)` — insert pre-rendered HTML without escaping
---
## Element Reference (94 Classes)
All classes live under `Components::Elements`. Constructor: `.new(**attrs)` unless noted.
### Document Elements
| Class | Tag | Factory Methods |
|-------|-----|-----------------|
| `Html` | `<html>` | — (render includes `<!DOCTYPE html>`) |
| `Head` | `<head>` | — |
| `Body` | `<body>` | — |
| `Title` | `<title>` | — (text children only) |
| `Meta` | `<meta>` | `.charset(charset?)`, `.viewport(content?)`, `.description(text)`, `.keywords(text)`, `.author(text)`, `.http_equiv(equiv, content)` |
| `Link` | `<link>` | `.stylesheet(href)`, `.icon(href, type?)`, `.favicon(href)`, `.apple_touch_icon(href, sizes?)`, `.manifest(href)`, `.preconnect(href)`, `.prefetch(href)`, `.preload(href, as_type)` |
| `Script` | `<script>` | `.new(js_string)` — content not escaped |
| `Style` | `<style>` | `.new(css_string)` — content not escaped |
### Section Elements
| Class | Tag | Notes |
|-------|-----|-------|
| `Header` | `<header>` | |
| `Footer` | `<footer>` | |
| `Nav` | `<nav>` | |
| `Main` | `<main>` | |
| `Article` | `<article>` | |
| `Section` | `<section>` | |
| `Aside` | `<aside>` | |
| `H1` - `H6` | `<h1>` - `<h6>` | All inherit from `Heading` |
### Grouping Elements
| Class | Tag | Notes |
|-------|-----|-------|
| `Div` | `<div>` | |
| `Span` | `<span>` | |
| `P` | `<p>` | |
| `Pre` | `<pre>` | Content not escaped (preserves whitespace) |
| `Blockquote` | `<blockquote>` | Validates `cite` URL |
| `Ol` | `<ol>` | Validates `type` (1/a/A/i/I), `start` |
| `Ul` | `<ul>` | |
| `Li` | `<li>` | Validates `value` (integer) |
| `Dl` | `<dl>` | |
| `Dt` | `<dt>` | |
| `Dd` | `<dd>` | |
| `Figure` | `<figure>` | |
| `Figcaption` | `<figcaption>` | |
| `Hr` | `<hr>` | Void element |
### Text Semantics
| Class | Tag | Notes |
|-------|-----|-------|
| `A` | `<a>` | Validates `target`, `rel`, `download` |
| `Em` | `<em>` | |
| `Strong` | `<strong>` | |
| `Small` | `<small>` | |
| `S` | `<s>` | |
| `Cite` | `<cite>` | |
| `Code` | `<code>` | |
| `Sub` | `<sub>` | |
| `Sup` | `<sup>` | |
| `I` | `<i>` | |
| `B` | `<b>` | |
| `U` | `<u>` | |
| `Mark` | `<mark>` | |
| `Del` | `<del>` | Validates `cite`, `datetime` |
| `Ins` | `<ins>` | Validates `cite`, `datetime` |
| `Abbr` | `<abbr>` | |
| `Time` | `<time>` | Validates `datetime` |
| `Data` | `<data>` | Requires `value` attribute |
| `Var` | `<var>` | |
| `Kbd` | `<kbd>` | |
| `Samp` | `<samp>` | |
### Form Elements
| Class | Tag | Factory Methods / Notes |
|-------|-----|------------------------|
| `Form` | `<form>` | Validates `method` (GET/POST/dialog), `enctype`, `autocomplete` |
| `Input` | `<input>` | **18 factory methods** — see below |
| `Textarea` | `<textarea>` | `.new(name, value?)`, validates `rows`/`cols`/`wrap` |
| `Select` | `<select>` | Validates `size`, `multiple`, `required` |
| `Option` | `<option>` | `.new(text, value?)`, validates `selected`/`disabled` |
| `Optgroup` | `<optgroup>` | Validates `label` (required), `disabled` |
| `Button` | `<button>` | `.new(text, type?)` — defaults to `type="button"` |
| `Label` | `<label>` | `.new(text, for?)` |
| `Fieldset` | `<fieldset>` | Validates `disabled` |
| `Legend` | `<legend>` | |
| `Datalist` | `<datalist>` | |
| `Output` | `<output>` | Validates `for` (space-separated IDs) |
| `Progress` | `<progress>` | Validates `value`/`max` (non-negative) |
| `Meter` | `<meter>` | Validates `value`/`min`/`max`/`low`/`high`/`optimum` |
**Input Factory Methods** (all return `Input` with correct `type` set):
```crystal
Input.text("username") Input.email("email")
Input.password("pass") Input.number("qty")
Input.checkbox("agree", "yes") Input.radio("plan", "pro")
Input.submit("Save") Input.button("Click")
Input.hidden("token", "abc") Input.file("upload")
Input.date("birthday") Input.time("alarm")
Input.datetime_local("meeting") Input.range("volume", "0", "100")
Input.color("theme") Input.search("query")
Input.tel("phone") Input.url("website")
```
### Table Elements
| Class | Tag | Notes |
|-------|-----|-------|
| `Table` | `<table>` | |
| `Thead` | `<thead>` | |
| `Tbody` | `<tbody>` | |
| `Tfoot` | `<tfoot>` | |
| `Tr` | `<tr>` | |
| `Th` | `<th>` | Validates `scope` (row/col/rowgroup/colgroup), `colspan`/`rowspan` |
| `Td` | `<td>` | Validates `colspan`/`rowspan` |
| `Caption` | `<caption>` | |
| `Colgroup` | `<colgroup>` | Validates `span` |
| `Col` | `<col>` | Void element, validates `span` |
### Embedded / Media
| Class | Tag | Notes |
|-------|-----|-------|
| `Img` | `<img>` | Void. Validates `loading` (lazy/eager), `decoding`, `crossorigin` |
| `Video` | `<video>` | Validates `controls`/`autoplay`/`loop`/`muted`/`playsinline`, `preload` |
| `Audio` | `<audio>` | Validates `controls`/`autoplay`/`loop`/`muted`, `preload` |
| `Source` | `<source>` | Void. Validates MIME type, media queries |
| `Track` | `<track>` | Void. Validates `kind`, `srclang` |
| `Iframe` | `<iframe>` | Validates `sandbox`, `loading` |
| `Embed` | `<embed>` | Void. Validates MIME type |
| `Object` | `<object>` | Validates MIME type |
| `Param` | `<param>` | Void. Requires `name`/`value` |
| `Canvas` | `<canvas>` | |
| `Svg` | `<svg>` | Content not escaped |
| `Picture` | `<picture>` | |
### Interactive Elements
| Class | Tag | Notes |
|-------|-----|-------|
| `Details` | `<details>` | Validates `open` (boolean) |
Ver no GitHub