Install
$ agentstack add skill-metapages-framejs-io-framejs-css-sync ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
framejs.io ⇐ framejs.app design system ("blueprint")
This repo's surfaces are styled to copy framejs.app, not invented here. framejs.app (the framejs-nhost sibling repo) owns the design system; we mirror it so everything reads as one product. See [[framejs-origins]] for the repo split.
Two surfaces mirror it, each with its own bridge because each uses a different framework:
| Surface | Framework | Bridge file | | -------- | -------------------- | --------------------------------------- | | Editor | Chakra UI v2 (React) | editor/src/styles/theme.ts | | Docs | VitePress | docs/.vitepress/theme/blueprint.css |
The rest of this doc covers the editor first, then the docs.
Design language: "Blueprint / drafting table" — warm technical paper ground, cobalt blueprint ink accent, hairline warm-gray rules, low radii (3–4px), IBM Plex Sans (body/chrome) + IBM Plex Mono (labels/wordmark/data/code).
The core tension
- framejs.app is Tailwind v4 + Preact (Deno Fresh). Its entire design
system is one file: assets/styles.css (CSS custom properties + @theme inline Tailwind mapping + component classes .btn/.plate/.eyebrow/.field + fonts).
- This editor is Chakra UI v2 + React (Vite). No Tailwind.
So we can't share the file verbatim. Instead we mirror the tokens, component classes, and fonts as plain CSS, and bridge Chakra to the same values.
Source of truth (in the sibling repo)
| What | Path (in framejs-nhost) | | --------------- | ---------------------------------------------------------- | | Stylesheet | frontend/worker/assets/styles.css | | Fonts (6 woff2) | frontend/worker/static/fonts/ibm-plex-*.woff2 | | HTML shell/theme| frontend/worker/routes/_app.tsx (theme-color, data-theme)|
Sibling repo root on this machine: /Users/dion/dev/git/metapages/framejs-nhost.
Where it lands here
| File | Role | | ----------------------------------- | ---------------------------------------------------------- | | editor/src/styles/blueprint.css | Plain-CSS mirror of styles.css — tokens + classes + fonts| | editor/public/fonts/*.woff2 | The 6 IBM Plex woff2, served by Vite at /fonts/ | | editor/src/styles/theme.ts | Chakra bridge: maps blueprint token values onto Chakra | | editor/src/index.tsx | import "/@/styles/blueprint.css" | | editor/index.html | ` + theme-color = #fbfaf7` |
How styles.css → blueprint.css (the transform)
blueprint.css is styles.css with the Tailwind-only bits removed. When re-merging, apply exactly these transforms and copy everything else verbatim:
- Drop the first line
@import "tailwindcss";. - Drop the
@theme inline { … }block (it only generates Tailwind
utilities). BUT preserve the non-color tokens it defines by adding them to :root as plain custom properties: --font-sans, --font-mono, --radius-plate, --radius-control, --ease-out.
- Unwrap the
@layer base { },@layer components { },
@layer utilities { } wrappers into plain rules. (Emotion injects Chakra's styles unlayered, which would out-prioritize layered rules — so don't keep the layers.)
- Move
body { background/color }out. Chakra's CSS reset sets the body
bg/color and wins by injection order, so the paper/ink body styles live in theme.ts styles.global, not here. Keep ::selection and :focus-visible.
- Keep verbatim: all
@font-faceblocks (paths are/fonts/…, same as
framejs.app), the full :root light palette, the dark palette blocks (kept dormant — see below), and the component classes .eyebrow/.btn*/.field/ .plate/.bg-grid/.ticks/.tabular.
Hero-specific animation classes (.hero-plot*, .live-dot, .overlay-in, .panel-in) are framejs.app landing-page-only — omit them here unless a matching component is added.
The Chakra bridge (theme.ts)
Chakra can't consume .btn/.plate classes, so theme.ts remaps Chakra's own scales onto the blueprint neutrals + accent and sets the fonts:
fonts.body/heading→ IBM Plex Sans;fonts.mono→ IBM Plex Mono.colors.gray.{50..900}→ blueprint neutrals (surface / surface-2 / line /
line-strong / ink-3 / ink-2 / ink), so existing components (header, panels, dividers that use gray.*) adopt the palette with no per-component edits.
colors.blue.{...}→ cobalt accent (--accent/--accent-hover/--accent-soft).borders."1px"→ warm hairline (--line) soborderBottom="1px"follows.styles.global.body→ paper bg + ink text.
> Important: theme.ts uses literal hex, NOT var(--token). Chakra runs > build-time color math (transparentize, etc.) that chokes on CSS variables. > This means token values are duplicated between blueprint.css (:root) and > the const block at the top of theme.ts. When a hex value changes upstream, > update both.
Re-merge checklist (when framejs.app's style changes)
diff <(sed …) …— compare the siblingassets/styles.cssagainst our
editor/src/styles/blueprint.css to see what moved. (They won't match line-for -line because of the transforms above; scan for changed token values, new component classes, and new @font-face.)
- Re-apply the transforms in "How styles.css → blueprint.css" for any changed/new
rules.
- If any color token hex changed: update the mirrored
constintheme.ts
to match. This is the easiest step to forget.
- If fonts were added/changed: copy the new woff2 from the sibling
static/fonts/ into editor/public/fonts/.
- If theme-color / data-theme default changed: update
editor/index.html. just check(types), then visually verify — run the stack and screenshot the
editor header + Settings panel. The editor is embedded at /editor/; see [[framejs-origins]] for the local dev URL. (If the editor window is blank after a git op, that's the bind-mount gotcha, not CSS — recreate the editor container.)
Surface: docs (VitePress)
The docs (docs/, VitePress with the default theme) get the blueprint look by overriding VitePress's own --vp-* CSS variables — you never touch Chakra here.
Files:
| File | Role | | ------------------------------------------ | ------------------------------------------------ | | docs/.vitepress/theme/blueprint.css | Blueprint tokens + --vp-* mapping (light+dark) | | docs/.vitepress/theme/fonts/*.woff2 | The 6 IBM Plex woff2 | | docs/.vitepress/theme/index.ts | import "./blueprint.css" | | docs/.vitepress/theme/HomeLayout.vue | Hero URL callout set to --vp-font-family-mono |
How it works:
blueprint.cssdeclares the framejs.app tokens twice: light in:root, dark
in .dark (VitePress's dark-mode class). Unlike the editor, docs support both modes — VitePress has a built-in appearance toggle and framejs.app ships both palettes, so wire both.
- It then maps VitePress variables onto the tokens (here
var(--token)is safe —
no Chakra color math): surfaces (--vp-c-bg{,-alt,-soft,-elv}), text (--vp-c-text-{1,2,3}), hairlines (--vp-c-border/-divider/-gutter), brand→accent (--vp-c-brand-{1,2,3}, -soft), brand buttons, and the home hero name (--vp-home-hero-name-color = accent, -background = transparent to kill the default gradient). Fonts via --vp-font-family-{base,mono}.
- A few structural rules restore the low-radius plate feel VitePress otherwise
rounds heavily: .VPButton, .VPFeature, code blocks → --radius-control/plate.
Fonts — the base-path catch: docs run under base: "/docs/" (config.ts), so a root-absolute /fonts/… would resolve to the site root, not /docs/. So the docs fonts are co-located in theme/fonts/ and referenced relatively (url("./fonts/…")) — Vite processes them and rewrites to the correct /docs/assets/… hashed URL. (This differs from the editor, which serves fonts from public/fonts/ at /fonts/.) Validate with just docs/build and grep the built dist/assets/*.css for url(/docs/assets/ibm-plex-…).
Re-merge for docs: same idea as the editor checklist — diff the sibling styles.css, and if token values changed, update them in docs/.vitepress/theme/blueprint.css :root/.dark. Because the --vp-* mapping uses var(--token) (not hex), only the token declarations need editing, not the mapping. Verify with just docs/build + a visual check of the home page and one content page in both light and dark.
Deliberate divergences (don't "fix" these)
- Light-only.
editor/index.htmlpinsdata-theme="light", so the dark
palette in blueprint.css never activates. It's kept verbatim (dormant) so a future theme toggle can match framejs.app in one step. framejs.app itself is 3-way (System/Light/Dark).
- Text inputs keep the external
@metapages/metaframe-chakra-themepackage's
#ECECEC !important background — it can't be overridden cleanly from theme.ts. Not blueprint-warm, but low-priority; changing it needs a full Chakra Input override or editing the package.
- Code editor pane is an external Monaco metaframe (
editor.mtfm.io) with its
own styling — out of scope for this sync.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: metapages
- Source: metapages/framejs.io
- License: MIT
- Homepage: https://framejs.io/docs
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.