SuperAPI AI Directory

ui-kit-from-code

Design & creation

View repository

Updated 10-16 02:41

About this skill

Skill: ui-kit-from-code

WHAT

Purpose and context

  • Purpose: build a standalone HTML file so people (designers, devs, agents) can look up and reuse the project's REAL design system like a Figma UI kit: view every token and component, switch themes in place, copy tokens or markup directly to take away.
  • Trigger: "extract it into a UI kit", "figma ui kit in html", "design system reference page", "component gallery for reuse". Usually encountered after the project already has stable tokens and components but no page gathers them together.
  • Non-goals: inventing a new system or adjusting aesthetics (→ hallmark); cloning the whole page to run it again (→ extract-site / web-clone); exporting a real Figma file, Storybook or npm package; modifying the project's component code.

Mental model

nguồn token (TS/CSS/Tailwind/Compose…) ──chạy thật──▶ JSON token ──▶ CSS vars --<ns>-<role> + --k
component trong code (kích thước, variant, tên kind) ──đọc──▶ class .<ns>-* chỉ dựa vào vars
URL gốc ──discover.mjs (link BFS · mở phần ẩn · chữ ký · bão hoà)──▶ inventory.json + crops ──▶ danh mục phải dựng
kit.html + inventory.json ──--coverage──▶ phủ 100% (data-sig / ui-kit-skip)
                                   └──▶ ui-kit.html (7 phần bắt buộc) ──Playwright──▶ bằng chứng

The kit is a verified copy of the system in code: every color and measurement can be traced back to a constant or function in the repo. Where something must be drawn to spec because the code does not state it clearly, it must be declared (RULE-04).

Input and output contract

Field Required? Meaning
In token source yes file/function generating colors, type, shape, sizes (e.g. lib/tokens.ts → paletteOf)
In component inventory yes type name (kind/component name) + groups as the app currently divides them (palette, sidebar, docs)
In target platform yes mobile / desktop / both, with real frame sizes
Out docs/ui-kit.html (or the path the user chooses) yes 1 file, openable via file://, containing all 7 sections below
Out provenance report yes which parts come from code, which parts are drawn to spec
Out Playwright evidence yes 0 JS errors, scrollWidth - innerWidth = 0 at 390px, light + dark screenshots + sample screen
Out version on the kit repo yes (except UI_KIT_SYNC=0) <id>/vN/index.html + manifest.json + line registry.json on the PRIVATE repo Rheinmir/ui-kits (W09)

Rules and capabilities

  • RULE-01 (MUST): colors and measurements are obtained by running the project's token code (test runner/temporary script, deleted after dumping), not by typing hex by hand. If the code can only be read but not run (CSS vars, JSON), parse the file.
  • RULE-02 (MUST): every component class references only CSS var (--<ns>-<role>, --k); no loose hex/rgb in component CSS. Thanks to that, the block :root + CSS .<ns>-* can be moved to another project and just works.
  • RULE-03 (MUST): each component tag records exactly the name in the code (kind/component name + main fields) and measurements (dp/px) as used in the code.
  • RULE-04 (MUST): declare in the report (and note on the tag if needed) every part drawn per spec or simplified (e.g. morph animation is rotation only); do not present it as a 1:1 copy.
  • RULE-05 (MUST): all 7 structural sections below are required; for any missing section, state the reason in the report (e.g. the app has no desktop).
  • RULE-06 (MUST): self-contained file: fonts only from Google Fonts, JS/CSS inline, no build, no fetching external data.
  • RULE-08 (MUST): Do not stop at the skeleton. The component inventory must come from the W01b discovery loop (follow links, click to open hidden parts, stop when saturated), not just from a few known pages/files. Every pattern in inventory.json must have a tag in the kit carrying data-sig="<sig>", or be omitted with a reason via <meta name="ui-kit-skip" content="<sig>:<lý do>;…">. Gate: node references/discover.mjs --coverage inventory.json kit.html rc 0.
  • RULE-10 (MUST): Components must not have only an idle state. Every state that W01b measured (hover, focus-visible, active, selected) must appear in the kit: the tag shows a variant in that state (class is-hover / is-focus / is-active / is-on copies the exact rule :hover… of the site) and carries data-sig-state="<sig>:<state>". Or it is omitted with a reason via ui-kit-skip "<sig>:<state>:lý do". If focus is only the browser outline:auto (ua: true), the kit states clearly "focus is the browser default, the site does not style it separately". Gate --coverage also counts states.
  • RULE-11 (MUST): Respect robots.txt and the site's terms. discover.mjs read /robots.txt before scanning. User-agent: * blocks the starting path, or there is a note prohibiting automated collection (e.g. facebook.com) → stop rc 3, do NOT add --i-have-permissionyourself. Only use that flag when the user confirms they have a license. Instead, propose B05: the user saves the page themselves.
  • RULE-09 (MUST): if the source is someone else's site, do not embed their images, logos, SVG wordmarks or copyrighted content in the kit; replace with a placeholder block stating clearly "image …". The kit syncing itself to a PRIVATE repo (W09) is fine; to share it OUTSIDE that repo (public link, hosting), ask first (B03).
  • RULE-12 (MUST): Only sync a kit that has PASSED (W05b + W06 green), and only to a PRIVATE — sync-kit.py self-check visibility and refuse a PUBLIC repo (GH#187). Versions only increase: no overwriting, no deleting old vN .
  • RULE-07 (SHOULD): place the sample file references/ui-kit-reference.html alongside while working; reuse its frame, page shell CSS and control script, only swapping tokens, components, sample screens.
  • Capabilities: read the codebase; run the project's test runner/script; write 1 HTML file; run Playwright (see /playwright-verify).

Required structure of each section

In the exact order in references/ui-kit-reference.html:

# Section Required to have In the sample file
1 Control bar (sticky) select palette (if the app has multiple) · light/dark · shape scale (if the app has one) · "Copy CSS token" button for the block :root of the current selection; remember the selection with localStorage (wrap in try/catch); on first load follow prefers-color-scheme .bar · #swatches #mode #shape #copy-tokens
2 Table of contents (sidebar, hidden < 900px) group Foundations / Components / Patterns; auto-highlight the section being viewed (IntersectionObserver) .side
3 Intro eyebrow pointing to the source (<file token> → <file kit>) · h1 · 1 paragraph saying where the kit comes from and how to reuse it · a "facts" strip of the important layout constants .intro .facts
4 Foundations (a) Color: every role, cells with the text color on* in the correct pair, click to copy hex · (b) Type: type scale with name + size/line-height/weight · (c) Shape: corner radius scale multiplied by --k · (d) Layout & sizing: size scale table of the main component + table of frame/margin/spacing constants #color #type #shape #layout
5 Components group them the way the app groups them (e.g. Actions · Navigation · Content containers · Input · Feedback); each card .spec = header (human-readable name + chip name in code + measurements) → .stage show every important variant/size/state → footer (notes + Copy HTML button auto-attached); the card carries data-sig of the patterns it covers (RULE-08). If the inventory has many patterns for a group, split the cards by pattern, do not merge them into one generic card #actions #navigation #containment #inputs #feedback
6 Sample screens ≥ 1 mobile screen and ≥ 1 desktop screen (when the app supports it) at the app's exact frame size, scaled down with zoom; assembled from the actual component classes in section 5 following the app's layout rules (e.g. bottom nav on mobile → navigation rail + list-detail on desktop); a caption below the frame lists the components used #screens .phone .desk
7 Script token table embedded as ROLES + PALETTES[key].light/dark generated from the dump · apply() write CSS vars to :root · render color cells / type scale / shape scale from the data · attach copy buttons · toast to report copy (if clipboard is blocked, report manual copy) <script> at the end of the file

Common to the whole page: the page shell wears the kit's own tokens (the page changes theme together with the components); prefers-reduced-motion turn off animation; :focus-visible clear; icon buttons have aria-label; mobile 390px does not overflow horizontally (tables/sample screens are inside the container overflow-x:auto).

Failure boundaries

  • Could not find a token source (colors hardcoded scattered in components) → partial: collect the colors actually used with grep, state clearly "no token source, colors inferred from components", propose extracting tokens (separate task).
  • Site scrolls inside its own container (body is exactly 1 screen tall) → fullPage only captures 1 screen; discover.mjs open the container yourself before capturing, a self-written script must do the same.
  • States exist only when JS runs (menu opened by click, delayed tooltip, entrance animation) → :hover pure CSS is measurable, while JS-based states may slip through; W01c looks at the crop *-hover.png then adds them manually. Press-and-hold (active) is measured with mouse.down → drag the mouse outside → mouse.up, so it does not activate the link. If it cannot be measured, state it clearly in the report.
  • Page with JS disabled (B05): states attached by JS (e.g. FDS Facebook's hover/pressed overlay class) cannot be measured. Reconstruct from the corresponding token (--hover-overlay, --press-overlay), state clearly "reconstructed from token, not measured". Very heavy pages (feed with 3000+ elements) need --page-timeout high.
  • Exploration hits the page ceiling (stoppedBy: page-budget) while the last page still yields new patterns → tell the user, propose increasing --pages or narrowing --include, do not consider the kit sufficient.
  • Tokens only run in a heavy runtime (needs DB/app server) → dump with Playwright from the running app (getComputedStyle on :root), state clearly how to obtain them.
  • Project has only one platform → section 6 has only that screen, state the reason.
  • Playwright reports a JS error or horizontal overflow after 3 rounds of fixes → stop, report the remaining error.
  • W09 sync fails due to network / no push permission / repo not PRIVATE → script prints "not synced, reason …" rc 0; the local kit is still a valid result, report the reason in W08.

HOW

Main workflow

Step Type Inputs Action Outputs/exit Failure/next
W01 judgment codebase find token source, component catalog + groups, mobile/desktop frame sizes, schema documentation (if any) anchor list file:line no tokens → partial (see Failure)
W01b deterministic root URL (dev app or site) exploration loop: NODE_PATH=$(npm root -g) node references/discover.mjs <url> --out <scratch>/disc --pages 32 --saturate 4 (needs global Playwright; ~40 s/page, runs in background) (BFS same-origin links, scroll all inner containers, click to open accordions/tabs/details, signature = type + style + child shape, dedupe; for clickable patterns also force HOVER / ACTIVE / FOCUS-VISIBLE via CDP CSS.forcePseudoState (like DevTools' "Force state": deterministic, no drag-and-drop, no click, works with JS disabled), and match the SELECTED variant via aria-pressed/selected/current, data-state, class is-active) inventory.json + crops/*.png + image of each page stoppedBy: page-budget still produces new ones → increase budget (Failure)
W01c judgment crops + page images VIEW every new crop and full-page image; group patterns into kit groups, record patterns the classifier missed (e.g. trapezoid tabs, flow diagrams) in the list to be built pattern table → group → kit card —
W02 deterministic token anchor write a temporary test/script that calls the real token function, dump JSON (every palette × light/dark, size scale) → delete the temporary file tokens.json in scratchpad run fails → static parse
W03 effect JSON reduce to ROLES + PALETTES and embed into a copy of the sample file data block in <script> —
W04 judgment component code rewrite sections 4–5: one class per component .<ns>-* use measurements from code; card .spec with full header/stage/footer sections 4–5 missing measurements in code → draw per spec + declare (RULE-04)
W05 judgment app layout rules assemble section 6: mobile + desktop screens from the classes in W04 section 6 single-platform app → record the reason
W05b deterministic kit + inventory node references/discover.mjs --coverage <scratch>/disc/inventory.json <kit.html> phủ N/N (100%) + trạng thái M/M rc 0 MISSING pattern → build card; MISSING sig:state → add state variants to the card (RULE-10) or ui-kit-skip with reason; rerun
W06 deterministic file Playwright: load the page, count pageerror, capture top + middle + sample screen, enable dark + another palette and capture again, viewport 390 measure horizontal overflow images + metrics errors → fix, up to 3 rounds
W07 judgment images look at the images in one pass, fix visible layout errors (text wrapping, misaligned items), capture once more kit done —
W08 judgment results report to user: file path, contents of the 7 sections, which sections come from code / per spec, W06 evidence, W09 sync results report —
W09 effect kit that PASSED W05b + W06 python3 references/sync-kit.py <kit.html> --source <thư mục repo | URL gốc> (add --id <id> when the source cannot infer the id, e.g. the app after login). Id: repo → <owner>-<repo> from origin (no remote → <thư mục>-local); URL → host without www., dots → dashes. Hash sha256 excluding meta ui-kit-id; same as latest version → "unchanged from vN", no commit. Different → vN+1 + latest/, push, if rejected then pull and recompute up to 3 times. UI_KIT_SYNC=0 skip; UI_KIT_SYNC_REPO change repo ✓ <id>: vN → <link> or = không đổi error → "not synced, reason" rc 0 (Failure)

Sample token dump (vitest, TS): create lib/zz-kit-dump.test.ts call the token function then writeFileSync(<scratchpad>/tokens.json, …), run npx vitest run lib/zz-kit-dump.test.ts, then rm file. Do not let temp files leak into the project diff.

Sample Playwright check: like /playwright-verify: chromium.launch() → goto(file://…/ui-kit.html) → page.on("pageerror") → screenshot → click('#mode button[data-v="dark"]') → page 390×844 measure document.documentElement.scrollWidth - innerWidth.

Branches

ID Kind Guard Behavior Skip / failure Rejoin
B01 conditional_required token is CSS vars / Tailwind config / JSON (not a function) static parse instead of running (W02) — W03
B02 conditional_required app has schema documentation for the agent (e.g. agent.md) chip "name in code" records the exact field name of the schema, sample screen follows the rules in that documentation none → use component name W04
B04 conditional_required source is the URL of a running site (no code) W02 replaced by Playwright: read CSS vars at :root/.dark + getComputedStyle of the real element; color lab()/oklch() convert to hex; apply RULE-09 has code → skip W03
B05 conditional_required site forbids crawlers (RULE-11) or the interface is behind login user saves the page themselves ("Webpage, Complete") into a folder → python3 -m http.server 8765 --bind 127.0.0.1 → discover.mjs http://127.0.0.1:8765/<file>.html --offline --no-js --page-timeout 1500 (block all outbound requests, disable JS so no script calls back to the site). The saved page contains personal data: the kit only takes style/structure, all names/images/content replaced with samples, check by grepping real names before delivery site allows crawling → W01b as usual W01c
B03 user_optional user wants to share the link outside the private kit repo upload the file to the Artifact/hosting the user chooses; the file stays in the repo default: local file + private kit repo (W09) W08

Validation and stopping

Machine check: deterministic test of W09 python3 -m pytest harness/tests/test_ui_kit_sync.py (id, hash, version count, v1 immutable); W05b coverage gate rc 0 (every pattern has data-sig or ui-kit-skip); W06 must give pageerror = 0 and horizontal overflow = 0 at 390px. Visual check: all 7 sections present per the structure table; compare light/dark images; try the Copy CSS token button and 1 Copy HTML button. Maximum 3 revision rounds at W06, W07 just re-screenshot once.

Examples

  • Positive: m3e-canvas (Next.js, Material 3 Expressive editor). Dump paletteOf() via vitest into 7 palettes × light/dark; the kit has 25 color roles, 15 type styles, 3 shape levels, about 30 components split into 5 groups like the app's Parts table, mobile screens 412×892 (list + detail) and desktop 1280×800 (rail + list-detail); Playwright 0 errors, 390px no overflow. The main result is references/ui-kit-reference.html.
  • Positive (URL): drafted.ai. The first time only 3 known pages were scanned so the kit was just a skeleton. Ran W01b from /learn/ai-for-homebuyers with --pages 32 --saturate 4: discover.mjs went through 32 pages (learn, use-cases, house plan listing pages, FAQ, news, careers, onboarding), collected 171 patterns, stopped because 4 consecutive pages had nothing new (~40 s/page). W05b first reported phủ 62/171 (36%). After building additional groups House plan listings, Articles & sections, a real type table generated from the inventory, and recording 22 ui-kit-skip (images, third-party embeds, page frames), the gate reported phủ 171/171 (100%). W01c also caught one more pattern the classifier missed: trapezoid tabs drawn with SVG. Round 3 added state measurement: 175 patterns, 104 states (44 hover, 56 focus, 1 active, 3 selected pairs). First time trạng thái 0/104. After adding 23 state strips and a "Focus · 2 styles" card, the gate reported 175/175 + 104/104. The measurements also fixed 6 previously mis-guessed hover rules (e.g. the selection card hovers to stone-500, not stone-900), and showed that 44/56 focuses are just the browser default outline.
  • Positive (B05, site bans crawlers): facebook.com. robots.txt bans automated collection so discover.mjs stopped with rc 3, the agent asked the user. The user saved the News Feed (6.4 MB + 252 resources), ran offline + with JS off (blocked 49 outbound requests). Got 579 FDS variables for both .__fb-light-mode and .__fb-dark-mode from the saved CSS, 78 patterns, 5 measurable states (FAB hover changes shadow, story active scale(.98), link hover underline). Button hover/pressed built from --hover-overlay/--press-overlay. Kit 27 cards, 66 color roles; gate 78/78 + 5/5; grep 0 real names; 0 outbound requests.
  • Boundary/failure: React repo has only hex colors scattered across 40 files .module.css, no tokens → partial: grep found 18 colors actually used, the kit notes "colors inferred from components", recommends extracting tokens before full kit-ification.