htmlboard 0.5.0 โ 0.7.0
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/README.md +29 -9
- package/bin/htmlboard.js +24 -4
- package/package.json +1 -1
- package/skills/htmlboard/SKILL.md +21 -60
- package/skills/htmlboard-components/SKILL.md +36 -0
- package/skills/htmlboard-designs-json/SKILL.md +25 -0
- package/src/canvases.js +38 -0
- package/src/check.js +1 -31
- package/src/client/explorer.css +173 -2
- package/src/client/explorer.js +436 -36
- package/src/client/index.html +33 -0
- package/src/client/picture.js +281 -0
- package/src/designs.js +44 -8
- package/src/migrate.js +2 -2
- package/src/server.js +1 -1
- package/src/skills.js +2 -1
package/README.md
CHANGED
|
@@ -39,6 +39,8 @@ Modern AI agents and developers generate dozens of HTML mockups, layout variatio
|
|
|
39
39
|
- ๐ **Optional `designs.json`**: Curates when you want it (section titles, order, overrides); designs it doesn't list still show at the end, or don't (`"unlisted": "hide"`). `htmlboard migrate` moves an existing one into the files.
|
|
40
40
|
- ๐ฑ **Device Filters**: **Mobile**, **Tablet** and **Desktop** (and **Components**, or your own categories) lay out just those designs, side by side, to compare them.
|
|
41
41
|
- ๐ **Search**: Find designs by name, file, tags or section (case and accents don't matter); the others fade out in place.
|
|
42
|
+
- ๐๏ธ **Sections You Can Fold and Hide**: Fold a section in the list, or hide it from the canvas: its designs unload, so a canvas of hundreds of designs stays light. <kbd>Alt</kbd> + click its eye to see only that one; the list remembers what you folded and hid.
|
|
43
|
+
- ๐ธ **Copy or Save as an Image**: Hover a design for two buttons: copy it to the clipboard as a PNG of its full size (twice that on a Retina screen), or save it in a folder you choose once. <kbd>C</kbd> and <kbd>S</kbd> do the same for the selected design.
|
|
42
44
|
- ๐ฏ **Focus & Zoom-to-Fit**: Click a design in the list or double-click it on the canvas to fit it, <kbd>Shift</kbd> + <kbd>1</kbd> fits everything, <kbd>โ</kbd> <kbd>โ</kbd> step through the designs.
|
|
43
45
|
- ๐ **Deep Links**: The URL follows the selected design (`http://localhost:3000/#checkout-b`), so you can share or reload a view of one design.
|
|
44
46
|
- โจ๏ธ **Keyboard-First Workflow**: Figma-like shortcuts; <kbd>?</kbd> lists them.
|
|
@@ -93,6 +95,8 @@ Options:
|
|
|
93
95
|
-h, --help Display help message
|
|
94
96
|
```
|
|
95
97
|
|
|
98
|
+
Running `htmlboard` for a folder it already shows (same folder and root, same version) doesn't start a second canvas on the next port: it prints the running one's URL, and opens it with `--open`.
|
|
99
|
+
|
|
96
100
|
### Examples
|
|
97
101
|
|
|
98
102
|
```bash
|
|
@@ -131,31 +135,39 @@ htmlboard migrate design --write
|
|
|
131
135
|
|
|
132
136
|
HtmlBoard is built from the ground up to pair with AI coding agents (Claude, Gemini, Codex, ChatGPT, Antigravity, Cursor).
|
|
133
137
|
|
|
134
|
-
### Install the agent
|
|
138
|
+
### Install the agent skills
|
|
139
|
+
|
|
140
|
+
HtmlBoard ships [Agent Skills](https://agentskills.io) that teach your agent how to use it. An agent keeps only each skill's one-line description in mind and reads a skill when a task calls for it, so the ones it rarely needs cost almost nothing until then:
|
|
135
141
|
|
|
136
|
-
|
|
142
|
+
| Skill | What it teaches | Read |
|
|
143
|
+
| :--- | :--- | :--- |
|
|
144
|
+
| `htmlboard` | Start the canvas; write, size, name and group designs; shared styles; size and state variants; the catalog and the check | For any design work |
|
|
145
|
+
| `htmlboard-components` | The component file: attributes, slots, scoped CSS, a script, its first-line example | Before writing or changing a component |
|
|
146
|
+
| `htmlboard-designs-json` | Curating the canvas with `designs.json`, and `htmlboard migrate` | When the designs folder has a `designs.json` |
|
|
147
|
+
|
|
148
|
+
Install them in your project:
|
|
137
149
|
|
|
138
150
|
```bash
|
|
139
151
|
npx htmlboard skills install
|
|
140
152
|
```
|
|
141
153
|
|
|
142
|
-
This writes `.claude/skills
|
|
154
|
+
This writes them in `.claude/skills/` (Claude Code) and `.agents/skills/` (Codex and other agents that read `.agents/skills`). Commit them so everyone working on the project gets them. Then start a new agent session and just ask for a design.
|
|
143
155
|
|
|
144
156
|
```bash
|
|
145
157
|
npx htmlboard skills install --global # for your user (~/.claude/skills, ~/.agents/skills), all projects
|
|
146
158
|
npx htmlboard skills install --agent claude # only one agent: claude | agents | all (default)
|
|
147
159
|
npx htmlboard skills install --force # update a copy you edited locally
|
|
148
|
-
npx htmlboard skills uninstall # remove
|
|
160
|
+
npx htmlboard skills uninstall # remove them (accepts the same --global / --agent)
|
|
149
161
|
```
|
|
150
162
|
|
|
151
|
-
Running `install` again after upgrading HtmlBoard updates the
|
|
163
|
+
Running `install` again after upgrading HtmlBoard updates the skills; a copy you edited is kept unless you pass `--force`.
|
|
152
164
|
|
|
153
165
|
### The workflow
|
|
154
166
|
|
|
155
167
|
1. **Ask your LLM to generate artboards:**
|
|
156
168
|
> *"Create 3 design explorations for our checkout flow in `designs/03-checkout/`."*
|
|
157
169
|
|
|
158
|
-
With the
|
|
170
|
+
With the skills, the agent writes `01-one-page.html`, `02-steps.html`, `03-express.html`, each with its size in a `<meta>` tag. They show up as a new "checkout" row, in that order.
|
|
159
171
|
|
|
160
172
|
2. **Run HtmlBoard in the background:**
|
|
161
173
|
```bash
|
|
@@ -230,7 +242,8 @@ Checked 6 designs in designs (the canvas at http://localhost:3000): 2 with probl
|
|
|
230
242
|
|
|
231
243
|
Every `.html` file under the folder is a design (hidden folders, `node_modules`, `dist`, `build` and the [components](#-components) in `components/` aside):
|
|
232
244
|
|
|
233
|
-
- **Sections are folders.** Each folder is a row on the canvas, named after the folder; the files at the top of the folder make a first row without a title.
|
|
245
|
+
- **Sections are folders.** Each folder is a row on the canvas, named after the folder; the files at the top of the folder make a first row without a title ("Top level" in the list).
|
|
246
|
+
- **In the list, a section folds** under its title, and **its eye hides it from the canvas**: its designs unload and the rows below move up, which keeps a canvas of many designs light. <kbd>Alt</kbd> + click an eye shows only that section (again: all of them); the buttons above the sections fold, unfold, show or hide them all. A hidden section's designs stay in the list, faded, and the search still finds them: clicking one (or a link to it) shows its section. The list remembers what you folded and hid, for each project.
|
|
234
247
|
- **Number prefixes set the order** of files and folders: `01-โฆ`, `02-โฆ`, `10-โฆ` (up to three digits; `2` comes before `10`). They don't show in names or in links, so reordering breaks nothing.
|
|
235
248
|
- **A file describes itself** in its `<head>`:
|
|
236
249
|
|
|
@@ -240,6 +253,7 @@ Every `.html` file under the folder is a design (hidden folders, `node_modules`,
|
|
|
240
253
|
<meta name="htmlboard" content="width=390, height=844"> <!-- its size (and category=โฆ) -->
|
|
241
254
|
<meta name="htmlboard:label" content="light"> <!-- added to its name: "Checkout ยท light" -->
|
|
242
255
|
<meta name="htmlboard:variant" content="?theme=dark ยท dark"> <!-- one more frame: "Checkout ยท dark" -->
|
|
256
|
+
<meta name="htmlboard:variant" content="width=768, height=1024 ยท tablet"> <!-- and at a tablet's size -->
|
|
243
257
|
```
|
|
244
258
|
|
|
245
259
|
| Tag | What it says |
|
|
@@ -248,7 +262,7 @@ Every `.html` file under the folder is a design (hidden folders, `node_modules`,
|
|
|
248
262
|
| `<meta name="description">` | Its description, shown in the list and found by the search. |
|
|
249
263
|
| `<meta name="htmlboard" content="โฆ">` | `width=โฆ`, `height=โฆ`, `category=โฆ` (`mobile`, `tablet`, `desktop`, `components` or your own word), or `skip` to leave the file out (partials, templates). |
|
|
250
264
|
| `<meta name="htmlboard:label">` | Text added to the name of the file's own frame, when it has variants. |
|
|
251
|
-
| `<meta name="htmlboard:variant">` | One more frame of the same file with
|
|
265
|
+
| `<meta name="htmlboard:variant">` | One more frame of the same file: with a query string (`?theme=dark`), at another size (`width=390, height=844`, so a responsive page shows on a phone too), or both; then its label. As many as you like; they follow the file. |
|
|
252
266
|
|
|
253
267
|
**Sizes.** The width is the frame's viewport: the page's CSS needs it before it renders. Give the height for designs of a fixed size (a phone screen, a flyer: the frame is that tall and cuts the rest); leave it out and the frame grows with the page. Without a size, words in the path decide (`mobile` โ 390 ร 844, `tablet` โ 768 ร 1024, `desktop` โ 1280 ร 800, `component` โ 720 ร 600), else 1200 wide.
|
|
254
268
|
|
|
@@ -355,6 +369,7 @@ It writes each file's size, category, label, variants and description as tags af
|
|
|
355
369
|
| <kbd>Shift</kbd> + <kbd>2</kbd>, double-click | Fit the design |
|
|
356
370
|
| <kbd>โ</kbd> / <kbd>โ</kbd> | Previous / next design |
|
|
357
371
|
| <kbd>Enter</kbd> | Open the selected design on its own |
|
|
372
|
+
| <kbd>C</kbd> / <kbd>S</kbd> | Copy / save the selected design as an image (<kbd>Shift</kbd> + <kbd>S</kbd>: choose the folder) |
|
|
358
373
|
| <kbd>/</kbd> | Search (<kbd>Enter</kbd> fits the first match, <kbd>Esc</kbd> clears) |
|
|
359
374
|
| <kbd>1</kbd> โ <kbd>4</kbd> | Show all, mobile, tablet, desktop (<kbd>5</kbd> on: the other categories) |
|
|
360
375
|
| <kbd>R</kbd> | Reload the designs |
|
|
@@ -364,6 +379,8 @@ It writes each file's size, category, label, variants and description as tags af
|
|
|
364
379
|
|
|
365
380
|
On the canvas the designs are pictures: the canvas takes every click and scroll, so <kbd>Ctrl</kbd> + scroll never zooms the page itself. Use **Open** (or <kbd>Enter</kbd>) to try a design's links and hover states on its own.
|
|
366
381
|
|
|
382
|
+
**Images of a design.** Hovering a design shows two buttons over its top right corner. **Copy** puts it in the clipboard as a PNG, to paste anywhere; **Save** writes it in a folder you pick the first time (the canvas remembers it for the project, <kbd>Shift</kbd> + click picks another), as `<design id>.png`, then `-2`, `-3`โฆ so it never replaces an image. Browsers without folder access (Firefox, Safari) download it instead. Your browser draws the image from the page as its frame shows it, with its stylesheets, fonts and images, at the frame's size and your screen's pixel density. A few things can't be in it: images from other sites that don't allow it, pages inside the page (iframes), and shadow DOM.
|
|
383
|
+
|
|
367
384
|
---
|
|
368
385
|
|
|
369
386
|
## ๐ Programmatic API
|
|
@@ -456,6 +473,7 @@ htmlboard/
|
|
|
456
473
|
โ โ โโโ explorer.css # Dark chrome, dot grid
|
|
457
474
|
โ โ โโโ explorer.js # Layout, pan/zoom, search, live reload
|
|
458
475
|
โ โ โโโ check.js # Checks the designs in frames out of sight, for `htmlboard check`
|
|
476
|
+
โ โ โโโ picture.js # A design as an image, drawn by the browser (copy, save)
|
|
459
477
|
โ โ โโโ probe.js # Notes script errors and failed loads in the frames the check loads
|
|
460
478
|
โ โ โโโ favicon.svg, logo.svg, mark.svg
|
|
461
479
|
โ โ โโโ fonts/ # Inter and Bricolage Grotesque (OFL)
|
|
@@ -466,7 +484,9 @@ htmlboard/
|
|
|
466
484
|
โ โโโ 01-screens/ # Dashboard, customers, mobile feed
|
|
467
485
|
โ โโโ 02-ui-kit/
|
|
468
486
|
โโโ skills/
|
|
469
|
-
โ
|
|
487
|
+
โ โโโ htmlboard/ # Agent skills (SKILL.md each) copied by `htmlboard skills install`
|
|
488
|
+
โ โโโ htmlboard-components/
|
|
489
|
+
โ โโโ htmlboard-designs-json/
|
|
470
490
|
โโโ brand/ # The logo's files (from design/tools/logo.py)
|
|
471
491
|
โโโ design/ # htmlboard's own designs, on its own canvas: `npx htmlboard design --root .`
|
|
472
492
|
โโโ package.json
|
package/bin/htmlboard.js
CHANGED
|
@@ -10,6 +10,7 @@ import { planMigration, checkMigration, applyMigration } from '../src/migrate.js
|
|
|
10
10
|
import { installComponents, COMPONENTS_FOLDER, COMPONENTS_SCRIPT } from '../src/components.js';
|
|
11
11
|
import { buildCatalog, formatCatalog } from '../src/catalog.js';
|
|
12
12
|
import { runCheck, formatReport, hasProblems, CheckError } from '../src/check.js';
|
|
13
|
+
import { PORTS, findCanvases } from '../src/canvases.js';
|
|
13
14
|
|
|
14
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
16
|
const __dirname = path.dirname(__filename);
|
|
@@ -99,7 +100,7 @@ Examples:
|
|
|
99
100
|
htmlboard ./designs -p 8080 # Serve ./designs on port 8080
|
|
100
101
|
htmlboard design --root . # Artboards in ./design that link to ../css/โฆ
|
|
101
102
|
htmlboard --init # Add sample designs to the current folder
|
|
102
|
-
htmlboard skills install # Add the HtmlBoard
|
|
103
|
+
htmlboard skills install # Add the HtmlBoard skills to this project's agents
|
|
103
104
|
htmlboard components designs # Let ./designs use components (components/tx-item.html is <tx-item>)
|
|
104
105
|
htmlboard catalog designs # What ./designs share, in a few lines (for your AI agent)
|
|
105
106
|
htmlboard check designs # Script errors, missing files, cut-off text in ./designs
|
|
@@ -154,8 +155,8 @@ function scaffoldProject(targetDir) {
|
|
|
154
155
|
function showSkillsHelp() {
|
|
155
156
|
console.log(`
|
|
156
157
|
Usage:
|
|
157
|
-
htmlboard skills install [options] Copy the HtmlBoard
|
|
158
|
-
htmlboard skills uninstall [options] Remove
|
|
158
|
+
htmlboard skills install [options] Copy the HtmlBoard skills into your agents' skill folders
|
|
159
|
+
htmlboard skills uninstall [options] Remove them
|
|
159
160
|
|
|
160
161
|
Options:
|
|
161
162
|
-g, --global Install for your user (~/.claude/skills, ~/.agents/skills) instead of this project
|
|
@@ -211,7 +212,7 @@ function runSkills(args) {
|
|
|
211
212
|
console.log(` ${labels[status]} ${path.relative(process.cwd(), dest) || dest}`);
|
|
212
213
|
}
|
|
213
214
|
if (action === 'install' && results.some((r) => r.status === 'installed' || r.status === 'updated')) {
|
|
214
|
-
console.log(`\n Restart your agent session to load the
|
|
215
|
+
console.log(`\n Restart your agent session to load the skills, then ask it for a design.`);
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
218
|
|
|
@@ -453,6 +454,25 @@ async function main() {
|
|
|
453
454
|
scaffoldProject(targetDir);
|
|
454
455
|
}
|
|
455
456
|
|
|
457
|
+
// A canvas already running for this folder (same root, same version): say where, rather than
|
|
458
|
+
// starting a second one on the next port
|
|
459
|
+
const root = options.root ? path.resolve(options.root) : targetDir;
|
|
460
|
+
const running = (await findCanvases([...PORTS, ...Array.from({ length: 21 }, (_, i) => options.port + i)]))
|
|
461
|
+
.filter((canvas) => canvas.dir === targetDir && canvas.root === root);
|
|
462
|
+
const same = running.find((canvas) => canvas.version === pkg.version);
|
|
463
|
+
if (same) {
|
|
464
|
+
console.log(`
|
|
465
|
+
\x1b[90mhtml\x1b[0m\x1b[1mboard\x1b[0m is already running for this folder:
|
|
466
|
+
|
|
467
|
+
\x1b[32mโ\x1b[0m \x1b[1mLocal:\x1b[0m \x1b[36m${same.url}\x1b[0m
|
|
468
|
+
`);
|
|
469
|
+
if (options.open) openBrowser(same.url);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
for (const canvas of running) {
|
|
473
|
+
console.log(`[htmlboard] v${canvas.version} is running for this folder at ${canvas.url}; starting v${pkg.version} too.`);
|
|
474
|
+
}
|
|
475
|
+
|
|
456
476
|
try {
|
|
457
477
|
const { url, rootDir } = await startServer({
|
|
458
478
|
dir: targetDir,
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: htmlboard
|
|
3
|
-
description: Design UI in plain HTML files and show them side by side on an infinite, live-reloading canvas with the htmlboard CLI. Use when the user asks for mockups, screens, design variants or explorations,
|
|
3
|
+
description: Design UI in plain HTML files and show them side by side on an infinite, live-reloading canvas with the htmlboard CLI. Use when the user asks for mockups, screens, design variants or explorations, flyers or any visual proposal, or mentions htmlboard or artboards.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# HtmlBoard
|
|
7
7
|
|
|
8
8
|
HtmlBoard serves a folder of HTML files as artboards on a zoomable canvas, like a Figma page. Every design is one ordinary HTML file that says its own name and size, so you design by writing HTML and CSS, and the user sees the result update live while you work.
|
|
9
9
|
|
|
10
|
+
Two more skills cover the rest when you need it: **htmlboard-components** to write or change a component, **htmlboard-designs-json** when the designs folder has a `designs.json`.
|
|
11
|
+
|
|
10
12
|
## Start the canvas
|
|
11
13
|
|
|
12
14
|
Run it in the background, pointed at the designs folder (it is created if missing):
|
|
@@ -19,7 +21,7 @@ npx htmlboard designs --open
|
|
|
19
21
|
- `-p <port>` picks the port. `--init` adds sample designs; skip it unless the user wants a starter.
|
|
20
22
|
- If the artboards link to files above the designs folder (`../css/theme.css`), serve from the project root: `npx htmlboard design --root .`
|
|
21
23
|
- An empty folder gets the samples automatically, so write your first artboard before starting the server.
|
|
22
|
-
-
|
|
24
|
+
- Running it again for a folder that already has a canvas doesn't start another: it prints that canvas's URL.
|
|
23
25
|
|
|
24
26
|
## Write an artboard
|
|
25
27
|
|
|
@@ -48,87 +50,46 @@ One design per file, with its facts in the `<head>`, linking the shared styleshe
|
|
|
48
50
|
- Always give the `width`: it is the frame's viewport, and the page's CSS needs it. Give the `height` for fixed sizes (a phone screen, a flyer: the frame cuts at it); leave it out for pages that should grow with their content.
|
|
49
51
|
- Design at the real size of the target: phone 390ร844, tablet 768ร1024, desktop 1280ร800 (or 1440ร900), and the exact size for print or social formats (e.g. 1080ร1350).
|
|
50
52
|
- Use real content in the user's language, not lorem ipsum.
|
|
51
|
-
- Relative links to CSS, images, scripts and fonts work (`assets/logo.svg`; `../css/theme.css` with `--root`).
|
|
52
|
-
|
|
53
|
+
- Relative links to CSS, images, scripts and fonts work (`assets/logo.svg`; `../css/theme.css` with `--root`). No build step: plain HTML, CSS and browser JavaScript (ES modules and CDN scripts work).
|
|
54
|
+
|
|
55
|
+
More tags, all optional:
|
|
56
|
+
|
|
57
|
+
- `<meta name="htmlboard:variant" content="?theme=dark ยท dark">` shows the same file again with that query string, named "<title> ยท dark": for states the page builds from its URL (themes, empty or error states, data). A variant can also change the size: `content="width=390, height=844 ยท mobile"` shows a responsive page at a phone's size too, from the same file (`?theme=dark width=390 ยท dark mobile` does both). One tag per variant; they follow the file.
|
|
58
|
+
- `<meta name="htmlboard:label" content="light">` adds a label to the name of the file's own frame ("Checkout ยท light"), to tell it from its variants.
|
|
59
|
+
- `category=โฆ` in the `htmlboard` tag sets the explorer's filter (`mobile`, `components`, your own word) when the path and size don't say it.
|
|
60
|
+
- `<meta name="htmlboard" content="skip">` leaves a file out (partials, templates, a page that isn't a design).
|
|
53
61
|
|
|
54
62
|
## Shared styles
|
|
55
63
|
|
|
56
64
|
Put what the designs share in one stylesheet instead of repeating it in every file: it keeps the designs consistent and spares writing the same CSS again.
|
|
57
65
|
|
|
58
66
|
- The designs folder has a `styles.css` at its top (sample projects start with one). Before writing a design, see what the designs already share: `npx htmlboard catalog <designs folder>` prints it in a few lines, read from the files (each stylesheet's variables and classes, the scripts, the components with their attributes and an example). Use what fits (`var(--accent)`, `.btn`, `<tx-item>`) and add what's missing; open a file only for what the list doesn't say.
|
|
59
|
-
- If there is none and the project already has a stylesheet or design tokens, link those. Otherwise create `styles.css` with your first design: colors, type and spacing as custom properties, the base styles, and the
|
|
67
|
+
- If there is none and the project already has a stylesheet or design tokens, link those. Otherwise create `styles.css` with your first design: colors, type and spacing as custom properties, the base styles, and the classes you'll repeat.
|
|
60
68
|
- Link it with a relative path: `styles.css` from the top of the folder, `../styles.css` from a section folder.
|
|
61
|
-
- A design's own `<style>` holds only what is specific to it. When a second design needs the same thing, move it to `styles.css`.
|
|
62
|
-
- Split it when it grows (`forms.css`, `charts.css`) and link the extra files where they are used.
|
|
69
|
+
- A design's own `<style>` holds only what is specific to it. When a second design needs the same thing, move it to `styles.css`. Split it when it grows (`forms.css`, `charts.css`) and link the extra files where they are used.
|
|
63
70
|
- Editing `styles.css` restyles every design that links it, which is the point for a product's screens. For one-off explorations that should keep their look (a different style, logo options), style them in their own file instead.
|
|
64
71
|
|
|
65
|
-
More tags, all optional:
|
|
66
|
-
|
|
67
|
-
- `category=โฆ` in the `htmlboard` tag files the design under the explorer's filters: `mobile`, `tablet`, `desktop`, `components` or your own word. Usually not needed: it comes from words in the path, then from the size when it is a screen's.
|
|
68
|
-
- `<meta name="htmlboard:variant" content="?theme=dark ยท dark">` shows the same file again with that query string, named "<title> ยท dark". Use it for states the page builds from its URL (themes, empty or error states, data). Add one tag per variant; they follow the file.
|
|
69
|
-
- `<meta name="htmlboard:label" content="light">` adds a label to the name of the file's own frame ("Checkout ยท light"), to tell it from its variants.
|
|
70
|
-
- `<meta name="htmlboard" content="skip">` leaves a file out (partials, templates, a page that isn't a design).
|
|
71
|
-
|
|
72
72
|
## Components
|
|
73
73
|
|
|
74
|
-
Markup that designs repeat (a sidebar, a tab bar, a list row, a card with content) goes in a component,
|
|
75
|
-
|
|
76
|
-
- `components/tx-item.html`, at the top of the designs folder, is `<tx-item>`: the file name is the tag, with a hyphen and no number prefix. Designs load `components.js` once (above). If the folder has no `components.js`, run `npx htmlboard components <designs folder>`.
|
|
77
|
-
- The catalog (above) lists them, with their attributes, an example and the designs that use them. Use them; don't copy their markup into designs.
|
|
78
|
-
- Make one when a second design repeats a block (or a design repeats it three times). Not for what a `styles.css` class already does (`<button class="btn btn-primary">` is short), nor in one-off explorations.
|
|
79
|
-
|
|
80
|
-
A component file is a piece of HTML (no `<!DOCTYPE>`, `<html>`, `<head>` or `<body>`):
|
|
81
|
-
|
|
82
|
-
```html
|
|
83
|
-
<!-- <tx-item icon="๐ผ" name="Invoice #1042" meta="Yesterday" amount="+$3,450.00" positive></tx-item> -->
|
|
84
|
-
<div class="icon">{{icon}}</div>
|
|
85
|
-
<div class="what"><b>{{name}}</b> <span class="muted">{{meta}}</span></div>
|
|
86
|
-
<div class="amount">{{amount}}</div>
|
|
87
|
-
|
|
88
|
-
<style>
|
|
89
|
-
& { display: flex; align-items: center; gap: 14px; } /* & is the element itself */
|
|
90
|
-
.icon { width: 40px; height: 40px; border-radius: 12px; background: var(--surface-2); }
|
|
91
|
-
&[positive] .amount { color: var(--emerald); }
|
|
92
|
-
</style>
|
|
93
|
-
```
|
|
74
|
+
Markup that designs repeat (a sidebar, a tab bar, a list row, a card with content) goes in a component, a file in `components/` that designs use as one tag instead of the block: `<tx-item icon="โ" name="Blue Bottle" amount="-$6.75"></tx-item>`.
|
|
94
75
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
- No logic in the markup: variants are attributes styled in CSS (`&[positive]`), and a list is several elements.
|
|
98
|
-
- Its `<style>` only reaches inside the element, which is a block box unless `&` says otherwise. `styles.css` applies inside too: build with its classes (`card`, `btn`, `muted`) and name the component's own parts with names it doesn't use. A design's own `<style>` comes after and can restyle one element, but its selectors reach inside components too, so keep them specific.
|
|
99
|
-
- `<script>` runs for each element once it is built, with `this` as the element. Use it sparingly, a few lines for what markup and CSS can't do, like marking the current link (`<app-sidebar active="Customers">`) or a toggle. No fetching, data or frameworks.
|
|
100
|
-
- Relative links in a component start from its file (`../01-screens/02-customers.html`).
|
|
101
|
-
- Close the tag: `<tx-item โฆ></tx-item>`, never `<tx-item />`. A component can't be a table's row or cell itself (the browser moves it out of the `<table>`), but it works inside a cell: `<td><org-cell โฆ></org-cell></td>`. Where it sits (fixed, margins, the grid around it) belongs to the design.
|
|
102
|
-
- Editing a component changes every design that uses it, like `styles.css`.
|
|
76
|
+
- The catalog lists the project's components with their attributes, an example and the designs that use them. Use them; don't copy their markup into designs.
|
|
77
|
+
- Make one when a second design repeats a block (or a design repeats it three times); not for what a `styles.css` class already does, nor in one-off explorations. To write or change one, use the htmlboard-components skill.
|
|
103
78
|
|
|
104
79
|
## Group and order
|
|
105
80
|
|
|
106
81
|
- **A folder is a section** (a row on the canvas), named after the folder. Put related designs in a folder: `designs/03-checkout/`.
|
|
107
82
|
- **Number prefixes set the order** of files and folders: `01-one-page.html`, `02-steps.html`. They don't show in names or links, so you can renumber freely. Files at the top of the designs folder form a first row without a title.
|
|
108
83
|
- New files and folders show up on the canvas by themselves; you don't register them anywhere.
|
|
109
|
-
|
|
110
|
-
**`designs.json`** is optional. When a project has one (in the designs folder), it curates: sections with titles (accents, capitals, an order across folders), and values that win over the files'. Designs it doesn't list still show after the ones it lists, in their folder's section, unless it says `"unlisted": "hide"`. If the project's designs.json groups designs by hand, add a new design to the right section there:
|
|
111
|
-
|
|
112
|
-
```json
|
|
113
|
-
{
|
|
114
|
-
"sections": [
|
|
115
|
-
{ "title": "Checkout ยท mobile", "designs": ["03-checkout/01-one-page.html", "03-checkout/02-steps.html"] },
|
|
116
|
-
{ "title": "Launch flyers", "folder": "04-flyers" }
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
An entry is a file name (its variants follow it), or `{ "file", "title", "width", "height", "category", "description" }` to override the file. `"folder"` brings a whole folder. Components aren't on the canvas unless designs.json says `"components": "show"` (or lists `components/<tag>.html`): each shows alone, from its first-line example, 480 wide unless its file has `<meta name="htmlboard" content="width=โฆ">`. `npx htmlboard migrate <folder>` moves an old designs.json's sizes, names and variants into the files (`--write` to apply; without it, it only shows the plan).
|
|
84
|
+
- A `designs.json` in the designs folder, when there is one, curates the canvas (section titles, an order across folders, designs it hides): use the htmlboard-designs-json skill before adding, moving or renaming designs there.
|
|
122
85
|
|
|
123
86
|
## Live reload
|
|
124
87
|
|
|
125
|
-
|
|
126
|
-
- Saving a file artboards use (a stylesheet, a component, a script, an image, data) reloads the artboards that loaded it.
|
|
127
|
-
- Adding, removing or moving artboards, or saving `designs.json`, rebuilds the canvas.
|
|
88
|
+
Saving a design reloads its frame, saving a file designs use (a stylesheet, a component, an image) reloads the designs that loaded it, and new or removed designs show up by themselves. The user's view stays put: there is nothing to refresh.
|
|
128
89
|
|
|
129
90
|
## Working with the user
|
|
130
91
|
|
|
131
92
|
- Put variants in separate files next to each other (`01-hero-a.html`, `02-hero-b.html`) instead of overwriting the current design, so they can be compared on the canvas. Keep the current one as the reference until the user picks.
|
|
132
|
-
- Say which files you created or changed and what to look at; the user is watching the canvas, not the code. Link straight to
|
|
133
|
-
- Before reporting back, run `npx htmlboard check <designs folder>` (or a design's file)
|
|
93
|
+
- Say which files you created or changed and what to look at; the user is watching the canvas, not the code. Link straight to a design with its file after the `#`: `http://localhost:3000/#03-checkout/02-steps.html`.
|
|
94
|
+
- Before reporting back, run `npx htmlboard check <designs folder>` (or a design's file): the canvas open in the user's browser loads each design again and lists what went wrong (script errors, missing files and components, fonts that didn't load, content cut at the frame's edge, text cut off or spilling out). Fix it and run it again. A note about content below a fixed-height frame is fine for a screen that scrolls, not for a flyer. If it says the canvas isn't open, ask the user to open its URL.
|
|
134
95
|
- The check can't judge looks (colors, spacing, alignment). If you can take screenshots, also look at the design at its real size (`http://localhost:3000/<file>`).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: htmlboard-components
|
|
3
|
+
description: Write or change an htmlboard component, a file in components/ that designs use as a custom element (components/tx-item.html is <tx-item>). Use when a block repeats across htmlboard designs, or before creating or editing a file in components/.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# HtmlBoard components
|
|
7
|
+
|
|
8
|
+
A component is markup that designs repeat (a sidebar, a tab bar, a list row, a card with content), written once and used as one tag: `<tx-item icon="โ" name="Blue Bottle" amount="-$6.75"></tx-item>`. The htmlboard skill covers the designs themselves.
|
|
9
|
+
|
|
10
|
+
- `components/tx-item.html`, at the top of the designs folder, is `<tx-item>`: the file name is the tag, with a hyphen and no number prefix.
|
|
11
|
+
- Designs load `components.js` once, after their stylesheet: `<script type="module" src="../components.js"></script>`. If the designs folder has no `components.js`, run `npx htmlboard components <designs folder>`.
|
|
12
|
+
- See what exists first: `npx htmlboard catalog <designs folder>` lists the components with their attributes, an example and the designs that use them.
|
|
13
|
+
|
|
14
|
+
A component file is a piece of HTML (no `<!DOCTYPE>`, `<html>`, `<head>` or `<body>`):
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<!-- <tx-item icon="๐ผ" name="Invoice #1042" meta="Yesterday" amount="+$3,450.00" positive></tx-item> -->
|
|
18
|
+
<div class="icon">{{icon}}</div>
|
|
19
|
+
<div class="what"><b>{{name}}</b> <span class="muted">{{meta}}</span></div>
|
|
20
|
+
<div class="amount">{{amount}}</div>
|
|
21
|
+
|
|
22
|
+
<style>
|
|
23
|
+
& { display: flex; align-items: center; gap: 14px; } /* & is the element itself */
|
|
24
|
+
.icon { width: 40px; height: 40px; border-radius: 12px; background: var(--surface-2); }
|
|
25
|
+
&[positive] .amount { color: var(--emerald); }
|
|
26
|
+
</style>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- First line: a comment with an example of use, with real values, every attribute and its variants side by side. The catalog shows it, and it is the component's preview on the canvas.
|
|
30
|
+
- `{{name}}` is the element's `name="โฆ"` attribute, as text. Content goes in slots: `<slot></slot>` takes the element's children, `<slot name="actions"></slot>` its children with `slot="actions"`, and what a slot holds shows when nothing fills it.
|
|
31
|
+
- No logic in the markup: variants are attributes styled in CSS (`&[positive]`), and a list is several elements.
|
|
32
|
+
- Its `<style>` only reaches inside the element, which is a block box unless `&` says otherwise. `styles.css` applies inside too: build with its classes (`card`, `btn`, `muted`) and name the component's own parts with names it doesn't use. A design's own `<style>` comes after and can restyle one element, but its selectors reach inside components too, so keep them specific.
|
|
33
|
+
- `<script>` runs for each element once it is built, with `this` as the element. Use it sparingly, a few lines for what markup and CSS can't do, like marking the current link (`<app-sidebar active="Customers">`) or a toggle. No fetching, data or frameworks.
|
|
34
|
+
- Relative links in a component start from its file (`../01-screens/02-customers.html`).
|
|
35
|
+
- Close the tag: `<tx-item โฆ></tx-item>`, never `<tx-item />`. A component can't be a table's row or cell itself (the browser moves it out of the `<table>`), but it works inside a cell: `<td><org-cell โฆ></org-cell></td>`. Where it sits (fixed, margins, the grid around it) belongs to the design.
|
|
36
|
+
- Editing a component changes every design that uses it, like `styles.css`. Afterwards, `npx htmlboard check <designs folder>` lists components that weren't built and errors in their scripts.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: htmlboard-designs-json
|
|
3
|
+
description: Curate an htmlboard canvas with designs.json (section titles and order, a design's name or size, hiding unlisted designs, showing components). Use when the designs folder has a designs.json, or the user wants sections named or ordered beyond what folders give.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# HtmlBoard designs.json
|
|
7
|
+
|
|
8
|
+
Without one, folders are the canvas's sections and number prefixes set the order (the htmlboard skill covers the designs themselves). A `designs.json` in the designs folder curates on top of that: sections with titles (accents, capitals, an order across folders), and values that win over the files'. The files still say the rest: their name, size and variants in their `<head>`.
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"title": "Wallet app",
|
|
13
|
+
"sections": [
|
|
14
|
+
{ "title": "Checkout ยท mobile", "designs": ["03-checkout/01-one-page.html", "03-checkout/02-steps.html"] },
|
|
15
|
+
{ "title": "Launch flyers", "folder": "04-flyers" }
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- Sections come in their order, designs in theirs. An entry is a file name (its variants follow it; with a query string, it places one variant), or `{ "file", "title", "width", "height", "category", "description" }` to override the file's values.
|
|
21
|
+
- `"folder"` brings a whole folder, in prefix order.
|
|
22
|
+
- Designs it doesn't list still show after the ones it lists, in the section that holds their folder (or a section of their own), unless it says `"unlisted": "hide"`. When it groups designs by hand, add a new design to the right section.
|
|
23
|
+
- Components aren't on the canvas unless it says `"components": "show"` (or lists `components/<tag>.html`): each shows alone, from its first-line example, 480 wide unless its file has `<meta name="htmlboard" content="width=โฆ">`.
|
|
24
|
+
- Saving it rebuilds the canvas.
|
|
25
|
+
- `npx htmlboard migrate <designs folder>` moves an old designs.json's sizes, names and variants into the files and keeps only what they can't say (`--write` to apply; without it, it only shows the plan).
|
package/src/canvases.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
|
|
3
|
+
// The htmlboard canvases running on this machine, found by asking ports for /api/health: for
|
|
4
|
+
// `htmlboard check`, and so that starting htmlboard again for a folder points at the running one.
|
|
5
|
+
|
|
6
|
+
/** Where htmlboard listens unless told otherwise: 3000, or the next free port */
|
|
7
|
+
export const PORTS = Array.from({ length: 21 }, (_, i) => 3000 + i);
|
|
8
|
+
|
|
9
|
+
/** GET a URL: its status and body */
|
|
10
|
+
export function get(url, { timeout = 0 } = {}) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const req = http.get(url, (res) => {
|
|
13
|
+
const chunks = [];
|
|
14
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
15
|
+
res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks).toString('utf8') }));
|
|
16
|
+
res.on('error', reject);
|
|
17
|
+
});
|
|
18
|
+
req.on('error', reject);
|
|
19
|
+
if (timeout) req.setTimeout(timeout, () => req.destroy(new Error('timeout')));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The htmlboard servers listening on these ports: each its health (dir, root, version) with its
|
|
25
|
+
* port and url.
|
|
26
|
+
*/
|
|
27
|
+
export async function findCanvases(ports = PORTS) {
|
|
28
|
+
const found = await Promise.all([...new Set(ports)].map(async (port) => {
|
|
29
|
+
try {
|
|
30
|
+
const { status, body } = await get(`http://127.0.0.1:${port}/api/health`, { timeout: 1500 });
|
|
31
|
+
const health = JSON.parse(body);
|
|
32
|
+
return status === 200 && health.status === 'ok' && health.dir ? { ...health, port, url: `http://localhost:${port}` } : null;
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
37
|
+
return found.filter(Boolean);
|
|
38
|
+
}
|
package/src/check.js
CHANGED
|
@@ -1,48 +1,18 @@
|
|
|
1
|
-
import http from 'node:http';
|
|
2
1
|
import path from 'node:path';
|
|
2
|
+
import { PORTS, get, findCanvases } from './canvases.js';
|
|
3
3
|
|
|
4
4
|
// `htmlboard check`: the canvas running for a folder, open in a browser, loads its designs and says
|
|
5
5
|
// what went wrong in each (see client/check.js); this finds that canvas, asks it, and puts the
|
|
6
6
|
// answer in a few lines of text.
|
|
7
7
|
|
|
8
|
-
/** Where htmlboard listens unless told otherwise: 3000, or the next free port */
|
|
9
|
-
const PORTS = Array.from({ length: 21 }, (_, i) => 3000 + i);
|
|
10
|
-
|
|
11
8
|
/** A problem the check couldn't get past (no canvas running, none open in a browser): exit code 2 */
|
|
12
9
|
export class CheckError extends Error {}
|
|
13
10
|
|
|
14
|
-
function get(url, { timeout = 0 } = {}) {
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
const req = http.get(url, (res) => {
|
|
17
|
-
const chunks = [];
|
|
18
|
-
res.on('data', (chunk) => chunks.push(chunk));
|
|
19
|
-
res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks).toString('utf8') }));
|
|
20
|
-
res.on('error', reject);
|
|
21
|
-
});
|
|
22
|
-
req.on('error', reject);
|
|
23
|
-
if (timeout) req.setTimeout(timeout, () => req.destroy(new Error('timeout')));
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
11
|
const isInside = (base, target) => {
|
|
28
12
|
const rel = path.relative(base, target);
|
|
29
13
|
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
|
|
30
14
|
};
|
|
31
15
|
|
|
32
|
-
/** The htmlboard servers listening on these ports, each with its designs folder (dir) and root */
|
|
33
|
-
async function findCanvases(ports) {
|
|
34
|
-
const found = await Promise.all(ports.map(async (port) => {
|
|
35
|
-
try {
|
|
36
|
-
const { status, body } = await get(`http://127.0.0.1:${port}/api/health`, { timeout: 1500 });
|
|
37
|
-
const health = JSON.parse(body);
|
|
38
|
-
return status === 200 && health.status === 'ok' && health.dir ? { ...health, port, url: `http://localhost:${port}` } : null;
|
|
39
|
-
} catch {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
}));
|
|
43
|
-
return found.filter(Boolean);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
16
|
/**
|
|
47
17
|
* Check designs with the canvas that shows them: a designs folder, a folder in it, or a design's
|
|
48
18
|
* file (default: all the designs of the canvas found). The canvas must be open in a browser, where
|