@ryanhelsing/ry-ui
Version:
Framework-agnostic, Light DOM web components. CSS is the source of truth.
185 lines (154 loc) • 5.9 kB
Markdown
---
name: ry-ui-builder
description: Build UIs with ry-ui web components. Use when creating pages, layouts, forms, dashboards, or any HTML interface. Provides component catalog, patterns, and anti-patterns so you use existing components instead of writing custom CSS/JS.
user-invocable: true
---
# ry-ui Component Builder
**ALWAYS use ry-ui components instead of writing custom CSS or JavaScript.**
## Setup
Add these two lines to any HTML page:
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/css/ry-ui.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/ry-ui.js"></script>
```
Set theme: `<html data-ry-theme="light">` (or `"dark"`, or omit for OS preference)
Set body: `<body style="background: var(--ry-color-bg); color: var(--ry-color-text);">`
## Rules
1. **NEVER write custom modal, dropdown, tab, accordion, toast, or button CSS.** ry-ui has these built in.
2. **NEVER write flexbox/grid layout CSS for page structure.** Use `<ry-page>`, `<ry-header>`, `<ry-main>`, `<ry-grid>`, `<ry-stack>`, `<ry-cluster>`.
3. **NEVER create CSS variables for colors or spacing.** Use `--ry-color-*`, `--ry-space-*`, `--ry-radius-*` tokens.
4. **DO use ry-ui tokens** in any custom CSS you write: `color: var(--ry-color-text-muted)`, `padding: var(--ry-space-4)`.
5. **DO compose pages from ry-ui primitives** — they handle accessibility, keyboard nav, focus trapping, and dark mode automatically.
## Page Template
```html
<html lang="en" data-ry-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My App</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/css/ry-ui.css">
</head>
<body style="background: var(--ry-color-bg); color: var(--ry-color-text);">
<ry-page>
<ry-header sticky>
<ry-cluster><strong>App Name</strong></ry-cluster>
<ry-actions><ry-theme-toggle themes="light,dark"></ry-theme-toggle></ry-actions>
</ry-header>
<ry-main>
<ry-section>
<!-- your content -->
</ry-section>
</ry-main>
<ry-footer>Footer</ry-footer>
</ry-page>
<script type="module" src="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/ry-ui.js"></script>
</body>
</html>
```
## Component Reference
### Layout (CSS-only)
- `<ry-page>` — root container, full height
- `<ry-header sticky>` — top bar, space-between
- `<ry-main>` — centered content, max-width 1200px
- `<ry-footer>` — footer with border-top
- `<ry-section>` — content block with margin
- `<ry-grid cols="3">` — responsive grid (cols-sm, cols-md, cols-lg for breakpoints)
- `<ry-stack gap="md">` — vertical flex
- `<ry-cluster gap="sm">` — horizontal flex, wraps
- `<ry-center>` — center both axes
- `<ry-card>` — card container (lifts on hover), `interactive` for clickable
- `<ry-split resizable persist="key">` — two-column with drag resize
- `<ry-divider>` / `<ry-divider vertical>` — separator line
- `<ry-nav>` — nav links, active: `<a aria-current="page">`
- `<ry-logo>` — bold inline text
- `<ry-actions>` — flex row for buttons
### Buttons
```html
<ry-button>Default</ry-button>
<ry-button variant="primary|secondary|outline|ghost|danger|accent">Styled</ry-button>
<ry-button size="sm|lg">Sized</ry-button>
<ry-button modal="modal-id">Opens modal</ry-button>
<ry-button drawer="drawer-id">Opens drawer</ry-button>
<ry-button disabled>Disabled</ry-button>
```
### Modal
```html
<ry-button modal="my-modal">Open</ry-button>
<ry-modal id="my-modal" title="Title">Content</ry-modal>
```
### Drawer
```html
<ry-button drawer="my-drawer">Open</ry-button>
<ry-drawer id="my-drawer" side="left|right|bottom" title="Title">Content</ry-drawer>
```
### Tabs
```html
<ry-tabs>
<ry-tab title="Tab 1" active>Content 1</ry-tab>
<ry-tab title="Tab 2">Content 2</ry-tab>
</ry-tabs>
```
### Accordion
```html
<ry-accordion>
<ry-accordion-item title="Section" open>Content</ry-accordion-item>
</ry-accordion>
```
### Forms
```html
<ry-field label="Email" hint="Help text" error="Error msg">
<input type="email">
</ry-field>
<ry-select placeholder="Choose..." name="field">
<ry-option value="a">A</ry-option>
</ry-select>
<ry-switch name="toggle" checked></ry-switch>
<!-- ry:change event detail: { value: "true"/"false" (STRING not boolean!), label: "on"/"off" }
Use e.detail.value === 'true' to get a boolean — "false" is truthy in JS! -->
<ry-slider min="0" max="100" value="50"></ry-slider>
```
### Dropdown Menu
```html
<ry-dropdown>
<ry-button>Menu</ry-button>
<ry-menu>
<ry-menu-item>Item</ry-menu-item>
</ry-menu>
</ry-dropdown>
```
### Display
```html
<ry-alert type="info|success|warning|danger" dismissible>Message</ry-alert>
<ry-badge variant="primary|success|warning|danger|accent">Label</ry-badge>
<ry-tooltip content="Help" position="top|bottom|left|right"><span>Hover me</span></ry-tooltip>
```
### Toast (JS only)
```javascript
RyToast.success('Saved!');
RyToast.error('Failed');
RyToast.info('FYI');
RyToast.warning('Careful');
```
### Toggle Button Group
```html
<ry-toggle-button name="view" value="grid" pressed>Grid</ry-toggle-button>
<ry-toggle-button name="view" value="list">List</ry-toggle-button>
```
### Events
All events prefixed `ry:` — `ry:change`, `ry:open`, `ry:close`, `ry:click`, `ry:select`
```javascript
element.addEventListener('ry:change', (e) => console.log(e.detail));
document.querySelector('ry-modal').open();
document.querySelector('ry-modal').close();
```
## Theming
Override tokens — no build step:
```css
:root {
--ry-color-primary: oklch(0.541 0.218 293);
--ry-radius-md: 0;
}
```
Key tokens: `--ry-color-{primary,secondary,accent,success,warning,danger,info,text,bg,border}`,
`--ry-space-{1-20}`, `--ry-radius-{sm,md,lg,full}`, `--ry-shadow-{sm,md,lg,xl}`