skill
Adom UI Design System
Public Made by Adomby adom
How the AI builds and tests interfaces people love to use in Adom. Ships as one installable skill that bundles focused skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps, clickable-paths).
← Commit history
Restructure: bundle the six UI skills as files in this repo; install deploys them all
12 files changed
+692−83
README.md+8−8@@ -304,16 +304,16 @@ Fix every FAIL (the hint tells you exactly what to do), address relevant WARNs, ## 13. The skill map -This system is split across focused skills. The AI loads them by trigger word. Click any skill below to read it. The first six are the dedicated UI skills that this page breaks out.+This whole system ships as **one installable skill**. `adompkg install adom-ui-design` drops the design-system skill **plus all six UI skills below** onto your container as separate skill files, so the AI loads each by its own trigger words. The first six live as files in **this** repo; click to read them. The rest are existing platform skills. | Skill | Governs | |---|---|-| [**tooltips**](https://wiki.adom.inc/john/tooltips) | Great desktop tooltips: 600ms reveal, the fixed-div renderer, placement that never covers what you hover, edge-aware flipping, the z-index ladder |-| [**floating-windows**](https://wiki.adom.inc/john/floating-windows) | Draggable floaty tool windows and HUDs that never get in the way |-| [**toasts**](https://wiki.adom.inc/john/toasts) | Non-blocking confirmations, and the AI's live narration channel to the user |-| [**responsive-ui**](https://wiki.adom.inc/john/responsive-ui) | Phone-to-desktop layouts, touch targets, and tooltips on touch where hover does not exist |-| [**app-shell**](https://wiki.adom.inc/john/app-shell) | The full-height single-page app shell, the footer / status bar, and the sizing traps |-| [**ai-driven-apps**](https://wiki.adom.inc/john/ai-driven-apps) | The build-show-drive-ralph loop, the live SSE/WebSocket channel, the three display surfaces |+| [**tooltips**](https://wiki.adom.inc/john/adom-ui-design/files/skills/tooltips/SKILL.md) | Great desktop tooltips: 600ms reveal, the fixed-div renderer, placement that never covers what you hover, edge-aware flipping, the z-index ladder |+| [**floating-windows**](https://wiki.adom.inc/john/adom-ui-design/files/skills/floating-windows/SKILL.md) | Draggable floaty tool windows and HUDs that never get in the way |+| [**toasts**](https://wiki.adom.inc/john/adom-ui-design/files/skills/toasts/SKILL.md) | Non-blocking confirmations, and the AI's live narration channel to the user |+| [**responsive-ui**](https://wiki.adom.inc/john/adom-ui-design/files/skills/responsive-ui/SKILL.md) | Phone-to-desktop layouts, touch targets, and tooltips on touch where hover does not exist |+| [**app-shell**](https://wiki.adom.inc/john/adom-ui-design/files/skills/app-shell/SKILL.md) | The full-height single-page app shell, the footer / status bar, and the sizing traps |+| [**ai-driven-apps**](https://wiki.adom.inc/john/adom-ui-design/files/skills/ai-driven-apps/SKILL.md) | The build-show-drive-ralph loop, the live SSE/WebSocket channel, the three display surfaces | | [human-ui-patterns](https://wiki.adom.inc/adom/human-ui-patterns) | The broader user-first interaction primitives these were extracted from | | [app-creator](https://wiki.adom.inc/adom/app-creator) | Scaffolding a webview app: two-way comms, proxy URL, favicon, publish flow | | `adom-app-header` | The 44px three-zone header and the app-info popover |@@ -328,7 +328,7 @@ This system is split across focused skills. The AI loads them by trigger word. C | `adom-wiki-hero` | Rendering a billboard-grade hero image | | [adom-ui-linter](https://wiki.adom.inc/adom/adom-ui-linter) | The deterministic check suite that enforces this page | -*(Skills shown in plain text do not have a standalone wiki page yet; the linked ones are live.)*+*(The first six are files in this repo and install together with the design system. The other linked skills are existing platform skills with their own pages; plain-text ones have no standalone page yet.)* ---
SKILL.md+8−8@@ -327,16 +327,16 @@ Fix every FAIL (the hint tells you exactly what to do), address relevant WARNs, ## 13. The skill map -This system is split across focused skills. The AI loads them by trigger word. Click any skill below to read it. The first six are the dedicated UI skills that this page breaks out.+This whole system ships as **one installable skill**. `adompkg install adom-ui-design` drops the design-system skill **plus all six UI skills below** onto your container as separate skill files, so the AI loads each by its own trigger words. The first six live as files in **this** repo; click to read them. The rest are existing platform skills. | Skill | Governs | |---|---|-| [**tooltips**](https://wiki.adom.inc/john/tooltips) | Great desktop tooltips: 600ms reveal, the fixed-div renderer, placement that never covers what you hover, edge-aware flipping, the z-index ladder |-| [**floating-windows**](https://wiki.adom.inc/john/floating-windows) | Draggable floaty tool windows and HUDs that never get in the way |-| [**toasts**](https://wiki.adom.inc/john/toasts) | Non-blocking confirmations, and the AI's live narration channel to the user |-| [**responsive-ui**](https://wiki.adom.inc/john/responsive-ui) | Phone-to-desktop layouts, touch targets, and tooltips on touch where hover does not exist |-| [**app-shell**](https://wiki.adom.inc/john/app-shell) | The full-height single-page app shell, the footer / status bar, and the sizing traps |-| [**ai-driven-apps**](https://wiki.adom.inc/john/ai-driven-apps) | The build-show-drive-ralph loop, the live SSE/WebSocket channel, the three display surfaces |+| [**tooltips**](https://wiki.adom.inc/john/adom-ui-design/files/skills/tooltips/SKILL.md) | Great desktop tooltips: 600ms reveal, the fixed-div renderer, placement that never covers what you hover, edge-aware flipping, the z-index ladder |+| [**floating-windows**](https://wiki.adom.inc/john/adom-ui-design/files/skills/floating-windows/SKILL.md) | Draggable floaty tool windows and HUDs that never get in the way |+| [**toasts**](https://wiki.adom.inc/john/adom-ui-design/files/skills/toasts/SKILL.md) | Non-blocking confirmations, and the AI's live narration channel to the user |+| [**responsive-ui**](https://wiki.adom.inc/john/adom-ui-design/files/skills/responsive-ui/SKILL.md) | Phone-to-desktop layouts, touch targets, and tooltips on touch where hover does not exist |+| [**app-shell**](https://wiki.adom.inc/john/adom-ui-design/files/skills/app-shell/SKILL.md) | The full-height single-page app shell, the footer / status bar, and the sizing traps |+| [**ai-driven-apps**](https://wiki.adom.inc/john/adom-ui-design/files/skills/ai-driven-apps/SKILL.md) | The build-show-drive-ralph loop, the live SSE/WebSocket channel, the three display surfaces | | [human-ui-patterns](https://wiki.adom.inc/adom/human-ui-patterns) | The broader user-first interaction primitives these were extracted from | | [app-creator](https://wiki.adom.inc/adom/app-creator) | Scaffolding a webview app: two-way comms, proxy URL, favicon, publish flow | | `adom-app-header` | The 44px three-zone header and the app-info popover |@@ -351,7 +351,7 @@ This system is split across focused skills. The AI loads them by trigger word. C | `adom-wiki-hero` | Rendering a billboard-grade hero image | | [adom-ui-linter](https://wiki.adom.inc/adom/adom-ui-linter) | The deterministic check suite that enforces this page | -*(Skills shown in plain text do not have a standalone wiki page yet; the linked ones are live.)*+*(The first six are files in this repo and install together with the design system. The other linked skills are existing platform skills with their own pages; plain-text ones have no standalone page yet.)* ---
install.sh+14−6@@ -1,8 +1,16 @@ #!/usr/bin/env bash-# Install the Adom UI Design System as a reference skill.+# Install the Adom UI Design System and all the UI skills bundled in this repo. set -e-DEST="$HOME/.claude/skills/adom-ui-design"-mkdir -p "$DEST"-cp SKILL.md "$DEST/SKILL.md"-echo "OK: Installed Adom UI Design System to $DEST/SKILL.md"-echo "Hint: Ask Claude 'what are the Adom color tokens' or 'build me an on-brand Adom app' to use it."+DEST="$HOME/.claude/skills"+# main design-system skill (root SKILL.md)+mkdir -p "$DEST/adom-ui-design"; cp SKILL.md "$DEST/adom-ui-design/SKILL.md"+n=1+# bundled sub-skills, one folder each under skills/+for d in skills/*/; do+ name="$(basename "$d")"+ mkdir -p "$DEST/$name"+ cp "$d/SKILL.md" "$DEST/$name/SKILL.md"+ n=$((n+1))+done+echo "OK: Installed the Adom UI Design System plus $((n-1)) bundled UI skills to $DEST/."+echo "Hint: Ask Claude 'what are the Adom color tokens', 'great tooltips', or 'make my app a joy to use'."
package.json+9−3@@ -1,8 +1,8 @@ { "slug": "adom-ui-design",- "version": "1.1.0",+ "version": "1.2.0", "type": "skill",- "description": "How the AI builds and tests interfaces people love to use in Adom: user-first interaction, the build-show-drive-ralph loop across native browser / pup / webview, a live SSE/WebSocket channel, toasts as the AI's narration, responsive UI, and the polish tokens last.",+ "description": "The Adom UI Design System plus a bundle of focused UI skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps). One adompkg install drops the whole set of skill files onto your container.", "dependencies": {}, "files": [ "SKILL.md",@@ -11,7 +11,13 @@ "install.sh", "uninstall.sh", "screenshots/hero.png",- "screenshots/tokens.png"+ "screenshots/tokens.png",+ "skills/tooltips/SKILL.md",+ "skills/floating-windows/SKILL.md",+ "skills/toasts/SKILL.md",+ "skills/responsive-ui/SKILL.md",+ "skills/app-shell/SKILL.md",+ "skills/ai-driven-apps/SKILL.md" ], "scripts": { "install": "./install.sh",
page.json+14−55@@ -1,64 +1,23 @@ {+ "type": "doc", "slug": "adom-ui-design",- "version": "1.1.0",- "type": "skill",- "description": "How the AI builds and tests interfaces people love to use in Adom: user-first interaction, the build-show-drive-ralph loop across native browser / pup / webview, a live SSE/WebSocket channel, toasts as the AI's narration, responsive UI, and the polish tokens last.",- "dependencies": {},- "files": [- "SKILL.md",- "README.md",- "page.json",- "install.sh",- "uninstall.sh",- "screenshots/hero.png",- "screenshots/tokens.png"- ],- "scripts": {- "install": "./install.sh",- "uninstall": "./uninstall.sh"- },- "tags": [- "design-system",- "brand",- "ui",- "ux",- "guidelines",- "tokens"- ],- "author": {- "name": "John Lauer",- "email": "[email protected]"- }, "title": "Adom UI Design System", "brief": "How the AI builds and tests interfaces people love to use in Adom: user-first interaction, the build-show-drive-ralph loop across native browser / pup / webview, a live SSE/WebSocket channel, toasts as the AI's narration, responsive UI, and the polish tokens last.", "hero": { "type": "image", "path": "screenshots/hero.png" },- "discovery_triggers": [- "ui design",- "design system",- "build an app",- "build a ui",- "test the ui",- "ralph test",- "ralph loop",- "drive the front-end",- "ai-drivable",- "live channel",- "sse",- "websocket",- "show app in native browser",- "show app in pup",- "webview vs pup",- "auto-click buttons",- "toast",- "app header",- "mobile tooltip",- "responsive layout",- "make my app a joy to use",- "ui linter"- ],- "discovery_pitch": "How the AI builds AND tests interfaces people love to use in Adom: the build-show-drive-ralph loop across native browser, pup, and webview, a live SSE/WebSocket channel the AI drives the front-end with, toasts as the AI's narration to the user, responsive UI, and the polish tokens last.",- "updated_at": "2026-06-20T15:36:16.934Z"+ "visibility": {+ "public": true+ },+ "metadata": {+ "tags": [+ "design-system",+ "brand",+ "ui",+ "ux",+ "guidelines",+ "tokens"+ ]+ } }
skills/ai-driven-apps/SKILL.mdadded+99@@ -0,0 +1,99 @@+---+name: ai-driven-apps+user-invocable: true+description: >-+ How the AI builds, shows, drives, and tests an Adom app. In Adom the AI writes the app, shows it to+ the user (in their own already-signed-in native browser via Adom Desktop, in pup for the+ logged-out experience, or in a Hydrogen webview), drives the front-end over a live SSE or WebSocket+ channel to exercise every path (auto-click buttons, show and hide floaty toolbars, load a+ different symbol or footprint, click send-to-kicad), reads back state, console, and screenshots,+ and ralph-loops until the UI is right. Covers state-on-the-server, GET /state, GET /console, the+ live channel, the CLI driving commands, the three display surfaces and when to use each, and the+ ralph-test loop. Read BEFORE building or testing any Adom web app. Trigger words: ai-drivable, ai+ driven app, drive the front-end, drive the ui, live channel, SSE, server-sent events, websocket,+ GET /state, GET /console, ralph test, ralph loop, auto-click buttons, test the ui, show app in+ native browser, show app in pup, webview vs pup, native browser auth, logged out testing, ui click.+---++# AI-driven apps++This is the loop the whole Adom UI system is designed around. The AI is both the builder and the+tester; the user watches and steers. An app is only as good as the AI's ability to drive and observe+it, so build for that from the first commit.++## The loop++1. **The AI builds the app:** a small server plus a webview UI, made drivable from day one (state on+ the server, a live channel, a CLI).+2. **The AI shows it to the user** in one of three surfaces (below), chosen for what is being tested.+3. **The AI drives the front-end** over the live channel to exercise every path: click each button,+ open and close the floating toolbar, load a different symbol or footprint, hit send-to-kicad, and+ read back state, the console buffer, and screenshots.+4. **The AI ralph-loops:** screenshot, analyze what looks wrong, fix the code, reload, repeat, until+ the UI is correct and a pleasure to use.+5. **The AI narrates** through toasts and chat so the user can follow along.++## State on the server, observed two ways++- **The server owns the truth.** The UI never keeps its own copy of "am I recording?" or "which+ symbol is loaded?". Buttons fire commands to the server; the server holds state.+- **`GET /state`** returns the current state as JSON. The UI reflects it; the AI reads it to check+ progress without scraping HTML.+- **`GET /console`** exposes the app's log: override `console.log/warn/error` to also post to the+ server, keep a ring buffer, and catch `window.onerror` and `unhandledrejection`. Now the AI reads+ what the UI is doing without DevTools.++## The live channel++- Expose **Server-Sent Events** for one-way push, or a **WebSocket** for two-way. The UI subscribes+ and updates the instant anything changes; the CLI subscribes too. A 300 to 500ms poll of+ `GET /state` is the minimum fallback, but the live channel is what makes driving feel instant.+- **Because the channel is two-way, the AI drives the front-end from the CLI.** Every user-visible+ action has a command that does exactly what the click does:+ ```+ mytool ui click "send-to-kicad" # press a button+ mytool ui toolbar show # reveal the floating toolbar, then `hide`+ mytool ui load-symbol LM358 # swap the loaded symbol or footprint+ mytool ui toast "file downloaded" # raise a toast (also the AI's narration channel)+ ```+ Each command drives the real UI through the same path a user click would, so the AI can walk every+ interaction, assert the resulting state, screenshot it, and move on. This is the test harness and+ the demo driver in one.+- **Acceptance test:** an AI driving the app over the channel and a person clicking the UI produce+ identical visible results.++## The three display surfaces++Pick the surface by what you need to test, and use more than one at once when it helps.++| Surface | What it is | Use it for |+|---|---|---|+| **Native browser** | The user's own Chrome or Edge, driven through Adom Desktop | The real, signed-in experience. The browser is already the user's, so the app opens **logged in with zero friction**. Use it for anything behind a login. |+| **pup** | A clean, scriptable Chrome the AI fully owns | The **fresh, logged-out, first-time-user** experience, and automated screenshot loops that should not fight the user's own tabs. |+| **Hydrogen webview** | An in-workspace browser tab | Showing a result inside the workbench, next to the code and the chat. |++The native-browser and pup split is the point: open the **logged-in path in the native browser** and+the **logged-out path in pup** at the same time, and verify both. Tell the user which surface and+why.++## Ralph-testing the UI++The visual feedback loop that turns "it runs" into "it is right": edit, reload, drive a step over+the channel, screenshot, read the console buffer, analyze, fix, repeat. Drive it in pup (or the+native browser), capture each frame to the screenshot log, and loop until the UI looks and behaves+correctly. This is how the AI debugs UI with no human in the loop, and it is the proof the app is+genuinely drivable.++## Reaching a container app from outside++- A webview and a browser window both render **outside** the container, so they cannot reach+ `http://localhost:<port>` or `http://127.0.0.1:<port>`. Always use the public proxy URL:+ `https://<slug>.adom.cloud/proxy/<port>/`.+- **Inside a webview, always use relative URLs.** A leading slash bypasses the proxy and hits the+ wrong origin. Every `fetch`, `href`, `src`, and CSS `url()` must be relative.++## Related skills++`app-creator` (scaffolding the app), `adom-screenshot` (the ralph loop and shotlog),+`adom-cli-design` and `ai-hints-in-clis` (the CLI the driving commands live in), `toasts` (the+narration channel), `app-shell` (the layout being driven), and `adom-ui-design` (the hub).
skills/app-shell/SKILL.mdadded+77@@ -0,0 +1,77 @@+---+name: app-shell+user-invocable: true+description: >-+ How to lay out the shell of an Adom single-page app: a full-height column with a fixed 44px header,+ a scrollable body that fills the rest, and an optional footer or status bar, built so it sizes+ correctly and reshapes responsively instead of overflowing or collapsing. Covers the full-height+ flex column, the intrinsic-sizing traps (min-height:0, minmax(0,1fr), position:fixed on the app+ root), where HUDs and toasts mount, fluid panels and split layouts, and the footer / status-bar+ convention. Read BEFORE laying out a new app or when an app overflows, scrolls the whole page, or a+ canvas will not fill its pane. Trigger words: app shell, app layout, single page app layout, SPA+ layout, full height layout, fluid layout, header body footer, status bar, footer, app footer,+ scroll container, min-height 0, minmax 0 1fr, canvas wont fill, page scrolls, sticky header layout.+---++# The app shell++The shell is the frame every Adom app shares: a header at the top, content that fills the rest, and+an optional footer. Get the shell right and everything inside it sizes correctly; get it wrong and+the page scrolls as a whole, a canvas refuses to fill its pane, or a panel overflows the screen.++## The full-height column++```+.app position: fixed; inset: 0; display: flex; flex-direction: column;+ .app-header height: 44px; flex: 0 0 auto; (see adom-app-header)+ .app-body flex: 1 1 auto; min-height: 0; overflow: auto / a grid of panes+ .app-footer flex: 0 0 auto; (optional status bar)+```++```css+.app { position: fixed; inset: 0; display: flex; flex-direction: column;+ background: #0d1117; color: #e6edf3; }+.app-header { flex: 0 0 44px; }+.app-body { flex: 1 1 auto; min-height: 0; } /* min-height:0 is load-bearing */+.app-footer { flex: 0 0 auto; }+```++### The intrinsic-sizing traps (these cause most layout bugs)++- **`min-height: 0` on the flex body.** Without it a flex child refuses to shrink below its content,+ so a tall child makes the whole app overflow and the page scrolls. This single line fixes the most+ common "the whole page scrolls instead of the panel" bug.+- **`minmax(0, 1fr)` in grids.** A grid track defaults to `min-content`, which will not shrink. Use+ `minmax(0, 1fr)` so a pane (especially one holding a canvas) can actually size down.+- **`position: fixed` on `.app`,** anchored to `inset: 0`, so the shell is exactly the viewport and+ nothing relies on document flow. For a canvas, sync its pixel size to the pane's client size on+ resize, do not assume CSS size equals backing-store size.++## The body: fluid panes and splits++- A single scroll region uses `overflow: auto` on `.app-body`.+- A multi-pane layout uses CSS grid with `minmax(0, 1fr)` tracks and a draggable splitter. Keep+ split ratios between 0.1 and 0.9 and persist them.+- The body reshapes responsively: side-by-side panes on desktop become a single column or tabs on a+ phone (see `responsive-ui`).++## The footer / status bar++A footer is optional. When present it is a thin status bar, not a second toolbar:++- One row, muted, for ambient state: connection status, current mode, counts, a save indicator, the+ zoom level. Monospace for numeric/data readouts (see the type rules in `adom-ui-design`).+- It does not hold primary actions (those live in the header's actions zone or a bottom bar on+ mobile). It must never grow or wrap.+- `flex: 0 0 auto`, at `--z-toolbar: 80`.++## Where overlays mount++- HUDs (`floating-windows`) float over `.app-body` at `--z-hud: 50`.+- Toasts (`toasts`) mount at the bottom center at `--z-toast: 400`.+- The global tooltip div (`tooltips`) is appended to `<body>`, outside the shell, at 99999.++## Related skills++`adom-app-header` (the header), `responsive-ui` (how the shell reshapes), `floating-windows`,+`toasts`, `tooltips`, and `adom-ui-design` (the hub).
skills/floating-windows/SKILL.mdadded+73@@ -0,0 +1,73 @@+---+name: floating-windows+user-invocable: true+description: >-+ How to build floaty tool windows and HUDs in an Adom app: draggable side panels and overlays that+ hold tools, inspectors, and controls without blocking the main content. Covers the drag grip,+ never clamping to a screen edge, max-height with internal scroll and a pinned header and footer,+ minimize / close / double-click-to-collapse, the z-index ladder slot (--z-hud), remembering+ position, and driving show/hide from the AI over the live channel for testing. Read BEFORE adding+ a floating panel, inspector, toolbar overlay, or HUD. Trigger words: floating window, floaty+ window, floaty toolbar, HUD, draggable panel, tool window, inspector panel, overlay panel,+ floating toolbar, drag handle, collapse panel, minimize panel, show hide toolbar, z-index hud.+---++# Floating windows and HUDs++A HUD is a draggable window that floats over the main content and holds a cluster of tools: an+inspector, a source list, a measurement readout, a settings panel. Done well it is always reachable+and never in the way. Done wrong it clamps to a corner, covers the thing you are working on, or+grows past the screen with no scroll.++## Rules++- **Draggable by a visible grip.** A clear header bar or a grip icon, not the whole panel (you still+ want to click controls inside). Cursor `grab` on the grip, `grabbing` while dragging.+- **Never clamp to a screen edge.** The user places it; do not snap it flush to a corner or pin it.+ Keep it fully on-screen on resize (clamp into the viewport), but do not force it to an edge.+- **Bounded height with internal scroll.** `max-height: calc(100% - <margin>)`. The header and+ footer stay pinned, the body scrolls. A HUD must never grow taller than the viewport.+- **Minimize, close, and double-click to collapse.** Every HUD offers all three. Collapsed shows+ just the header bar so it stays out of the way without disappearing.+- **It floats above content but below tooltips and modals.** Use the `--z-hud: 50` slot from the+ z-index ladder. A menu opened from a HUD button uses `--z-floating-menu: 200` so it can escape the+ HUD's box. Tooltips (99999) always win. See the `tooltips` skill for the full ladder.+- **Do not cover what the user is acting on.** Open in a neutral spot, let it be moved, and remember+ its last position (persist via the preferences API where it matters).+- **Watch the clipping trap.** If a menu inside the HUD opens out of the HUD and vanishes, that is+ `overflow: hidden` clipping, not z-index. Fix with `overflow-x: clip; overflow-y: visible` or+ reposition the menu to `position: fixed`.++## Structure++```+.hud+ .hud-head (drag grip, title, minimize, close) <- pinned+ .hud-body (the tools) <- flex:1, min-height:0, overflow:auto+ .hud-foot (optional status / actions) <- pinned+```++```css+.hud {+ position: fixed; z-index: var(--z-hud);+ display: flex; flex-direction: column;+ max-height: calc(100% - 32px);+ background: #161b22; border: 1px solid #30363d; border-radius: 12px;+ box-shadow: 0 18px 50px rgba(0,0,0,.5);+}+.hud-head { cursor: grab; display: flex; align-items: center; gap: 8px; padding: 10px 12px; }+.hud-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 12px; }+.hud.collapsed .hud-body, .hud.collapsed .hud-foot { display: none; }+```++## AI-drivable++Because the AI builds and tests the app (see `ai-driven-apps`), every HUD action has a command so+the AI can exercise it: `mytool ui toolbar show`, `mytool ui toolbar hide`, `mytool ui hud collapse+inspector`. This is also how the AI screenshots a panel open and closed during a ralph-test.++## Related skills++`tooltips` (the z-index ladder and tooltips that render above HUDs), `app-shell` (where HUDs mount+in the layout), `human-ui-patterns` (the broader interaction rules), `ai-driven-apps` (driving HUDs+for tests), and `adom-ui-design` (the hub).
skills/responsive-ui/SKILL.mdadded+74@@ -0,0 +1,74 @@+---+name: responsive-ui+user-invocable: true+description: >-+ How to make an Adom app adapt from phone to desktop: a rich, dense layout on a large screen and a+ lean, thumb-friendly one on a phone, the same app reshaped rather than a desktop layout shrunk+ until it is unusable. Covers a breakpoint set, reshape-not-shrink, what to show on desktop versus+ collapse on mobile, 44px minimum touch targets, safe-area insets and the soft keyboard, and the+ big one: tooltips on touch, where hover does not exist, so you replace hover tooltips with visible+ labels, tap-to-reveal sheets, or long-press. Read BEFORE building any UI that a phone might open.+ Trigger words: responsive, responsive design, mobile layout, mobile UI, phone layout, adapt to+ screen, breakpoint, breakpoints, media query, touch target, mobile tooltip, hover none, pointer+ coarse, tap to reveal, long press, safe area, soft keyboard, desktop vs mobile, container query.+---++# Responsive and touch++The same Adom app should feel native on a 32 inch monitor and on a phone. This is part of treating+the user well, not an afterthought bolted on at the end.++## Reshape, do not shrink++- **Pick a small set of breakpoints** and change the layout at each, rather than scaling one fixed+ desktop layout down. A reasonable default set:+ - phone: up to 600px+ - tablet: 600 to 1024px+ - desktop: 1024px and up+- **Rich on desktop, focused on mobile.** A desktop has room for side panels, dense tables,+ persistent toolbars, and secondary stats inline. On a phone:+ - collapse multi-column layouts into a single column,+ - move primary actions into a bottom bar or a menu within thumb reach,+ - hide secondary detail behind a tap, do not cram it in,+ - turn a fixed HUD into a bottom sheet.+ The app header already models this: the subject meta line hides below ~720px and returns on+ request.+- **Container queries** are good when a panel needs to reshape by its own width, not the viewport's.++## Touch++- **Touch targets are at least 44 by 44 px.** Fingers are not cursors. Space them so a thumb does+ not hit two at once.+- **Respect safe areas and the soft keyboard.** Use `env(safe-area-inset-*)` for notches and home+ bars, keep primary actions reachable with a thumb, and do not let the on-screen keyboard cover the+ field being typed into.++## Tooltips on touch: hover does not exist++This is the rule that is easy to miss. A hover tooltip can never fire on a phone, so it must not be+the only way to learn a control.++```css+@media (hover: none), (pointer: coarse) {+ /* hover tooltips never trigger here */+}+```++Under that query, replace the hover tooltip with one of:+- **A visible label** next to or under the icon (best for primary controls).+- **A tap that opens an info sheet or popover** describing the control (good for dense toolbars).+- **A long-press** that reveals the description (familiar from mobile OSes).++Never hide essential meaning behind a hover a phone can never produce. See the `tooltips` skill for+the desktop renderer this complements.++## Test it++Test every app at a phone width before publishing, not just at desktop. When the AI is building the+app it can resize the pup window to a phone viewport and ralph-loop the mobile layout the same way+it does the desktop one (see `ai-driven-apps`).++## Related skills++`tooltips` (desktop tooltips), `app-shell` (the fluid layout that reshapes), `human-ui-patterns`,+`ai-driven-apps` (testing the mobile layout), and `adom-ui-design` (the hub).
skills/toasts/SKILL.mdadded+72@@ -0,0 +1,72 @@+---+name: toasts+user-invocable: true+description: >-+ How to do toasts in an Adom app: the transient, non-blocking confirmation that tells the user what+ just happened. Covers position (bottom center), the three types (success green, error red, info+ teal), durations (about 4s for a confirmation, sticky for errors, pause on hover), stacking newest+ on top with a cap, dismiss, carrying an action (above all Undo for destructive actions), ARIA, and+ the POST /ui/toast hook. In Adom a toast does double duty: it is also how the AI, which is driving+ the front-end, narrates to the user live during a test or demo ("file downloaded", "running test+ 3 of 8"). Read BEFORE adding any confirmation, snackbar, or status popup. Trigger words: toast,+ toasts, snackbar, notification popup, confirmation message, status toast, ui toast, POST /ui/toast,+ show a toast, undo toast, success message, error toast, ai narration, file downloaded toast.+---++# Toasts++A toast is a short, non-blocking message that appears, says what just happened, and goes away. It is+the default answer to "every action answers back": a click that produces no visible change reads as+broken, and a toast is the cheapest way to confirm it worked.++## Rules++- **Position:** bottom center, consistent across the app. One stack, one place to look.+- **Three types,** each with a clear icon and color:+ - success: green `#3fb950`+ - error: red `#f85149`+ - info / neutral: teal `#00b8b0`+- **Duration:** about **4 seconds** for a confirmation, **sticky** for an error the user must read+ and dismiss. **Pause the timer on hover** so it does not vanish while being read.+- **Stack newest on top, cap the visible count** (3 to 4), and collapse or drop the oldest beyond+ the cap. Each toast is individually dismissable.+- **Carry an action when one exists,** above all **Undo** for anything destructive. A delete that+ shows "Deleted. Undo" is forgiving; a silent delete is not.+- **Never block.** A toast never traps focus or stops the user. If a choice is required, that is a+ modal, not a toast.+- **It sits at `--z-toast: 400`** in the z-index ladder (below modals and tooltips, above HUDs).+- **Accessible:** `role="status"` and `aria-live="polite"` for info and success, `role="alert"` and+ `aria-live="assertive"` for errors, so screen readers announce them.++```css+.toast {+ display: flex; align-items: center; gap: 12px;+ background: #161b22; border: 1px solid #30363d; border-radius: 12px;+ padding: 14px 18px; box-shadow: 0 18px 50px rgba(0,0,0,.5);+ border-left: 3px solid var(--toast-accent); /* green / red / teal by type */+}+.toast .action { margin-left: auto; color: #58a6ff; font-weight: 600; cursor: pointer; }+```++## The `POST /ui/toast` hook: feedback AND AI narration++Build a server endpoint that raises a toast, from day one. It serves two callers with one+mechanism:++1. **The user**, getting feedback on their own actions.+2. **The AI**, which is driving the front-end (see `ai-driven-apps`). Because the AI controls the+ app, a toast is the natural place for it to narrate live, on top of the running app, during a+ test or a demo: "running test 3 of 8", "file downloaded", "loaded LM358, re-routing". That status+ belongs over the app, not buried in chat.++```+POST /ui/toast { "type": "success", "message": "File downloaded", "action": null, "ttl": 4000 }+```+The CLI wraps it: `mytool ui toast "file downloaded"`. The same command the AI uses to drive+everything else also lets it talk to the user.++## Related skills++`tooltips` (the z-index ladder, the sibling transient element), `ai-driven-apps` (the AI raising+toasts while driving), `human-ui-patterns` (the broader feedback rules), and `adom-ui-design` (the+hub).
skills/tooltips/SKILL.mdadded+240@@ -0,0 +1,240 @@+---+name: tooltips+user-invocable: true+description: >-+ How to build great desktop tooltips in an Adom app: extensive, well-written content, a 600ms+ hover-reveal delay so cursor fly-bys stay quiet, a body-appended position:fixed div at z-index+ 99999 (never a ::after pseudo that gets clipped), cursor-relative placement that never covers the+ thing you are hovering, and edge-aware flipping so a tooltip never renders off-screen. Read BEFORE+ adding a tooltip to any hoverable or icon-only control. Includes the canonical z-index ladder,+ one-tooltip-at-a-time suppression, the no-all-caps rule, and full working CSS and JS. For touch+ and phones (where hover does not exist) see the responsive-ui skill. Trigger words: tooltip,+ tooltips, hover tooltip, hover preview, data-tooltip, tooltip placement, tooltip off-screen,+ tooltip clipped, tooltip z-index, z-index ladder, tooltip delay, hover delay, icon button label,+ explain this control, tooltip covers cursor, tooltip not showing, title attribute, ::after tooltip.+---++# Tooltips++A tooltip is how a control explains itself. Done well it is the single highest-leverage usability+feature in an Adom app: every icon button, every jargon label, every non-obvious number becomes+self-explaining without cluttering the screen. Done wrong it is clipped, off-screen, covering the+thing you are trying to read, or shouting in all caps. This skill is the one correct way.++> The rules here are paranoid-earned. The wrong patterns (`title=""`, a `::after` pseudo, z-index+> tiered to the owner HUD) are plausible-sounding ideas that keep getting reinvented and keep+> breaking. Use the body-appended fixed-div renderer below and nothing else.++## 1. Every interactive element gets a descriptive tooltip++Buttons, icon-only controls, labels with jargon, and any non-obvious widget MUST carry a+`data-tooltip` written for a person who has never seen the app.++**A button tooltip says:**+1. What the button does, in one sentence.+2. What changes in the app after you click it.+3. For destructive or irreversible actions, what is preserved and what is lost.+4. For jargon-labeled buttons (`EBU R128`, `MPSSE`, `zUp`), define the jargon in plain English.++**A label tooltip says:** what the label means, its unit (for example "in millimetres"), and how a+user would act on the value (is it a warning, a measurement, a setting).++**Never use `title=""`.** Browsers render native titles inconsistently (instant on some, a 2 second+delay on others), handle multi-line poorly, and ignore your brand styling. Use `data-tooltip`.++## 2. Reveal after a 600ms hover delay++Instant tooltips spam the user every time the cursor crosses the UI. A 600ms delay means a+deliberate hover gets the explanation and a fly-by fires nothing.++```css+[data-tooltip]:hover::after, /* if you ever use the simple form */+#global-tooltip.shown {+ /* 600ms reveal, 0.18s ease-in */+ transition: opacity 0.18s ease 0.6s, transform 0.18s ease 0.6s;+}+```++(The standalone delay token is 600ms across Adom apps. A future app may expose `--tooltip-delay`,+but 600ms is the default and the value the rest of the system assumes.)++## 3. The renderer: ONE body-appended `position:fixed` div at z-index 99999++A tooltip is the topmost layer of the entire app. It is a single `<div>` appended directly to+`<body>`, `position: fixed`, `z-index: 99999` flat. It is **never** a CSS `::after` pseudo on the+trigger, and its z-index is **never** tiered against the owner HUD.++This is the only pattern that survives all three failure modes:++1. **`overflow: hidden` clipping.** A `::after` bound to its trigger's box is clipped at a HUD edge+ regardless of z-index if any ancestor has `overflow: hidden`. Only a `position: fixed` element+ outside the clipping tree escapes.+2. **Stacking contexts.** A `transform`, `filter`, `isolation`, or `z-index` on any ancestor caps+ the descendant's effective z-index. A `::after` on a button inside a transformed bar tops out at+ the bar's z, not 99999. Only a body-reparented fixed element escapes.+3. **Owner-HUD tiering.** Tiering tooltip z to `ownerHud.z + 1` breaks the instant one HUD covers+ another HUD's button. Do not tier. A tooltip is always topmost.++```html+<div id="global-tooltip"></div> <!-- one element for the whole app, appended at init -->+```++```css+#global-tooltip {+ position: fixed; z-index: 99999; top: 0; left: 0;+ transform: translate(var(--tip-x, 0), var(--tip-y, 0));+ max-width: min(320px, calc(100vw - 40px));+ pointer-events: none; opacity: 0; display: none;+ /* brand styling */+ background: rgba(22,27,34,.96); color: #e6edf3;+ border: 1px solid rgba(0,184,177,.28); border-radius: 10px;+ padding: 9px 13px; font: 400 13px/1.4 'Satoshi', sans-serif;+ box-shadow: 0 12px 40px rgba(0,0,0,.55); backdrop-filter: blur(16px);+ text-transform: none; letter-spacing: 0; /* see rule 6 */+}+#global-tooltip.visible { display: block; }+#global-tooltip.shown { opacity: 1; transition: opacity 0.18s ease 0.6s; }+```++```js+const tip = document.createElement('div');+tip.id = 'global-tooltip';+document.body.appendChild(tip);++let _cx = 0, _cy = 0;+document.addEventListener('mousemove', e => { _cx = e.clientX; _cy = e.clientY; }, true);++function show(trigger) {+ tip.textContent = trigger.getAttribute('data-tooltip') || '';+ tip.classList.add('visible');+ const { x, y } = pickAnchor(trigger, tip.offsetWidth, tip.offsetHeight, _cx, _cy);+ tip.style.setProperty('--tip-x', x + 'px');+ tip.style.setProperty('--tip-y', y + 'px');+ requestAnimationFrame(() => tip.classList.add('shown'));+}+function hide() { tip.classList.remove('visible', 'shown'); }++document.addEventListener('mouseover', e => {+ const t = e.target.closest('[data-tooltip]');+ t ? show(t) : hide();+});+```++## 4. Placement: never cover what you are hovering++Position is a separate concern from z-index. Anchor the tooltip **relative to the cursor**, offset+~14px into the quadrant that does not cover the cursor or the trigger. Do NOT anchor to the trigger+element: inside a dense HUD that lands the tooltip far from the cursor or on top of other controls.+The one cardinal sin is a tooltip that covers the thing you are hovering.++```js+function pickAnchor(trigger, tipW, tipH, cx, cy) {+ const O = 14;+ const cands = [+ { x: cx + O, y: cy + O }, // below-right (default)+ { x: cx - tipW - O, y: cy + O }, // below-left+ { x: cx + O, y: cy - tipH - O }, // above-right+ { x: cx - tipW - O, y: cy - tipH - O }, // above-left+ ];+ const tr = trigger.getBoundingClientRect();+ const vw = window.innerWidth, vh = window.innerHeight;+ const score = c => {+ let p = 0;+ if (c.x < 8 || c.x + tipW > vw - 8) p += 1000; // off right/left edge+ if (c.y < 8 || c.y + tipH > vh - 8) p += 1000; // off top/bottom edge+ const ox = Math.max(0, Math.min(c.x+tipW, tr.right) - Math.max(c.x, tr.left));+ const oy = Math.max(0, Math.min(c.y+tipH, tr.bottom) - Math.max(c.y, tr.top));+ p += ox * oy; // overlap with trigger+ return p;+ };+ const best = cands.map(c => ({ c, s: score(c) })).sort((a,b)=>a.s-b.s)[0].c;+ // final clamp into the viewport (or nearest .panel for chrome-reduced apps)+ best.x = Math.max(8, Math.min(best.x, vw - tipW - 8));+ best.y = Math.max(8, Math.min(best.y, vh - tipH - 8));+ return best;+}+```++## 5. Edge-aware: never render off-screen++Two guarantees: a `max-width` that fits the viewport (rule 3), and the clamp in `pickAnchor` that+keeps the box inside the screen. If you use the simpler `::after` form anywhere, measure the trigger+on `mouseenter` and flip:++```js+document.addEventListener('mouseenter', e => {+ const el = e.target; if (!el?.matches?.('[data-tooltip]')) return;+ const r = el.getBoundingClientRect(), vw = innerWidth, vh = innerHeight;+ el.toggleAttribute('data-tooltip-v', vh - r.bottom < 240); // <240px below -> flip up+ el.toggleAttribute('data-tooltip-align', vw - r.left < 340); // near right edge -> right-align+}, true);+```+```css+[data-tooltip][data-tooltip-v]::after { top: auto; bottom: calc(100% + 8px); }+[data-tooltip][data-tooltip-align]::after { left: auto; right: 0; }+```++## 6. One at a time, and never ALL CAPS++- **One tooltip in the DOM at any moment,** belonging to the innermost `[data-tooltip]` under the+ cursor. Browsers fire `:hover` on every ancestor, so a button and its parent row both showing is a+ real bug. Suppress ancestors:+ ```js+ document.addEventListener('mouseover', e => {+ document.querySelectorAll('[data-tooltip-suppress]').forEach(n => n.removeAttribute('data-tooltip-suppress'));+ let n = e.target.closest?.('[data-tooltip]')?.parentElement;+ while (n && n !== document.body) {+ if (n.hasAttribute?.('data-tooltip')) n.setAttribute('data-tooltip-suppress', '');+ n = n.parentElement;+ }+ });+ ```+- **Never ALL CAPS.** `RESET CAMERA` reads as shouting. Write sentence case ("Reset the camera to+ the default isometric view"). If a parent sets `text-transform: uppercase`, the tooltip inherits+ it, so reset `text-transform: none; letter-spacing: 0` on the tooltip element (already in rule 3).+ For emphasis use weight (600 to 700), not caps.++## 7. The z-index ladder++Declare these tokens in `:root` and reference them everywhere, never a hardcoded number, so a+session can never invert the stacking order by picking a number out of thin air.++```css+:root {+ --z-hud: 50; /* draggable HUDs */+ --z-toolbar: 80; /* fixed header / footer / banner */+ --z-floating-menu: 200; /* dropdowns, popovers, pickers, autocomplete */+ --z-toast: 400; /* non-blocking confirmations */+ --z-modal: 600; /* full-screen overlays, dialogs */+ --z-tooltip: 99999; /* always on top */+}+```++A floating menu beats a toolbar so a dropdown can escape the toolbar's box. A tooltip sits two+orders of magnitude above everything so it always wins. Note the separate, non-z-index trap:+`overflow: hidden` on an ancestor clips an absolutely-positioned descendant regardless of z-index.+Fix with `overflow-x: clip; overflow-y: visible`, or reposition the element to `position: fixed`.++## Touch and mobile++Hover does not exist on a phone, so a hover tooltip can never fire there and must not be the only+way to learn a control. Under `@media (hover: none), (pointer: coarse)`, replace the hover tooltip+with a visible label, a tap that opens an info sheet, or a long-press. See the `responsive-ui`+skill for the full touch treatment.++## Acceptance check++- [ ] Every icon-only and jargon control has a `data-tooltip` written for a newcomer.+- [ ] Tooltips reveal after 600ms, not instantly.+- [ ] Rendered as one body-appended `position:fixed` div at z-index 99999, not a `::after` pseudo.+- [ ] The tooltip never covers the cursor or the trigger, and never renders off-screen.+- [ ] Only one tooltip shows at a time; nested triggers do not stack.+- [ ] No ALL CAPS; `text-transform` reset on the tooltip element.+- [ ] `--z-*` tokens declared in `:root`; no hardcoded z-index literals.++## Related skills++`human-ui-patterns` (the broader interaction rules this was extracted from), `floating-windows`+(HUDs that tooltips render above), `toasts` (the sibling transient-feedback element),+`responsive-ui` (tooltips on touch), and `adom-ui-design` (the design-system hub that indexes all of+these).
uninstall.sh+4−3@@ -1,5 +1,6 @@ #!/usr/bin/env bash set -e-DEST="$HOME/.claude/skills/adom-ui-design"-rm -rf "$DEST"-echo "OK: Removed Adom UI Design System skill."+DEST="$HOME/.claude/skills"+rm -rf "$DEST/adom-ui-design"+for d in skills/*/; do rm -rf "$DEST/$(basename "$d")"; done+echo "OK: Removed the Adom UI Design System and its bundled UI skills."