一键导入
frame
Create or update an HTML frame on the Frameground canvas. Use when the user asks to create a screen, page, component, or frame for their app.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create or update an HTML frame on the Frameground canvas. Use when the user asks to create a screen, page, component, or frame for their app.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Generate N alternative designs of a frame so the user can compare them side by side on the canvas. Use when the user wants options for a screen before committing.
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Port an existing app's screens into a Frameground project as one frame per screen so they can be redesigned. Use when the user wants to bring an existing codebase into Frameground.
Generate AI-curated design tweak suggestions (palette / typography variants) for a Frameground project. Drops three named variants into the project's suggestions drawer where the user can preview and apply them with one click.
基于 SOC 职业分类
| name | frame |
| description | Create or update an HTML frame on the Frameground canvas. Use when the user asks to create a screen, page, component, or frame for their app. |
Frameground organizes work into projects. Each project lives in its own directory and contains:
<project-dir>/
PROJECT.md project idea + frames list
DESIGN.md committed aesthetic direction (see frontend-design skill)
frames.json [{ id, name, file }]
.opendesign/layout.json { [frameId]: { x, y, w, h } }
<frame-id>.html one file per frame, fully self-contained
You edit these files directly with Read/Write/Edit. The Frameground dev server watches the filesystem and pushes changes to the canvas over SSE — no API calls needed for frame content.
Ask the dev server where projects live:
curl -s http://localhost:5173/api/workspace
Response:
{
"root": "/absolute/path/to/projects",
"projects": [
{ "id": "demo", "path": "/absolute/path/to/projects/demo" }
]
}
Parse $ARGUMENTS — the first token should be the project name. If it's missing or doesn't match an existing project, ask the user which project to use (or list the options). If they want a fresh project, create it via POST /api/projects — see "HTTP API reference" at the end of this doc. The rest of $ARGUMENTS is the frame description.
Also extract these flags out of $ARGUMENTS before treating the remainder as the description:
--no-suggest — skip the auto-suggestion step at the end (Step 10).Use the path field from the response — don't guess paths.
If curl fails (server not running): check $OPENDESIGN_PROJECTS_ROOT, or ask the user for the project directory path.
Read <project-path>/PROJECT.md, <project-path>/DESIGN.md, and <project-path>/FEEL.md. If any is missing (older project), create it from the template the server uses for new projects. DESIGN.md holds spec-compliant tokens + canonical prose; FEEL.md holds motion, spatial composition, and background/texture prose.
DESIGN.md and FEEL.md together drive the frame's aesthetic. Two cases:
TODO): treat it as law. Aesthetic direction, fonts, colors, motion, and composition rules are fixed for this project. Stay consistent.colors, typography, rounded, spacing, components) are all empty ({}), and the prose sections in both DESIGN.md and FEEL.md start with TODO:. HARD STOP — do not write any HTML yet. The first frame commits the aesthetic for every future frame in this project; you don't get to pick it silently. Follow the "First-frame protocol" in Step 4 before proceeding. Write the committed choices into DESIGN.md and FEEL.md in Step 8 before any subsequent frames read them.PROJECT.md gives product context (concept, naming conventions, existing frames) that shapes what the frame should be and say.
Read <project-path>/frames.json to see what's already there. Use this to:
id (lowercase kebab-case, e.g. login-page).Read <project-path>/.opendesign/layout.json to see current positions and pick a non-overlapping spot for the new frame.
TODO in Step 2)The user says "wacky" / "clean" / "fun" / "modern" — that's a mood, not a direction. A dozen aesthetics fit each mood. Your job is to turn the mood into a committed direction with the user's buy-in before it becomes law for every future frame in this project.
AskUserQuestion in Claude Code); otherwise print a numbered list and wait for the reply. Do not start writing.frontend-design skill with the user's answers. It returns three artifacts per its Output Contract: (a) YAML tokens for DESIGN.md's front-matter, (b) DESIGN.md prose blurbs, (c) FEEL.md prose.Skipping this protocol because the user's phrasing sounds "confident enough" is how a project ends up with an aesthetic they never actually chose. Don't skip it.
Write <project-path>/<id>.html.
Delegate the actual design and implementation to the frontend-design skill — it owns aesthetic decisions (typography, color, motion, composition, backgrounds). Feed it:
Hard constraints (these override stylistic preferences):
<style>, inline JS in <script>.<meta charset="UTF-8"> and <meta name="viewport" ...>.<head> (see below). Reference DESIGN.md tokens as CSS variables (var(--colors-primary), var(--typography-display-family), var(--spacing-md), var(--rounded-lg), var(--components-button-radius)) — never inline literal token values. Changing DESIGN.md should update this frame live.Do NOT fall back to the generic system font stack or Inter/Arial/Roboto. Pick distinctive fonts per the frontend-design skill.
Every frame's <head> must include this block (with PROJECT_ID substituted for the actual project id). It wires the frame to DESIGN.md tokens so edits propagate live without reloading the iframe.
<link rel="stylesheet" href="/frames/PROJECT_ID/shared.css">
<style id="od-tokens">/* tokens injected by Frameground canvas */</style>
<script>
(function(){
window.addEventListener('message', function(e){
if (!e.data || e.data.type !== 'od-tokens') return;
var el = document.getElementById('od-tokens');
if (el) el.textContent = e.data.css;
});
if (window.parent === window) {
fetch('/api/projects/PROJECT_ID/tokens.css', {cache:'no-store'})
.then(function(r){ return r.text(); })
.then(function(css){
var el = document.getElementById('od-tokens');
if (el) el.textContent = css;
}).catch(function(){});
}
})();
</script>
Load order matters: shared.css (static, project-level raw CSS) → od-tokens (injected tokens from DESIGN.md via the canvas) → the frame's own inline <style>. Later layers win — so the frame can override tokens for one-off tweaks.
Variable naming follows DESIGN.md's YAML paths kebab-joined (camelCase → kebab-case at each segment):
| YAML path in DESIGN.md | CSS variable |
|---|---|
colors.primary | var(--colors-primary) |
colors.surface | var(--colors-surface) |
spacing.md | var(--spacing-md) |
rounded.lg | var(--rounded-lg) |
typography.display.fontFamily | var(--typography-display-font-family) |
typography.display.fontSize | var(--typography-display-font-size) |
typography.display.fontWeight | var(--typography-display-font-weight) |
typography.body.lineHeight | var(--typography-body-line-height) |
components.button-primary.backgroundColor | var(--components-button-primary-background-color) |
components.button-primary.rounded | var(--components-button-primary-rounded) |
Reference resolution: {colors.primary} in DESIGN.md components resolves to var(--colors-primary) in the output CSS, so indirection is preserved. Object refs like typography: "{typography.body}" don't resolve to a useful single variable — reference individual leaves (var(--typography-body-font-family), etc.) instead.
frames.jsonRead <project-path>/frames.json, append the new entry, and Write the file back:
{
"frames": [
{ "id": "login-page", "name": "Login page", "file": "login-page.html" }
]
}
Read <project-path>/.opendesign/layout.json (create the .opendesign/ directory if it doesn't exist). Add an entry for the new frame and Write it back:
{
"login-page": { "x": 200, "y": 200, "w": 800, "h": 600 }
}
Position heuristic: offset each new frame by (900, 0) from the rightmost existing frame. When x > 3000, reset x to 200 and add 700 to y. Start at (200, 200) if the project is empty.
Size by kind:
1280 x 800390 x 844400 x 300500 x 400800 x 600Tell the user the frame was created and the name. The canvas picks up the new frame automatically via the file watcher.
Keep PROJECT.md, DESIGN.md, and FEEL.md in sync:
## Frames: - **<name>** — <one-sentence purpose>. Replace the _(none yet)_ placeholder the first time. If ## Concept is still TODO and the user's intent is now clear, fill it in.--- fences at the top). Replace empty maps with real tokens per the frontend-design Output Contract. Use the Edit tool on the literal YAML text; empty maps like colors: {} are unique anchors. Match 2-space indentation.TODO: with a short paragraph per the Output Contract.TODO: in Motion, Spatial Composition, and Backgrounds & Textures with the prose from the frontend-design skill's third artifact. Leave genuinely undefined sections as TODO:.Leave genuinely undefined groups as empty maps and TODO: bodies — don't invent a design system the user hasn't asked for. Use the Edit tool. The server does not need to be notified.
After updating DESIGN.md, run the @google/design.md linter against it:
node ./node_modules/@google/design.md/dist/index.js lint <project-path>/DESIGN.md --format json
(Invoke via node directly rather than npx — the CLI's bin name design.md collides with the .md file extension on Windows and can misfire.)
Parse the JSON output. Surface error-severity findings verbatim so the user can fix them (broken token refs, etc.). Warnings and info are noise — mention only if the user asked for a strict review. Never roll back a frame because lint complained — lint is advisory.
FEEL.md is not linted; it's freeform prose.
Unless --no-suggest was passed in Step 1, finish by invoking the /suggest skill twice for this project — once for palette, once for typography. Pass --source frame so the panel can badge the cards as auto-seeded:
/suggest <projectId> palette --source frame
/suggest <projectId> typography --source frame
This drops two cards into the project's Tokens panel ("Suggestions" section) the moment the frame appears on the canvas — three palette variants and three typography variants the user can preview and apply with one click. Each is scratch (no DESIGN.md edit until they hit Commit), so it's safe to seed even on filled projects.
Skip this step when:
--no-suggest was passed.Always prefer direct file edits for frame content, frames.json, and .opendesign/layout.json — single frame or many, create or update or delete. The dev server watches the filesystem and reconciles on its own. The canvas sees changes either way, and the diff stays reviewable.
The HTTP API exists for one thing this skill can't do via files: creating a new project (the server has to bootstrap PROJECT.md, DESIGN.md, design-reference.html, and seed the manifest/layout — don't try to reproduce that by hand).
curl -s http://localhost:5173/api/workspace
# → { "root": "/abs/path", "projects": [{ "id", "path" }] }
curl -s -X POST http://localhost:5173/api/projects \
-H 'Content-Type: application/json' \
-d '{"name":"<project-id>"}'
# 201 → { "id": "<project-id>" }
# 400 → "Invalid project name" (your id failed the regex)
# 409 → "Project already exists"
Gotcha: the body field is called name, but it is validated as an id, not a human-readable name. It must match ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$ — lowercase kebab-case by convention (e.g. tinder-agents, not "Tinder for Agents"). If you get "Invalid project name", your slug has spaces, uppercase, or forbidden chars.
Other endpoints exist (frame POST/PATCH/DELETE, layout PATCH, SSE events) and are documented in server/api.ts if you ever need them — but for this skill, file edits are the intended path.
Just edit <project-path>/<id>.html with the Edit tool. The file watcher detects the change and the canvas reloads the iframe automatically. No manifest or layout changes needed.
Edit the name field of the relevant entry in <project-path>/frames.json. Don't change the id — ids are stable. Also update the name in PROJECT.md under ## Frames.
Remove the entry from frames.json, remove the matching key from .opendesign/layout.json, and (optionally) delete the HTML file. Remove the frame's bullet from PROJECT.md under ## Frames. If that was the last frame, restore the _(none yet)_ placeholder.
Write all HTML files first, then update frames.json and .opendesign/layout.json once with all the new entries. Update PROJECT.md's ## Frames section once at the end with all new bullets.