Roblox UI maker
Designs game UI in HTML first, from your style reference (stud style by default), with every state and a phone layout. Once you approve the mockups it exports them, uploads the art and builds a real ScreenGui with ready-made Luau modules (buttons, toasts, shops, crate reels, number formatting), then checks it with a mock render and property sweeps.
You bring: Textures from ui-resources.com and an icon pack you own a licence for. Neither ships in the zip.
SKILL.md
LICENCES FIRST. Icons come from ONE licensed icon pack that you (the developer) own; this skill ships no
icons and no textures. Read your pack's licence: most paid packs allow use in your own games but forbid
resale, redistribution, asset packs and AI training (if so, never send the pack's PNGs to any image model).
In game repos, commit icons.json + the copy script, not the pack, unless the licence allows it. Textures
are downloaded by you from https://ui-resources.com (UI_RESOURCES.md); credit their creators.
Figma import tools are for designs that already exist in Figma; this skill is for designing new UI (or remaking a screenshot) through HTML first.
The loop: reference -> tokens -> HTML (all states) -> the developer checks -> learn -> import -> verify.
Read LESSONS.md before touching anything (every rule learned so far, with the bug behind it). It wins
over anything below that disagrees.
Files in this skill
| File | What |
|---|---|
LESSONS.md |
every rule + why. Read first; write the developer's feedback into it (REVIEW_LOOP.md) |
STYLE.md |
reading the reference into tokens; presets; CSS token -> Roblox table |
UI_RESOURCES.md |
ui-resources.com: what it is, finding/downloading, the stud texture and where to put it |
PATTERNS.md |
HUD, shop, crate reel + odds, toasts, confirm, loading, tutorial, bubble, goal pill, lists |
PIPELINE.md |
HTML -> JSON -> Roblox in depth: rbx.json, export markers, mapping table, ledger, templates, copies |
REVIEW_LOOP.md |
how to show screens, take feedback, get approval |
VERIFY.md |
mock render, property sweep, edit-mode harness, playtest clicks, checklists |
ROBLOX_FACTS.md |
measured engine + MCP tool facts (inset, TextSize, uploads, captures) |
styles/ |
stud.css (default preset), glass.css (dark simulator preset) |
templates/examples/ |
a working UI project: build.py, kit.css, icons.json, rbx.json |
templates/luau/ |
Builder, UIKit, NumberFormat, Theme, client/UI + Controller, Toasts, UpdateInPlace, PixelRows, ProgressiveReveal, NextGoal, CrateRoll, LoadingScreen, PropertySweep, WorldHitTest |
scripts/ |
read_reference, copy_icons, cdp/export/dump, gen, ledger, assets, bundle, mock/mockshot/mockdiff, check_copies, luau_to_rbxmx; remake/ = 1:1 measuring tools |
SK=<this skill's folder> below (the folder this SKILL.md is in). Tools you need: python3 with PIL + numpy,
node 22+, Google Chrome (headless renders; env CHROME overrides the macOS path), luau-analyze, a browser
pane or browser automation tool for viewing pages (in Claude Code desktop: mcp__Claude_Browser__*, load with
ToolSearch), a Studio MCP server that can run Luau and import .rbxmx files (this skill was written against the
community robloxstudio MCP) and the built-in Roblox_Studio MCP for upload_image only (ROBLOX_FACTS.md).
1. Brief, references, style (required: a style reference)
- Get the brief: which screens, what they do, what data they show, PC + phone. List every state each needs.
- A style reference image is required. Use the one the developer gave. If none: ask once ("send a screenshot of the style you want; or I'll use the stud style"). If they say just go: stud preset. Most games are stud.
- Do the project setup in step 4 first (it creates
ui/), then save the reference asui/ref.png. Layout references (another game's shop) are separate from the STYLE reference. - Read it from inside
ui/(STYLE.md section 2):python3 $SK/scripts/read_reference.py ref.png-> palette, outline px, texture hint, text style, density +ref_palette.png,ref_zoom.png(Read it to SEE studs/gloss) and a draftstyle.css. Confirm each value by eye, keep only overrides of the chosen preset. - "Like game X" = inspired by, not a copy, unless the developer says "remake this 1:1" (then LESSONS section 7
and
scripts/remake/).
2. Textures from ui-resources.com
Stud style MUST carry a stud texture. This skill does not bundle one: download the ui-resources "Stud texture"
(tag Stud) yourself from https://ui-resources.com, convert it to a real PNG if needed, and save it as
ui/stud.png in your project (4x4 studs per tile, tiled at 112 px with overlay blend). For anything else
(paper, sunbursts, halftones, backgrounds) search the site first and save downloads in ui/tex/
(UI_RESOURCES.md: filters, ?tag=Stud, reading lazy-loaded URLs, file + sips for WebP-named-png).
Credit creators in build.py's header.
3. Icons: your icon pack only
Icons come from ONE licensed vector icon pack the developer owns (one vector pack per project; never mix
packs), never emojis. Find icons by browsing or searching the pack's own folder or catalogue (e.g.
find <pack> -iname '*coin*'). After the step-4 setup, edit ui/icons.json ({"slug": "Icon file name or path inside the pack"}; the example's list is a starting point) and run
python3 $SK/scripts/copy_icons.py icons.json --pack <your icon pack> in ui/ (add --prefer " Outline" or
your pack's equivalent to take outlined variants first). Never emojis, close = text "X". Search synonyms
before calling anything missing.
4. Build every screen in HTML/CSS
Set up once in the game repo (do this before steps 1-3 write anything into ui/; cp -n never overwrites):
mkdir -p ui && cp -rn $SK/templates/examples/. ui/ && cd ui
cp <your downloaded stud texture> stud.png && cp -r $SK/styles . # stud texture from ui-resources.com
cp <reference> ref.png && python3 $SK/scripts/read_reference.py ref.png # -> style.css (draft)
python3 $SK/scripts/copy_icons.py icons.json --pack <your icon pack> && python3 build.py # -> *.html + index.html
python3 -m http.server 8870 --bind 127.0.0.1 # run_in_background; or the launch.json entry below + preview_start
.claude/launch.json in the game repo (then mcp__Claude_Browser__preview_start {name: "ui"}):
{ "version": "0.0.1", "configurations": [
{ "name": "ui", "runtimeExecutable": "python3",
"runtimeArgs": ["-m", "http.server", "8870", "--bind", "127.0.0.1", "--directory", "ui"], "port": 8870 } ] }
Edit build.py (it is the source of truth; never hand-edit generated .html). Rules that matter most:
- 1920x1080 stage, absolute px; shared components (
plate,panel,card,T,I) so siblings share one treatment; only colours vary. Styling comes from tokens:kit.css(structure) ->styles/<preset>.css(UI_STYLE=glass python3 build.pyto switch) ->style.css(this game).?style=glasspreviews live. - Export markers:
grp(name, html, anchor)= a top-level Roblox frame anchored to a screen edge;name=on every live label/icon/button;sub(..., btn=True, opens='Shop');hid=Truefor exported-but-hidden states. PIPELINE.md section 2 has the full table. - All states on
states.html; phone layout (hud_phone.html); every runtime element (toasts, confirm, reward, tutorial, billboards) ontemplates.html; show pieces in context over a real game capture once one exists. - No double quotes inside inline
style="..."; no element named Active/Visible/Position...; usehid(opacity 0), never display:none, for things the game toggles. - Self-check: open each page with
?checkin the browser pane (checkOverflow()must return no overflow and no broken images); render 2x headless shots (REVIEW_LOOP.md) and look at them.
5. Stop: the developer checks it
Show style.html + the first screen in the first round, then the rest (REVIEW_LOOP.md). Wait for an explicit
yes. Every correction: find the general rule, write it into LESSONS.md (rule, then why), apply it everywhere,
tell the developer in one line what you saved. No Roblox work before approval.
6. Import into Roblox (after approval)
Full detail: PIPELINE.md. From ui/ with the server running:
# first import only: the Luau runtime + builder (then replace "<Gui>" in ../roblox/ui/client/UI.luau with rbx.json "gui")
mkdir -p ../roblox/ui/client && cp -n $SK/templates/luau/*.luau ../roblox/ui/ && cp -n $SK/templates/luau/client/*.luau ../roblox/ui/client/
rm -f ../roblox/ui/LoadingScreen.client.luau ../roblox/ui/CrateRoll.luau # these live in ReplicatedFirst / ReplicatedStorage
node $SK/scripts/export.mjs # layout JSON + 2x transparent PNG per raster piece -> rbx/out/
python3 $SK/scripts/gen.py # Data_<Screen>.luau, Data_Templates, Data_Manifest -> luau_out; art -> rbx/out/upload
python3 $SK/scripts/mock.py && node $SK/scripts/mockshot.mjs # offline Roblox-semantics render: mean diff 1-3 per screen
python3 $SK/scripts/ledger.py 20 # next URLs to upload; upload with mcp__Roblox_Studio__upload_image(+studio_id),
# then: echo '<result json>' | python3 $SK/scripts/ledger.py 20 ; repeat until 0 left
python3 $SK/scripts/assets.py # AssetIds.luau
python3 $SK/scripts/bundle.py # rbx/out/<Gui>Build.rbxmx
Upload rules: <= 20 per batch, open the GROUP's place when the game is group-owned, never
re-upload what the ledger has, probe foreign ids. Then in Studio via robloxstudio:
destroy the old ServerStorage.<Gui>Build -> import_rbxm the .rbxmx into ServerStorage ->
require(game.ServerStorage.<Gui>Build.Builder).build() -> enable the gui, Builder.fit() twice, disable.
Never set_script_source (mangles \n); always .rbxmx + import_rbxm.
Wire game code (the Controller boots UI + UIKit; demo numbers only with the gui attribute PreviewMode):
client/UI.luau (open/close/back, juicy buttons with Button/Opens attributes, Escape,
scrim, popups queue, count-ups) + UIKit.luau (template contract: clone, set with Shadow twins, icons, bars,
plural/article, worldLabel) + NumberFormat.luau. Game code clones templates; it never builds a look with
Instance.new. Log every runtime change to imported screens (PIPELINE.md section 8).
7. Verify in Studio
VERIFY.md, in order: no empty images; fit ratios; PropertySweep.atSizes at 1920x1080 / 1366x768 / 844x390;
the edit-mode harness (clone the gui, require the clone's UI, drive, UI.destroy(), Archivable=false);
playtest only when needed (check nobody else's playtest is running): a magenta capture is not a broken UI;
real clicks at AbsolutePosition + size/2 +58 px; phone device profile; rapid-click test; lists update in
place. luau-analyze --mode=nonstrict every client file; check_copies.py so Studio copies match disk.
8. Iterate
New feedback -> LESSONS.md -> HTML -> approval -> export only the changed page -> gen -> ledger uploads only new
art -> bundle -> Builder.buildScreen("<Screen>") (one panel in place) or build() -> fit -> verify.
Never
Emojis or a second icon pack; AI-generated icons; icon-pack files sent to any model when the licence forbids
AI use; text baked into images; importing before approval; editing Data_*.luau by hand; set_script_source;
demo values written while the real game client runs; auto-claimed rewards; a line or marker drawn over a
prize; game-specific ids or names inside this skill folder.