@rip-lang/ui 0.3.66 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +93 -0
- package/README.md +22 -625
- package/browser/AGENTS.md +213 -0
- package/browser/CONTRIBUTING.md +375 -0
- package/browser/README.md +11 -0
- package/browser/TESTING.md +59 -0
- package/browser/browser.rip +56 -0
- package/{components → browser/components}/accordion.rip +1 -1
- package/{components → browser/components}/alert-dialog.rip +6 -3
- package/{components → browser/components}/autocomplete.rip +27 -21
- package/{components → browser/components}/avatar.rip +3 -3
- package/{components → browser/components}/badge.rip +1 -1
- package/{components → browser/components}/breadcrumb.rip +2 -2
- package/{components → browser/components}/button-group.rip +3 -3
- package/{components → browser/components}/button.rip +2 -2
- package/{components → browser/components}/card.rip +1 -1
- package/{components → browser/components}/carousel.rip +5 -5
- package/{components → browser/components}/checkbox-group.rip +40 -11
- package/{components → browser/components}/checkbox.rip +4 -4
- package/{components → browser/components}/collapsible.rip +2 -2
- package/{components → browser/components}/combobox.rip +36 -23
- package/{components → browser/components}/context-menu.rip +1 -1
- package/{components → browser/components}/date-picker.rip +5 -5
- package/{components → browser/components}/dialog.rip +8 -4
- package/{components → browser/components}/drawer.rip +8 -4
- package/{components → browser/components}/editable-value.rip +7 -1
- package/{components → browser/components}/field.rip +5 -5
- package/{components → browser/components}/fieldset.rip +2 -2
- package/{components → browser/components}/form.rip +1 -1
- package/{components → browser/components}/grid.rip +8 -8
- package/{components → browser/components}/input-group.rip +1 -1
- package/{components → browser/components}/input.rip +6 -6
- package/{components → browser/components}/label.rip +2 -2
- package/{components → browser/components}/menu.rip +17 -10
- package/{components → browser/components}/menubar.rip +1 -1
- package/{components → browser/components}/meter.rip +7 -7
- package/{components → browser/components}/multi-select.rip +76 -33
- package/{components → browser/components}/native-select.rip +3 -3
- package/{components → browser/components}/nav-menu.rip +3 -3
- package/{components → browser/components}/number-field.rip +11 -11
- package/{components → browser/components}/otp-field.rip +4 -4
- package/{components → browser/components}/pagination.rip +4 -4
- package/{components → browser/components}/popover.rip +11 -24
- package/{components → browser/components}/preview-card.rip +7 -11
- package/{components → browser/components}/progress.rip +3 -3
- package/{components → browser/components}/radio-group.rip +4 -4
- package/{components → browser/components}/resizable.rip +3 -3
- package/{components → browser/components}/scroll-area.rip +1 -1
- package/{components → browser/components}/select.rip +55 -27
- package/{components → browser/components}/separator.rip +2 -2
- package/{components → browser/components}/skeleton.rip +4 -4
- package/{components → browser/components}/slider.rip +15 -10
- package/{components → browser/components}/spinner.rip +2 -2
- package/{components → browser/components}/table.rip +2 -2
- package/{components → browser/components}/tabs.rip +12 -7
- package/{components → browser/components}/textarea.rip +8 -8
- package/{components → browser/components}/toast.rip +3 -3
- package/{components → browser/components}/toggle-group.rip +42 -11
- package/{components → browser/components}/toggle.rip +2 -2
- package/{components → browser/components}/toolbar.rip +2 -2
- package/{components → browser/components}/tooltip.rip +19 -23
- package/browser/hljs-rip.js +209 -0
- package/browser/playwright.config.mjs +31 -0
- package/browser/tests/overlays.js +349 -0
- package/email/AGENTS.md +16 -0
- package/email/README.md +55 -0
- package/email/benchmarks/benchmark.rip +94 -0
- package/email/benchmarks/samples.rip +104 -0
- package/email/compat.rip +129 -0
- package/email/components.rip +371 -0
- package/email/dom.rip +330 -0
- package/email/email.rip +10 -0
- package/email/render.rip +82 -0
- package/package.json +29 -39
- package/shared/README.md +3 -0
- package/shared/styles.rip +17 -0
- package/tailwind/AGENTS.md +3 -0
- package/tailwind/README.md +27 -0
- package/tailwind/engine.js +107 -0
- package/tailwind/inline.js +215 -0
- package/tailwind/serve.js +6 -0
- package/tailwind/tailwind.rip +13 -0
- package/ui.rip +3 -0
package/README.md
CHANGED
|
@@ -1,642 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
<img src="https://raw.githubusercontent.com/shreeve/rip-lang/main/docs/assets/rip.png" style="width:50px" /> <br>
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Every widget exposes `$` attributes (compiled to `data-*`) for styling and
|
|
5
|
-
handles keyboard interactions per WAI-ARIA Authoring Practices. Style with
|
|
6
|
-
Tailwind using `data-[attr]:` variants.
|
|
3
|
+
# Rip UI - @rip-lang/ui
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
them on the fly.
|
|
5
|
+
> **Unified UI system for Rip.**
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
`@rip-lang/ui` is the umbrella package for:
|
|
8
|
+
- `@rip-lang/ui/browser` — headless interactive browser widgets
|
|
9
|
+
- `@rip-lang/ui/email` — curated PascalCase email components
|
|
10
|
+
- `@rip-lang/ui/shared` — shared render/styling helpers
|
|
11
|
+
- `@rip-lang/ui/tailwind` — Tailwind compiler/inlining integration
|
|
12
12
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
Add the components directory to your serve middleware:
|
|
16
|
-
|
|
17
|
-
```coffee
|
|
18
|
-
use serve
|
|
19
|
-
dir: dir
|
|
20
|
-
bundle: ['components', '../../../packages/ui']
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
All widgets become available by name (`Select`, `Dialog`, `Grid`, etc.) in the
|
|
24
|
-
shared scope — no imports needed.
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
cd packages/ui
|
|
28
|
-
rip server
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
For browser quality checks and overlay regression tests, see
|
|
32
|
-
[TESTING.md](TESTING.md).
|
|
33
|
-
|
|
34
|
-
Every widget:
|
|
35
|
-
- Handles all keyboard interactions per WAI-ARIA Authoring Practices
|
|
36
|
-
- Sets correct ARIA attributes automatically
|
|
37
|
-
- Exposes state via `$` sigil (`$open`, `$selected`) — style with Tailwind's `data-[attr]:` variants
|
|
38
|
-
- Ships no CSS — you bring Tailwind classes
|
|
39
|
-
- Uses Rip's reactive primitives for all state management
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Rip in 60 Seconds
|
|
44
|
-
|
|
45
|
-
If you're coming from React or another framework, here's the Rip you need
|
|
46
|
-
to know to use these widgets:
|
|
47
|
-
|
|
48
|
-
| Syntax | Name | What It Does |
|
|
49
|
-
|--------|------|-------------|
|
|
50
|
-
| `:=` | State | `count := 0` — reactive state (like `useState`) |
|
|
51
|
-
| `~=` | Computed | `doubled ~= count * 2` — derived value (like `useMemo`, but auto-tracked) |
|
|
52
|
-
| `~>` | Effect | `~> document.title = "#{count}"` — side effect (like `useEffect`, but auto-tracked) |
|
|
53
|
-
| `<=>` | Bind | `value <=> @name` — two-way binding between parent and child |
|
|
54
|
-
| `@prop` | Prop | `@checked`, `@disabled` — component props (reactive) |
|
|
55
|
-
| `$attr` | Data attr | `$open`, `$selected` — compiles to `data-open`, `data-selected` in HTML |
|
|
56
|
-
| `@emit` | Event | `@emit 'change', value` — dispatches a CustomEvent |
|
|
57
|
-
| `ref:` | DOM ref | `ref: "_panel"` — saves DOM element reference |
|
|
58
|
-
| `slot` | Children | Projects parent-provided content into the component |
|
|
59
|
-
| `offer` / `accept` | Context | Share reactive state between ancestor and descendant components |
|
|
60
|
-
| `::` | Type | `@variant:: string := "default"` — typed prop (enables IDE completions + diagnostics) |
|
|
61
|
-
|
|
62
|
-
Two-way binding example — React vs Rip:
|
|
63
|
-
|
|
64
|
-
```coffee
|
|
65
|
-
# React: 4 lines per binding
|
|
66
|
-
const [show, setShow] = useState(false)
|
|
67
|
-
<Dialog open={show} onOpenChange={setShow} />
|
|
68
|
-
const [name, setName] = useState('')
|
|
69
|
-
<input value={name} onChange={e => setName(e.target.value)} />
|
|
70
|
-
|
|
71
|
-
# Rip: 1 line per binding
|
|
72
|
-
Dialog open <=> show
|
|
73
|
-
input value <=> @name
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Why Rip UI
|
|
79
|
-
|
|
80
|
-
| | ShadCN / Radix | Rip UI |
|
|
81
|
-
|--|---------------|--------|
|
|
82
|
-
| Runtime dependency | React (~42KB gz) + ReactDOM | None |
|
|
83
|
-
| Component count | ~40 | 54 |
|
|
84
|
-
| Total source | ShadCN wrappers (~3K LOC) atop Radix (~20K+ LOC) | 5,191 SLOC — everything included |
|
|
85
|
-
| Build step | Required (Next.js, Vite, etc.) | None — browser compiles `.rip` source |
|
|
86
|
-
| Styling | Pre-wired Tailwind (ShadCN) or unstyled (Radix) | Headless — `data-*` contract, styled with Tailwind |
|
|
87
|
-
| Controlled components | `value` + `onChange` callback pair | `<=>` two-way binding |
|
|
88
|
-
| Shared state | React Context + Provider wrappers | `offer` / `accept` keywords |
|
|
89
|
-
| Reactivity | `useState` + `useEffect` + dependency arrays | `:=` / `~=` / `~>` — language-level |
|
|
90
|
-
| Virtual DOM | Yes (diffing on every render) | No — fine-grained updates to exact nodes |
|
|
91
|
-
| Data grid | Not included | 901 SLOC — 100K+ rows at 60fps |
|
|
92
|
-
|
|
93
|
-
### Architecture
|
|
94
|
-
|
|
95
|
-
**Fine-grained reactivity.** When `count` changes, only the text node
|
|
96
|
-
displaying `count` updates. No tree diffing, no wasted renders, no
|
|
97
|
-
memoization needed. Same model as SolidJS and Svelte 5's runes, but built
|
|
98
|
-
into the language.
|
|
99
|
-
|
|
100
|
-
**Components compile to JavaScript.** The `component` keyword, `render`
|
|
101
|
-
block, and reactive operators resolve at compile time into ES2022 classes
|
|
102
|
-
with direct DOM operations. Source maps point back to `.rip` source for
|
|
103
|
-
debugging.
|
|
104
|
-
|
|
105
|
-
**No build pipeline.** The browser loads the Rip compiler (~50KB) once and
|
|
106
|
-
compiles `.rip` files on the fly. For production, pre-compile. For
|
|
107
|
-
development, save and see — SSE-based hot reload.
|
|
108
|
-
|
|
109
|
-
**Source as distribution.** Components are served as `.rip` source files.
|
|
110
|
-
Read them, understand them, modify them.
|
|
111
|
-
|
|
112
|
-
### Component Primitives
|
|
113
|
-
|
|
114
|
-
| Capability | React | Rip |
|
|
115
|
-
|-----------|-------|-----|
|
|
116
|
-
| Child projection | No equivalent | `slot` |
|
|
117
|
-
| DOM ownership | Virtual DOM abstraction | Direct DOM + `ref:` |
|
|
118
|
-
| State sharing | Context + Provider wrappers | `offer` / `accept` |
|
|
119
|
-
| Two-way binding | `value` + `onChange` pair | `<=>` operator |
|
|
120
|
-
| Reactivity | Hooks + dependency arrays | `:=` / `~=` / `~>` |
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## Styling with Tailwind
|
|
125
|
-
|
|
126
|
-
Widgets are headless — they ship no CSS. Each widget exposes semantic state
|
|
127
|
-
through `$` attributes that compile to `data-*` in HTML. Style them with
|
|
128
|
-
Tailwind's data attribute variants.
|
|
129
|
-
|
|
130
|
-
### Setup
|
|
131
|
-
|
|
132
|
-
```html
|
|
133
|
-
<script src="https://cdn.tailwindcss.com"></script>
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### The `data-*` Contract
|
|
137
|
-
|
|
138
|
-
Widgets set `data-*` attributes to reflect their state. Tailwind targets
|
|
139
|
-
these with `data-[attr]:` variants:
|
|
140
|
-
|
|
141
|
-
```coffee
|
|
142
|
-
# Widget source — behavior only, zero styling
|
|
143
|
-
button $open: open?!, $disabled: @disabled?!
|
|
144
|
-
div $highlighted: (idx is highlightedIndex)?!
|
|
145
|
-
div $selected: (@value is current)?!
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
```html
|
|
149
|
-
<!-- Your markup — Tailwind classes -->
|
|
150
|
-
<button class="border border-gray-300 rounded-lg px-4 py-2
|
|
151
|
-
data-[open]:border-blue-500 data-[open]:ring-2 data-[open]:ring-blue-200
|
|
152
|
-
data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed">
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Common Patterns
|
|
156
|
-
|
|
157
|
-
**Button:**
|
|
158
|
-
|
|
159
|
-
```html
|
|
160
|
-
<button class="inline-flex items-center gap-2 px-4 py-2 rounded-lg
|
|
161
|
-
bg-blue-600 text-white font-medium
|
|
162
|
-
hover:bg-blue-700 active:scale-[0.98] transition
|
|
163
|
-
data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed">
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Select trigger:**
|
|
167
|
-
|
|
168
|
-
```html
|
|
169
|
-
<button class="inline-flex items-center justify-between w-full px-3 py-2
|
|
170
|
-
border border-gray-300 rounded-lg bg-white
|
|
171
|
-
data-[open]:border-blue-500 data-[open]:ring-2 data-[open]:ring-blue-200">
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
**Select option:**
|
|
175
|
-
|
|
176
|
-
```html
|
|
177
|
-
<div class="px-3 py-2 rounded cursor-pointer
|
|
178
|
-
data-[highlighted]:bg-gray-100
|
|
179
|
-
data-[selected]:font-semibold data-[selected]:text-blue-600">
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
**Dialog overlay:**
|
|
183
|
-
|
|
184
|
-
```html
|
|
185
|
-
<div class="fixed inset-0 bg-black/50 flex items-center justify-center">
|
|
186
|
-
<div class="bg-white rounded-xl shadow-xl p-6 max-w-md w-full">
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### Dark Mode
|
|
190
|
-
|
|
191
|
-
Use Tailwind's `dark:` variant with a class-based toggle:
|
|
192
|
-
|
|
193
|
-
```html
|
|
194
|
-
<html class="dark">
|
|
195
|
-
<!-- dark:bg-gray-900 dark:text-gray-100 etc. -->
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
Or define semantic color tokens in your Tailwind config and reference them
|
|
199
|
-
throughout — `bg-surface`, `text-primary`, `border-muted` — so dark mode
|
|
200
|
-
is a single token swap, not per-element `dark:` classes.
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
## Code Density
|
|
205
|
-
|
|
206
|
-
### Checkbox — 18 Lines
|
|
207
|
-
|
|
208
|
-
```coffee
|
|
209
|
-
export Checkbox = component
|
|
210
|
-
@checked := false
|
|
211
|
-
@disabled := false
|
|
212
|
-
@indeterminate := false
|
|
213
|
-
@switch := false
|
|
214
|
-
|
|
215
|
-
onClick: ->
|
|
216
|
-
return if @disabled
|
|
217
|
-
@indeterminate = false
|
|
218
|
-
@checked = not @checked
|
|
219
|
-
@emit 'change', @checked
|
|
220
|
-
|
|
221
|
-
render
|
|
222
|
-
button role: @switch ? 'switch' : 'checkbox'
|
|
223
|
-
aria-checked: @indeterminate ? 'mixed' : !!@checked
|
|
224
|
-
aria-disabled: @disabled?!
|
|
225
|
-
$checked: @checked?!
|
|
226
|
-
$indeterminate: @indeterminate?!
|
|
227
|
-
$disabled: @disabled?!
|
|
228
|
-
slot
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
Full ARIA. Checkbox and switch modes. Indeterminate state. Data attributes
|
|
232
|
-
for styling. 18 lines, complete.
|
|
233
|
-
|
|
234
|
-
### Dialog — Effect-Based Lifecycle
|
|
235
|
-
|
|
236
|
-
Focus trap, scroll lock, escape dismiss, click-outside dismiss, focus
|
|
237
|
-
restore — all in one reactive effect with automatic cleanup:
|
|
13
|
+
## Import Paths
|
|
238
14
|
|
|
239
15
|
```coffee
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
# lock scroll, trap focus, wire ARIA ...
|
|
244
|
-
return ->
|
|
245
|
-
# cleanup runs automatically when @open becomes false
|
|
16
|
+
import { Select, Dialog, Tabs } from '@rip-lang/ui/browser'
|
|
17
|
+
import { toHTML, Email, Head, Body, Text, Button } from '@rip-lang/ui/email'
|
|
18
|
+
import { compile, inlineEmailTree, generateBrowserCss } from '@rip-lang/ui/tailwind'
|
|
246
19
|
```
|
|
247
20
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
### Grid — 901 Lines
|
|
251
|
-
|
|
252
|
-
No equivalent in ShadCN, Radix, or Headless UI. Virtual scrolling, DOM
|
|
253
|
-
recycling, Sheets-style selection, full keyboard nav, inline editing,
|
|
254
|
-
multi-column sort, column resizing, clipboard (Ctrl+C/V/X as TSV — interop
|
|
255
|
-
with Excel, Google Sheets, Numbers). 901 lines vs 50,000+ for AG Grid.
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Component Overview
|
|
260
|
-
|
|
261
|
-
54 headless components across 10 categories — 5,191 lines total.
|
|
262
|
-
|
|
263
|
-
### Selection
|
|
264
|
-
|
|
265
|
-
| Widget | Description | Key Props | Events |
|
|
266
|
-
|--------|-------------|-----------|--------|
|
|
267
|
-
| **Select** | Dropdown with typeahead, ARIA listbox | `@value`, `@placeholder`, `@disabled` | `@change` |
|
|
268
|
-
| **Combobox** | Filterable input + listbox | `@query`, `@placeholder`, `@disabled` | `@select`, `@filter` |
|
|
269
|
-
| **MultiSelect** | Multi-select with chips and filtering | `@value`, `@query`, `@placeholder` | `@change` |
|
|
270
|
-
| **Autocomplete** | Type to filter, select to fill | `@value`, `@query`, `@placeholder` | `@change` |
|
|
271
|
-
|
|
272
|
-
### Toggle
|
|
273
|
-
|
|
274
|
-
| Widget | Description | Key Props | Events |
|
|
275
|
-
|--------|-------------|-----------|--------|
|
|
276
|
-
| **Checkbox** | Toggle with checkbox/switch semantics | `@checked`, `@disabled`, `@switch` | `@change` |
|
|
277
|
-
| **Toggle** | Two-state toggle button | `@pressed`, `@disabled` | `@change` |
|
|
278
|
-
| **ToggleGroup** | Single or multi-select toggles | `@value`, `@multiple` | `@change` |
|
|
279
|
-
| **RadioGroup** | Exactly one selected, arrow nav | `@value`, `@disabled` | `@change` |
|
|
280
|
-
| **CheckboxGroup** | Multiple checked independently | `@value`, `@disabled` | `@change` |
|
|
281
|
-
|
|
282
|
-
### Input
|
|
283
|
-
|
|
284
|
-
| Widget | Description | Key Props | Events |
|
|
285
|
-
|--------|-------------|-----------|--------|
|
|
286
|
-
| **Input** | Focus, touch, and validation tracking | `@value`, `@type`, `@placeholder` | `@change` |
|
|
287
|
-
| **Textarea** | Auto-resizing text area | `@value`, `@autoResize`, `@rows` | `@change` |
|
|
288
|
-
| **NumberField** | Stepper buttons, hold-to-repeat | `@value`, `@min`, `@max`, `@step` | `@change` |
|
|
289
|
-
| **Slider** | Drag with pointer capture + keyboard | `@value`, `@min`, `@max`, `@step` | `@change` |
|
|
290
|
-
| **OTPField** | Multi-digit code, auto-advance + paste | `@value`, `@length` | `@complete` |
|
|
291
|
-
| **DatePicker** | Calendar dropdown, single or range | `@value`, `@min`, `@max`, `@range` | `@change` |
|
|
292
|
-
| **EditableValue** | Click-to-edit inline value | `@value`, `@placeholder` | `@change` |
|
|
293
|
-
| **NativeSelect** | Styled native `<select>` wrapper | `@value`, `@disabled` | `@change` |
|
|
294
|
-
| **InputGroup** | Input with prefix/suffix addons | `@disabled` | — |
|
|
295
|
-
|
|
296
|
-
### Navigation
|
|
297
|
-
|
|
298
|
-
| Widget | Description | Key Props | Events |
|
|
299
|
-
|--------|-------------|-----------|--------|
|
|
300
|
-
| **Tabs** | Arrow key nav, roving tabindex | `@active`, `@orientation` | `@change` |
|
|
301
|
-
| **Menu** | Dropdown action menu | `@disabled` | `@select` |
|
|
302
|
-
| **ContextMenu** | Right-click context menu | `@disabled` | `@select` |
|
|
303
|
-
| **Menubar** | Horizontal menu bar with dropdowns | — | `@select` |
|
|
304
|
-
| **NavMenu** | Site nav with hover/click panels | — | — |
|
|
305
|
-
| **Toolbar** | Grouped controls, roving tabindex | `@orientation`, `@label` | — |
|
|
306
|
-
| **Breadcrumb** | Navigation trail with separator | `@separator`, `@label` | — |
|
|
307
|
-
|
|
308
|
-
### Overlay
|
|
309
|
-
|
|
310
|
-
| Widget | Description | Key Props | Events |
|
|
311
|
-
|--------|-------------|-----------|--------|
|
|
312
|
-
| **Dialog** | Focus trap, scroll lock, ARIA modal | `@open` | `@close` |
|
|
313
|
-
| **AlertDialog** | Non-dismissable modal | `@open`, `@initialFocus` | `@close` |
|
|
314
|
-
| **Drawer** | Slide-out panel with focus trap | `@open`, `@side` | `@close` |
|
|
315
|
-
| **Popover** | Anchored floating with flip/shift | `@placement`, `@offset` | — |
|
|
316
|
-
| **Tooltip** | Hover/focus with delay | `@text`, `@placement`, `@delay` | — |
|
|
317
|
-
| **PreviewCard** | Hover/focus preview card | `@delay`, `@placement` | — |
|
|
318
|
-
| **Toast** | Auto-dismiss, ARIA live region | `@toast` (object) | `@dismiss` |
|
|
319
|
-
|
|
320
|
-
### Display
|
|
321
|
-
|
|
322
|
-
| Widget | Description | Key Props |
|
|
323
|
-
|--------|-------------|-----------|
|
|
324
|
-
| **Button** | Disabled-but-focusable pattern | `@disabled` |
|
|
325
|
-
| **Badge** | Inline label (solid/outline/subtle) | `@variant` |
|
|
326
|
-
| **Card** | Container with header/content/footer | `@interactive` |
|
|
327
|
-
| **Separator** | Decorative or semantic divider | `@orientation`, `@decorative` |
|
|
328
|
-
| **Progress** | Progress bar via CSS custom prop | `@value`, `@max` |
|
|
329
|
-
| **Meter** | Gauge with thresholds | `@value`, `@min`, `@max`, `@low`, `@high` |
|
|
330
|
-
| **Spinner** | Loading indicator | `@label`, `@size` |
|
|
331
|
-
| **Skeleton** | Loading placeholder with shimmer | `@width`, `@height`, `@circle` |
|
|
332
|
-
| **Avatar** | Image with fallback to initials | `@src`, `@alt`, `@fallback` |
|
|
333
|
-
| **Label** | Accessible form label | `@for`, `@required` |
|
|
334
|
-
| **ScrollArea** | Custom scrollbar, draggable thumb | `@orientation` |
|
|
335
|
-
|
|
336
|
-
### Form
|
|
337
|
-
|
|
338
|
-
| Widget | Description | Key Props |
|
|
339
|
-
|--------|-------------|-----------|
|
|
340
|
-
| **Field** | Label + description + error wrapper | `@label`, `@error`, `@required` |
|
|
341
|
-
| **Fieldset** | Grouped fields with cascading disable | `@legend`, `@disabled` |
|
|
342
|
-
| **Form** | Submit handling + validation state | `@onSubmit` |
|
|
343
|
-
| **ButtonGroup** | Grouped buttons, ARIA semantics | `@orientation`, `@disabled` |
|
|
344
|
-
|
|
345
|
-
### Data
|
|
346
|
-
|
|
347
|
-
| Widget | Description | Key Props |
|
|
348
|
-
|--------|-------------|-----------|
|
|
349
|
-
| **Grid** | Virtual scroll, 100K+ rows at 60fps | `@data`, `@columns`, `@rowHeight` |
|
|
350
|
-
| **Accordion** | Expand/collapse, single or multiple | `@multiple` |
|
|
351
|
-
| **Table** | Semantic table wrapper | `@caption`, `@striped` |
|
|
352
|
-
|
|
353
|
-
### Interactive
|
|
354
|
-
|
|
355
|
-
| Widget | Description | Key Props | Events |
|
|
356
|
-
|--------|-------------|-----------|--------|
|
|
357
|
-
| **Collapsible** | Animated expand/collapse | `@open`, `@disabled` | `@change` |
|
|
358
|
-
| **Pagination** | Page nav with ellipsis gaps | `@page`, `@total`, `@perPage` | `@change` |
|
|
359
|
-
| **Carousel** | Slide with autoplay + loop | `@loop`, `@autoplay`, `@interval` | `@change` |
|
|
360
|
-
| **Resizable** | Draggable resize handles | `@orientation`, `@minSize` | `@resize` |
|
|
361
|
-
|
|
362
|
-
---
|
|
363
|
-
|
|
364
|
-
## Widget Reference
|
|
365
|
-
|
|
366
|
-
### Select
|
|
21
|
+
The root package is intentionally minimal and does not flatten browser + email names into one namespace.
|
|
367
22
|
|
|
368
|
-
|
|
369
|
-
Select value <=> selectedRole, @change: handleChange
|
|
370
|
-
option value: "eng", "Engineer"
|
|
371
|
-
option value: "des", "Designer"
|
|
372
|
-
option value: "mgr", "Manager"
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
**Keyboard:** ArrowDown/Up navigate, Enter/Space select, Escape close, Home/End, type-ahead
|
|
376
|
-
**Data attributes:** `$open`, `$highlighted`, `$selected`, `$disabled`
|
|
377
|
-
|
|
378
|
-
### Combobox
|
|
379
|
-
|
|
380
|
-
```coffee
|
|
381
|
-
Combobox query <=> searchText, @select: handleSelect, @filter: handleFilter
|
|
382
|
-
for item in filteredItems
|
|
383
|
-
div $value: item.id
|
|
384
|
-
span item.name
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
**Keyboard:** ArrowDown/Up navigate, Enter select, Escape close/clear, Tab close
|
|
388
|
-
**Data attributes:** `$open`, `$highlighted`
|
|
389
|
-
|
|
390
|
-
### Dialog
|
|
391
|
-
|
|
392
|
-
```coffee
|
|
393
|
-
Dialog open <=> showDialog, @close: handleClose
|
|
394
|
-
h2 "Confirm Action"
|
|
395
|
-
p "Are you sure?"
|
|
396
|
-
button @click: (=> showDialog = false), "Cancel"
|
|
397
|
-
button @click: handleConfirm, "Confirm"
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
**Keyboard:** Escape to close, Tab trapped within dialog
|
|
401
|
-
**Data attributes:** `$open`
|
|
402
|
-
**Behavior:** Focus trap, body scroll lock, focus restore on close
|
|
403
|
-
|
|
404
|
-
### AlertDialog
|
|
405
|
-
|
|
406
|
-
```coffee
|
|
407
|
-
AlertDialog open <=> showConfirm
|
|
408
|
-
h2 "Delete account?"
|
|
409
|
-
p "This action cannot be undone."
|
|
410
|
-
button @click: (=> showConfirm = false), "Cancel"
|
|
411
|
-
button @click: handleDelete, "Delete"
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
Like Dialog but cannot be closed by Escape or click outside.
|
|
415
|
-
**ARIA:** `role="alertdialog"`, auto-wired `aria-labelledby`/`aria-describedby`
|
|
416
|
-
|
|
417
|
-
### Toast
|
|
418
|
-
|
|
419
|
-
```coffee
|
|
420
|
-
toasts := []
|
|
421
|
-
toasts = [...toasts, { message: "Saved!", type: "success" }]
|
|
422
|
-
toasts = toasts.filter (t) -> t isnt target
|
|
423
|
-
ToastViewport toasts <=> toasts
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
**Props:** `@toasts`, `@placement` (bottom-right, top-right, etc.)
|
|
427
|
-
**Per-toast:** `message`, `type`, `duration` (default 4000ms), `title`, `action`
|
|
428
|
-
**Data attributes:** `$type`, `$leaving`
|
|
429
|
-
**Behavior:** Timer pauses on hover, resumes on leave
|
|
430
|
-
|
|
431
|
-
### Tabs
|
|
432
|
-
|
|
433
|
-
```coffee
|
|
434
|
-
Tabs active <=> currentTab
|
|
435
|
-
div $tab: "one", "Tab One"
|
|
436
|
-
div $tab: "two", "Tab Two"
|
|
437
|
-
div $panel: "one"
|
|
438
|
-
p "Content for tab one"
|
|
439
|
-
div $panel: "two"
|
|
440
|
-
p "Content for tab two"
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
**Keyboard:** ArrowLeft/Right navigate, Home/End jump
|
|
444
|
-
**Data attributes:** `$active`
|
|
445
|
-
|
|
446
|
-
### Accordion
|
|
447
|
-
|
|
448
|
-
```coffee
|
|
449
|
-
Accordion multiple: false
|
|
450
|
-
div $item: "a"
|
|
451
|
-
button $trigger: true, "Section A"
|
|
452
|
-
div $content: true
|
|
453
|
-
p "Content A"
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
**Keyboard:** Enter/Space toggle, ArrowDown/Up between triggers, Home/End
|
|
457
|
-
**Methods:** `toggle(id)`, `isOpen(id)`
|
|
458
|
-
|
|
459
|
-
### Checkbox
|
|
460
|
-
|
|
461
|
-
```coffee
|
|
462
|
-
Checkbox checked <=> isActive, @change: handleChange
|
|
463
|
-
span "Enable notifications"
|
|
464
|
-
|
|
465
|
-
Checkbox checked <=> isDark, switch: true
|
|
466
|
-
span "Dark mode"
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
**ARIA:** `role="checkbox"` or `role="switch"`, `aria-checked` (true/false/mixed)
|
|
470
|
-
**Data attributes:** `$checked`, `$indeterminate`, `$disabled`
|
|
471
|
-
|
|
472
|
-
### Menu
|
|
473
|
-
|
|
474
|
-
```coffee
|
|
475
|
-
Menu @select: handleAction
|
|
476
|
-
button $trigger: true, "Actions"
|
|
477
|
-
div $item: "edit", "Edit"
|
|
478
|
-
div $item: "delete", "Delete"
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
**Keyboard:** ArrowDown/Up navigate, Enter/Space select, Escape close
|
|
482
|
-
**Data attributes:** `$open`, `$highlighted`
|
|
483
|
-
|
|
484
|
-
### Popover
|
|
485
|
-
|
|
486
|
-
```coffee
|
|
487
|
-
Popover placement: "bottom-start"
|
|
488
|
-
button "Options"
|
|
489
|
-
div
|
|
490
|
-
p "Popover content here"
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
**Keyboard:** Enter/Space/ArrowDown toggle, Escape close
|
|
494
|
-
**Data attributes:** `$open`, `$placement`
|
|
495
|
-
|
|
496
|
-
### Tooltip
|
|
497
|
-
|
|
498
|
-
```coffee
|
|
499
|
-
Tooltip text: "Save your changes", placement: "top"
|
|
500
|
-
button "Save"
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
**Data attributes:** `$open`, `$entering`, `$exiting`, `$placement`
|
|
504
|
-
**Behavior:** Shows after delay on hover/focus, uses `aria-describedby`
|
|
505
|
-
|
|
506
|
-
### Grid
|
|
507
|
-
|
|
508
|
-
```coffee
|
|
509
|
-
Grid
|
|
510
|
-
data: employees
|
|
511
|
-
columns: [
|
|
512
|
-
{ key: 'name', title: 'Name', width: 200 }
|
|
513
|
-
{ key: 'age', title: 'Age', width: 80, align: 'right' }
|
|
514
|
-
{ key: 'role', title: 'Role', width: 150, type: 'select', source: roles }
|
|
515
|
-
{ key: 'active', title: 'Active', width: 60, type: 'checkbox' }
|
|
516
|
-
]
|
|
517
|
-
rowHeight: 32
|
|
518
|
-
striped: true
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
**Column types:** `text`, `number`, `checkbox`, `select`
|
|
522
|
-
**Methods:** `getCell`, `setCell`, `getData`, `setData`, `sort`, `scrollToRow`, `copySelection`, `cutSelection`, `pasteAtActive`
|
|
523
|
-
**Keyboard:** Arrows, Tab, Enter/F2 edit, Escape cancel, Ctrl+arrows jump, PageUp/Down, Ctrl+A, Ctrl+C/V/X, Delete, Space (checkboxes), type-to-edit
|
|
524
|
-
**Sorting:** Click header (asc/desc/none), Shift+click for multi-column
|
|
525
|
-
**Clipboard:** TSV format — interop with Excel, Sheets, Numbers
|
|
526
|
-
**Data attributes:** `$active`, `$selected`, `$sorted`, `$editing`, `$selecting`
|
|
527
|
-
|
|
528
|
-
### Collapsible
|
|
529
|
-
|
|
530
|
-
```coffee
|
|
531
|
-
Collapsible open <=> isOpen
|
|
532
|
-
button $trigger: true, "Show details"
|
|
533
|
-
div $content: true
|
|
534
|
-
p "Hidden content here"
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
**Methods:** `toggle()`
|
|
538
|
-
**Data attributes:** `$open`, `$disabled`
|
|
539
|
-
**CSS custom properties:** `--collapsible-height`, `--collapsible-width`
|
|
540
|
-
|
|
541
|
-
### Pagination
|
|
542
|
-
|
|
543
|
-
```coffee
|
|
544
|
-
Pagination page <=> currentPage, total: 100, perPage: 10
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
**Keyboard:** ArrowLeft/Right, Home/End
|
|
548
|
-
**Data attributes:** `$active`, `$disabled`, `$ellipsis`
|
|
549
|
-
|
|
550
|
-
### Carousel
|
|
551
|
-
|
|
552
|
-
```coffee
|
|
553
|
-
Carousel loop: true
|
|
554
|
-
div $slide: true, "Slide 1"
|
|
555
|
-
div $slide: true, "Slide 2"
|
|
556
|
-
div $slide: true, "Slide 3"
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
**Methods:** `goto(index)`, `next()`, `prev()`
|
|
560
|
-
**Behavior:** Autoplay pauses on hover
|
|
561
|
-
|
|
562
|
-
### Drawer
|
|
563
|
-
|
|
564
|
-
```coffee
|
|
565
|
-
Drawer open <=> showDrawer, side: "left"
|
|
566
|
-
nav "Sidebar content"
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
**Props:** `@open`, `@side` (top/right/bottom/left), `@dismissable`
|
|
570
|
-
**Behavior:** Focus trap, scroll lock, Escape to close
|
|
571
|
-
|
|
572
|
-
### Breadcrumb
|
|
573
|
-
|
|
574
|
-
```coffee
|
|
575
|
-
Breadcrumb
|
|
576
|
-
a $item: true, href: "/", "Home"
|
|
577
|
-
a $item: true, href: "/products", "Products"
|
|
578
|
-
span $item: true, "Widget Pro"
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
**ARIA:** `aria-current="page"` on last item
|
|
582
|
-
|
|
583
|
-
### Resizable
|
|
584
|
-
|
|
585
|
-
```coffee
|
|
586
|
-
Resizable
|
|
587
|
-
div $panel: true, "Left"
|
|
588
|
-
div $panel: true, "Right"
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
**ARIA:** `role="separator"` on handles
|
|
592
|
-
**CSS custom properties:** `--panel-size` on each panel
|
|
593
|
-
|
|
594
|
-
### Context Sharing: `offer` / `accept`
|
|
595
|
-
|
|
596
|
-
For compound components where descendants need shared state:
|
|
597
|
-
|
|
598
|
-
```coffee
|
|
599
|
-
# Parent offers reactive state to all descendants
|
|
600
|
-
export Tabs = component
|
|
601
|
-
offer active := 'overview'
|
|
602
|
-
|
|
603
|
-
# Child accepts the shared signal
|
|
604
|
-
export TabContent = component
|
|
605
|
-
accept active
|
|
606
|
-
render
|
|
607
|
-
div hidden: active isnt @value
|
|
608
|
-
slot
|
|
609
|
-
```
|
|
23
|
+
## Domains
|
|
610
24
|
|
|
611
|
-
|
|
612
|
-
direction are instantly visible. No Provider wrappers, no string keys.
|
|
25
|
+
### Browser
|
|
613
26
|
|
|
614
|
-
|
|
27
|
+
Headless, accessible browser widgets authored in Rip. They remain source-first and expose state via `$` / `data-*` attributes for external styling.
|
|
615
28
|
|
|
616
|
-
|
|
29
|
+
### Email
|
|
617
30
|
|
|
618
|
-
|
|
619
|
-
|----------|-------|-------|
|
|
620
|
-
| Selection | 4 | 638 |
|
|
621
|
-
| Toggle | 5 | 267 |
|
|
622
|
-
| Input | 9 | 854 |
|
|
623
|
-
| Navigation | 7 | 767 |
|
|
624
|
-
| Overlay | 7 | 700 |
|
|
625
|
-
| Display | 11 | 378 |
|
|
626
|
-
| Form | 4 | 140 |
|
|
627
|
-
| Data | 3 | 1,041 |
|
|
628
|
-
| Interactive | 4 | 406 |
|
|
629
|
-
| **Total** | **54** | **5,191** |
|
|
31
|
+
Curated PascalCase email components with `toHTML`, `toText`, and `renderEmail` helpers, built on a server-side DOM shim and serializer.
|
|
630
32
|
|
|
631
|
-
|
|
33
|
+
### Shared
|
|
632
34
|
|
|
633
|
-
|
|
35
|
+
Cross-domain style and utility helpers such as `joinStyles()` and `withMargin()`.
|
|
634
36
|
|
|
635
|
-
|
|
636
|
-
proven. The compiler has 1,436 tests. The widget suite is comprehensive
|
|
637
|
-
but still maturing — tests are being added, and a few widgets have known
|
|
638
|
-
structural issues being resolved (see [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
639
|
-
for details).
|
|
37
|
+
### Tailwind
|
|
640
38
|
|
|
641
|
-
|
|
642
|
-
development roadmap, see **[CONTRIBUTING.md](CONTRIBUTING.md)**.
|
|
39
|
+
The one dependency-bearing domain. It invokes the real Tailwind compiler and provides CSS generation for browser use and CSS injection/inlining helpers for email.
|