@ryanhelsing/ry-ui
Version:
Framework-agnostic, Light DOM web components. CSS is the source of truth.
243 lines (183 loc) • 5.34 kB
Markdown
# Layout Components
CSS-only layout primitives. No JavaScript needed.
## `<ry-page>`
Top-level page shell. Contains header, main, footer.
```html
<ry-page>
<ry-header sticky>...</ry-header>
<ry-main>...</ry-main>
<ry-footer>...</ry-footer>
</ry-page>
```
## `<ry-header>`
| Attribute | Values | Description |
|-----------|--------|-------------|
| `sticky` | boolean | Sticks to top of viewport |
Horizontal flex with space-between. Two direct children = left and right.
```html
<ry-header sticky>
<ry-cluster><strong>App</strong></ry-cluster>
<ry-cluster><ry-theme-toggle themes="light,dark"></ry-theme-toggle></ry-cluster>
</ry-header>
```
## `<ry-grid>`
| Attribute | Values | Description |
|-----------|--------|-------------|
| `cols` | 1–6 | Number of columns |
```html
<ry-grid cols="3">
<ry-card>One</ry-card>
<ry-card>Two</ry-card>
<ry-card>Three</ry-card>
</ry-grid>
```
## `<ry-stack>`
Vertical flex layout.
| Attribute | Values | Description |
|-----------|--------|-------------|
| `gap` | sm \| md \| lg | Vertical spacing |
```html
<ry-stack gap="sm">
<p>Item 1</p>
<p>Item 2</p>
</ry-stack>
```
## `<ry-cluster>`
Horizontal flex, wraps.
| Attribute | Values | Description |
|-----------|--------|-------------|
| `gap` | sm \| md \| lg | Horizontal spacing |
```html
<ry-cluster>
<ry-badge>A</ry-badge>
<ry-badge>B</ry-badge>
</ry-cluster>
```
## `<ry-card>`
Content container with padding and border. All cards lift subtly on hover. Add `interactive` for clickable cards.
| Attribute | Values | Description |
|-----------|--------|-------------|
| `interactive` | boolean | Clickable card — cursor pointer, stronger hover lift, primary border, keyboard support |
| `href` | URL | Navigate on click (requires `interactive`) |
Events: `ry:click` (interactive cards only)
```html
<!-- Static card -->
<ry-card>
<h3>Title</h3>
<p>Content.</p>
<ry-actions>
<ry-button>Save</ry-button>
<ry-button variant="ghost">Cancel</ry-button>
</ry-actions>
</ry-card>
<!-- Interactive card with navigation -->
<ry-card interactive href="/demos/goap">
<h3>GOAP</h3>
<p>Goal-Oriented Action Planning</p>
</ry-card>
<!-- Interactive card with event -->
<ry-card interactive>
<h3>Click Me</h3>
<p>Emits ry:click. Tab + Enter for keyboard.</p>
</ry-card>
```
Hover behavior:
- **All cards**: subtle lift (-2px) + border/shadow bump
- **Interactive**: stronger lift (-3px), primary-colored border, snaps back on click
- Clicks on child `<a>`, `<button>`, `<ry-button>` elements pass through normally
## `<ry-section>`
Content section block. Adds bottom margin between sections, removed on last child.
```html
<ry-main>
<ry-section><h2>Section 1</h2><p>Content.</p></ry-section>
<ry-section><h2>Section 2</h2><p>Content.</p></ry-section>
</ry-main>
```
## `<ry-aside>`
Sidebar / secondary content block.
```html
<ry-split>
<ry-main>Primary content</ry-main>
<ry-aside>Sidebar content</ry-aside>
</ry-split>
```
## `<ry-split>`
Two-pane layout: content (flexible) + sidebar (fixed). Stacks vertically on mobile (<768px).
| Attribute | Values | Description |
|-----------|--------|-------------|
| `resizable` | boolean | Enables drag handle between panes |
| `persist` | string | localStorage key — saves/restores width as `ry-split:{key}` |
CSS custom properties:
| Property | Default | Description |
|----------|---------|-------------|
| `--ry-split-width` | `300px` | Sidebar width |
| `--ry-split-min-width` | `100px` | Minimum width during resize |
| `--ry-split-max-width` | `80%` | Maximum width during resize |
Events: `ry:resize` — `e.detail.width`
```html
<!-- Basic -->
<ry-split>
<div>Main content (flex: 1)</div>
<div>Sidebar (300px)</div>
</ry-split>
<!-- Resizable with persistence -->
<ry-split resizable persist="sidebar" style="--ry-split-width: 400px">
<main>Content</main>
<aside>Resizable sidebar</aside>
</ry-split>
```
Resize interaction:
- **Drag** handle between panes (mouse + touch)
- **Arrow keys** ±10px, **Shift+Arrow** ±50px
- **Home/End** jump to min/max
- **Double-click** handle to reset to default
## `<ry-center>`
Centers content both horizontally and vertically.
```html
<ry-center>
<h1>Centered title</h1>
<p>Centered paragraph</p>
</ry-center>
```
## `<ry-nav>`
Horizontal navigation links. Use `aria-current="page"` to highlight the active link.
```html
<ry-nav>
<a href="/" aria-current="page">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</ry-nav>
```
## `<ry-logo>`
Inline logo element. Bold, large text. Typically used inside `<ry-header>`.
```html
<ry-header>
<ry-logo>My App</ry-logo>
<ry-nav>...</ry-nav>
</ry-header>
```
## `<ry-actions>`
Horizontal button group. Use inside cards, modals, or any container.
```html
<ry-actions>
<ry-button>Primary</ry-button>
<ry-button variant="ghost">Cancel</ry-button>
</ry-actions>
```
## `<ry-divider>`
Horizontal or vertical separator line.
| Attribute | Values | Description |
|-----------|--------|-------------|
| `vertical` | boolean | Vertical orientation (for use inside flex rows) |
```html
<ry-stack>
<p>Above</p>
<ry-divider></ry-divider>
<p>Below</p>
</ry-stack>
<ry-cluster>
<span>Left</span>
<ry-divider vertical></ry-divider>
<span>Right</span>
</ry-cluster>
```