roblox-ui-hud
Use when building or fixing Roblox game UI/HUD in Luau — responsive layout across phone/tablet/desktop, ScreenGui panels, progress bars/meters, wallet/nav chrome, and the layout gotchas that bite (UIScale vs position, horizontal UIListLayout order, boolean-visibility bug, font glyphs, click-outside-close). Also the copy/clarity rules that make a HUD read well.
How do I install this agent skill?
npx skills add https://github.com/flatbedj/roblox-skills --skill roblox-ui-hudIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill contains development guidelines and Luau code snippets for Roblox UI/HUD design. No security issues, malicious code, or suspicious patterns were found.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Roblox game UI / HUD patterns
Hand-built HUDs (not auto-scaled templates) that hold up across devices and read clearly.
Responsive layout across devices
UIScale(e.g. afitScalehelper) scales an element's SIZE about its anchor — NOT its position. So scaling alone doesn't reposition; recompute positions per device where it matters. Don't nest two UIScales on one element (parent + child) — it double-scales.- Device gate by MIN dimension, not by
TouchEnabledalone —math.min(vp.X, vp.Y)is orientation-proof: phone short side ≈ ≤430, tablet ≥ ~744. Branch desktop / phone (isMobile and min < 600) / tablet (isMobile and min >= 600) so a rotated tablet isn't treated as a phone. - Anchor stacked HUD elements to LIVE panel heights, not hardcoded pixel offsets. Classic bug: a boost/pill row hardcodes "panel is 104px" but the panel above it GROWS with content (
clamp(base + n*rowH, …)), so they overlap once the panel expands. Read the neighbor's actualAbsoluteSize.Y(or its formula) and position off that. - Leave room for Roblox's own mobile controls: the DynamicThumbstick capture area reaches up to ~0.75·
vp.Yon the bottom-left, and the Jump button sits bottom-right; lift bottom-anchored HUD off the edge enough to clear them WITH and WITHOUT the safe-area inset. Verify on a small phone (iPhone SE) and a large one (Pro Max) — the two worst cases.
ScreenGui panels
- Convention that tests well: one
ScreenGuiper screen/panel,Enabled = false, content pre-built. Toggle.Enabled = trueto open. (For MCP testing, enabling reveals CENTERED panels; SLIDE-IN panels sit off-screen in their closed position, so toggling shows nothing — animate them in instead.) - Only one panel open at a time: a
closeAll()that closes/hides every panel before opening the next. - Click-outside-to-close:
.Active = truedoes NOT reliably sink clicks that fall through a child ScrollingFrame. Bounds-check the backdrop'sInputBegan— close only when the click is OUTSIDE the panel'sAbsolutePosition/AbsoluteSize.
Progress bars / meters (the reusable recipe)
Don't resize a rounded fill. A rounded fill narrower than its own height shrinks to a dot that grows, then
stretches sideways (players saw it on every bar at low values). Instead the fill is the full size of its rounded
track (same UICorner) and a UIGradient named Mask on it cuts it at the fraction (opaque up to the cut,
transparent after, ~1 px soft edge): 2% is a sliver of the track's own round end and the edge is a straight cut.
-- The Mask's transparency along the bar: opaque on the kept side of `cut` (0..1), see-through on the other,
-- blended over `soft` (one pixel's width as a fraction of the bar) so the edge doesn't shimmer as it moves.
local function maskSequence(cut: number, soft: number, keepLow: boolean): NumberSequence
local function value(t: number): number
local v = math.clamp((t - (cut - soft / 2)) / soft, 0, 1)
return if keepLow then v else 1 - v
end
local points = { NumberSequenceKeypoint.new(0, value(0)) }
for _, t in { cut - soft / 2, cut + soft / 2 } do
if t > points[#points].Time + 1e-4 and t < 1 - 1e-4 then
table.insert(points, NumberSequenceKeypoint.new(t, value(t)))
end
end
table.insert(points, NumberSequenceKeypoint.new(1, value(1)))
return NumberSequence.new(points)
end
-- One shared helper owns every bar; nothing else writes a fill's Size or its Mask:
-- Bars.set(fill, fraction, axis?) (does nothing when unchanged: many callers set every frame),
-- Bars.tween(fill, fraction, TweenInfo), Bars.setColor(fill, colorOrSequence), Bars.get(fill)
- Shading moves to a child
ShadeImageLabel (a top-to-bottom alpha image tinted the top colour over the fill's bottom colour), because the Mask gradient owns Transparency. Shine / checker children get their own Mask cut where the fill's cut falls on them. - Grows from the right / bottom when the fill's AnchorPoint is 1. Redraw on
AbsoluteSizechanges. - A Studio command script adds Mask + Shade to every bar so new bars are made the same way; a fill without a Mask warns once and shows full.
- Typical layout: the helper in
src/shared/Bars.luau, the Studio command script intools/ui/bar_masks.luau. - Status colour still reads at a glance (
Bars.setColor(fill, good and GREEN or AMBER)); text overlay centred. ⚠️ Boolean-visibility bug (bit me): in Luau,local show = a and b and season.teamsreturns the last operand (a table), NOTtrue— soframe.Visible = showerrors orshow == trueis always false. Make the condition a real boolean: end it with a comparison (… and season.teams ~= nil), or wrap it. Reserve the bar's slot even when hidden so the layout doesn't jump.
Layout gotchas (waste hours)
- Horizontal auto-size
UIListLayoutignoresLayoutOrderby default — setSortOrder = Enum.SortOrder.LayoutOrderor chips render in reversed/creation order. UDim2.new(0,0,1,0)(single pair set) trips a selene "use fromScale" warning — useUDim2.fromScale/fromOffset.- Font glyphs: Gotham renders
✕ ↻ ✦as boxes — useX,★,◆,$. Emoji are fine (🍀 👑 ⚡ ⚔️ 🏆) EXCEPT flag emoji (show as 2 letters) — draw flags with frames.
Copy & clarity (small rules, big readability)
- Prefer ICONS/glyphs over jargon words in the HUD — e.g. show currency as its icon, not "Cash/Shards/Gems"; names belong only where the reward is described (shop). Same spirit: avoid internal-system nouns in player-facing text (say the action/outcome, not "level up the X board"; say "team", not "XI").
- Keep it plain-language — read every string as a brand-new player would.
Verifying UI (MCP)
Screenshots can lie for UI — a freecam capture can leave ScreenGuis hidden, and distant SurfaceGui close-ups render blank. Cross-check by walking PlayerGui:GetDescendants() and asserting on .Text / .Visible / ImageLabel.Image / accessory counts. The MCP tools + gotchas (clicking by AbsolutePosition, the cross-VM rule, capture artifacts) are in the roblox-studio-mcp skill; the dev/test loop is in roblox-studio-workflow.
Phone-first scaling, safe areas, ScreenInsets, touch buttons and gamepad menus are in roblox-cross-device-ui; verifying in Studio is roblox-studio-verification.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/flatbedj/roblox-skills/roblox-ui-hud">View roblox-ui-hud on skillZs</a>