Claude
Skill
roblox-gui
Use when building Roblox menus, HUDs, shops, notifications, dialogs, or responsive cross-platform UI.
Virus-scanned
Reviewed automatically before listing.
Download
tabooharmony-roblox-brain-skills_gameplay_roblox-gui-6051c35.zip · 9 KB
Install
skills CLI
npx skills add https://github.com/TabooHarmony/roblox-brain/tree/main/skills/gameplay/roblox-gui
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install tabooharmony-roblox-brain@llmmart
Git
git clone https://github.com/TabooHarmony/roblox-brain.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole tabooharmony/roblox-brain collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
roblox gui
When to Load
Load when building a HUD, menu, shop, dialog, notification, or UI attached to a 3D object.
Quick Reference
ScreenGuioverlays;SurfaceGuion surfaces;BillboardGuifor world labels.- Let
UIListLayout,UIGridLayout, and constraints own repeated layout. Avoid per-frame pixel positioning. - Use
Scalefor responsive structure andOffsetfor deliberate padding or fixed-size details. - Design for touch and gamepad as well as mouse and keyboard. Bind gameplay actions with
ContextActionServicewhere it fits. - For gamepad UI, set a selected entry point and test directional focus.
GuiService.SelectedObjectis the native baseline. - Reuse the project's visual language; let the owner choose art direction. Verify the UI over the game world.
- Keep UI state separate from the server state that it displays. A button is not an authority boundary.
- Server Authority UI may show corrected predictions. Show confirmed inventory and currency; route gameplay input through the Input Action System.
- Make scrolling, text growth, clipping, and safe-area behavior explicit before adding polish.
- Build custom loading UI in
ReplicatedFirst; never use a client-side character teleport as the readiness gate.
Need the details? Load references/full.md for layout recipes and UI lifecycle patterns.
Files (roblox-brain)
-
references
-
full.md 21.2 KB
# roblox gui: full reference > Code examples are illustrative. Adapt them to your project and verify in Studio before production use. The most reliable Roblox UI is layout-driven, state-aware, and tested at more than one aspect ratio. The examples below use native UI objects and do not require a UI framework. ## 1. Choose the container - `ScreenGui`: a 2D overlay attached to a player's screen. - `SurfaceGui`: controls rendered on a part or attachment. - `BillboardGui`: a camera-facing label or panel in the 3D world. - `ViewportFrame`: a UI region that renders a 3D model preview. Set `DisplayOrder` deliberately when multiple `ScreenGui` instances overlap. Use `ResetOnSpawn` only when the UI should survive character respawn. Do not set `IgnoreGuiInset` globally without checking how the layout interacts with Roblox's top-bar and safe-area behavior. ## 2. Build from containers Give each visual region a container with one responsibility: header, content, footer, or modal. Put a layout object inside the container that owns repeated children. ```luau local panel = Instance.new("Frame") panel.Name = "InventoryPanel" panel.AnchorPoint = Vector2.new(0.5, 0.5) panel.Position = UDim2.fromScale(0.5, 0.5) panel.Size = UDim2.fromScale(0.8, 0.75) panel.BackgroundColor3 = Color3.fromRGB(25, 28, 36) panel.Parent = screenGui local padding = Instance.new("UIPadding") padding.PaddingTop = UDim.new(0, 16) padding.PaddingBottom = UDim.new(0, 16) padding.PaddingLeft = UDim.new(0, 16) padding.PaddingRight = UDim.new(0, 16) padding.Parent = panel local list = Instance.new("UIListLayout") list.Padding = UDim.new(0, 8) list.SortOrder = Enum.SortOrder.LayoutOrder list.Parent = panel ``` Use `LayoutOrder` as data, not as a visual afterthought. If children are created dynamically, assign their order in the view model or row constructor. ## 3. Scale and offset `UDim2` combines proportional `Scale` with fixed `Offset`. A common pattern is scale for the outer region and offset for internal padding: ```luau local card = Instance.new("Frame") card.AnchorPoint = Vector2.new(0.5, 1) card.Position = UDim2.fromScale(0.5, 0.94) card.Size = UDim2.new(0.86, 0, 0, 120) local sizeLimit = Instance.new("UISizeConstraint") sizeLimit.MinSize = Vector2.new(260, 96) sizeLimit.MaxSize = Vector2.new(720, 220) sizeLimit.Parent = card ``` Use `UIAspectRatioConstraint` for shapes that must remain square or maintain a fixed ratio. Keep text containers bounded so a translated or long string cannot expand the entire screen unexpectedly. ## 4. Text and images Choose one sizing policy for each text region: - fixed `TextSize` plus a layout constraint when the content is controlled; - `TextScaled` only when the allowed range is bounded by `UITextSizeConstraint`; - `AutomaticSize` when the parent has room to grow and the layout can respond. Test long strings, empty strings, and missing images. A missing asset should produce a useful placeholder, not a layout collapse. Avoid putting essential game state only in color or iconography. ## 5. Scrolling content A `ScrollingFrame` needs a clear canvas policy. For a vertical list, put a `UIListLayout` inside it and use `AutomaticCanvasSize = Enum.AutomaticSize.Y` when the project does not need to calculate canvas size manually. ```luau local scroll = Instance.new("ScrollingFrame") scroll.Size = UDim2.fromScale(1, 1) scroll.AutomaticCanvasSize = Enum.AutomaticSize.Y scroll.CanvasSize = UDim2.fromScale(0, 0) scroll.ScrollBarThickness = 6 scroll.Parent = content local list = Instance.new("UIListLayout") list.Padding = UDim.new(0, 6) list.SortOrder = Enum.SortOrder.LayoutOrder list.Parent = scroll ``` Do not nest an automatically growing scroll region inside another automatic scroll region unless the interaction is intentional and tested. ## 6. UI state and server state The UI should render a snapshot or view model. It may request an action, but the server response determines the final display. ```luau local HttpService = game:GetService("HttpService") local state = { requestId = nil, -- non-nil while a purchase is pending or its outcome is unknown selectedId = nil, -- True once the timeout window closed without a correlated answer. The -- request stays recorded (requestId holds) so the UI blocks a FRESH -- same-intent purchase that could double-spend a purchase that may -- still have committed. Reconciliation can still settle it. unresolved = false, } local function render() local busy = state.requestId ~= nil -- While a request is recorded (pending OR unresolved), a fresh -- same-intent purchase must stay blocked. Only a correlated result or -- snapshot may release it. buyButton.Active = not busy and not state.unresolved and state.selectedId ~= nil spinner.Visible = busy and not state.unresolved end local REQUEST_TIMEOUT = 8 -- seconds; bounds both the request and the refresh buyButton.Activated:Connect(function() if state.requestId or not state.selectedId then return end state.requestId = HttpService:GenerateGUID(false) local myId = state.requestId -- token: only THIS purchase's timers act render() BuyItem:FireServer(myId, state.selectedId) -- Bound the pending state: silence can mean a lost request OR a -- committed purchase whose response never arrived. task.delay(REQUEST_TIMEOUT, function() -- Token check: if this purchase already resolved, or a NEWER -- purchase is now pending, this old timer must do nothing. if state.requestId ~= myId then return end statusLabel.Text = "Purchase outcome unknown. Refreshing..." RefreshPurchases:FireServer(myId) -- reconcile; never blind-retry -- Bound the refresh too: if no correlated answer arrives, stop the -- spinner but DO NOT discard the operation. The request id stays -- recorded and unresolved=true keeps Buy blocked, because the -- original purchase may still have committed, and a fresh same-intent -- purchase could double-spend. Reconciliation (a correlated -- PurchaseResult or echoed InventorySync) can still settle it. task.delay(REQUEST_TIMEOUT, function() if state.requestId == myId then state.unresolved = true statusLabel.Text = "Purchase outcome unknown. Check your inventory later." render() end end) end) end) PurchaseResult.OnClientEvent:Connect(function(id, ok, message) if id ~= state.requestId then return -- stale, duplicate, or already-reconciled result end state.requestId = nil state.unresolved = false statusLabel.Text = message render() end) -- Authoritative state wins, but only when it is actually NEWER than the -- pending request: an unrelated background snapshot (fired before the -- purchase committed) must not clear the pending state. The server echoes -- the reconciled request id in the snapshot it sends in response to -- RefreshPurchases; only that correlation settles a pending request, -- including one already marked unresolved. InventorySync.OnClientEvent:Connect(function(snapshot) if state.requestId then if snapshot.reconciledRequestId ~= state.requestId then return -- older or unrelated snapshot: leave the pending state alone end state.requestId = nil -- correlated reconciliation settles the request state.unresolved = false end statusLabel.Text = snapshot.ownsItem and "Purchased" or "Not purchased" render() end) ``` Do not grant currency, inventory, or ownership because a local button handler ran. The UI is an input surface, not a trust boundary. Correlate each request with an id, treat "no response yet" as a bounded unknown rather than a failure to retry, and reconcile against authoritative server state: a timed-out purchase may still have committed, so a blind retry can double-spend. In Server Authority projects, the client may briefly render predicted state that is later corrected by rollback. Keep durable displays tied to confirmed server or synchronized state, and mark optimistic feedback as pending when the distinction matters. ## 7. Input and interaction Use `Activated` for buttons when possible. Use `ContextActionService` for UI-only actions that should map across keyboard, gamepad, and touch. For gameplay-affecting input in a Server Authority project, use `InputAction`/`InputContext` and the synchronized simulation path. Use `UserInputService` when you need raw device details or gesture tracking. Every interactive control needs: - a visible state for hover, press, disabled, and focus where the platform supports it; - a clear label or tooltip; - a route that works without precise mouse aiming; - a debounced action that cannot issue duplicate requests while busy. ### Reliable hover (MouseEnter/MouseLeave pitfalls) Native `GuiObject.MouseEnter`/`MouseLeave` only re-check hover when the mouse moves, so they can miss when content scrolls under a stationary cursor (e.g. inside a `ScrollingFrame`) or occasionally fail to fire `MouseLeave`. For reliable hover, poll the cursor each frame and fire your own enter/leave on state transitions. This is a practitioner pattern (DevForum lead: "REAL MouseEnter/MouseLeave for GuiObjects", 7eoeb, https://devforum.roblox.com/t/real-mouseentermouseleave-for-guiobjects-they-actually-fire/3980310). Prefer `PlayerGui:GetGuiObjectsAtPosition()` over hardcoded top-bar offsets, and clean up the signals with the owning UI's lifetime. ```luau local Players = game:GetService("Players") local RS = game:GetService("RunService") local UIS = game:GetService("UserInputService") local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui") local hovered = false RS.RenderStepped:Connect(function() local pos = UIS:GetMouseLocation() local isOver = false for _, obj in playerGui:GetGuiObjectsAtPosition(pos.X, pos.Y) do if obj == frame then isOver = true break end end if isOver and not hovered then hovered = true onEnter() elseif not isOver and hovered then hovered = false onLeave() end end) ``` ## 8. Gamepad focus and selection Gamepad support is not complete when a button merely reacts to `Activated`. The player also needs a predictable focus path: ```luau local GuiService = game:GetService("GuiService") playButton.Selectable = true settingsButton.Selectable = true -- Set this when opening the menu, not every frame. GuiService.SelectedObject = playButton ``` For larger menus, group related controls and define explicit directional behavior where automatic selection is ambiguous. Test selection entering and leaving nested containers, covered elements, modal dialogs, and a changing list. Roblox's current directional-selection behavior includes ancestor grouping, covered-element handling, and improved analog-stick navigation. The official `focus-navigation` repository is a read-only mirror and is best treated as an optional reference rather than a mandatory dependency. React Luau is another optional reference for state-driven, declarative UI. Do not introduce it into a small native UI merely to avoid writing a few render functions. ## 9. Animation and cleanup Tween a small set of properties and cancel or replace the previous tween when state changes. Keep connections and tweens owned by the screen or row they affect. Destroy temporary UI when the owning feature closes. ```luau local TweenService = game:GetService("TweenService") local modal = screenGui.Modal -- CanvasGroup local openTween = TweenService:Create( modal, TweenInfo.new(0.18, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), { GroupTransparency = 0 } ) openTween:Play() ``` If the object can be destroyed before the tween completes, connect cleanup to the owner's lifetime rather than assuming `Completed` will always run. ## 10. Shops, dialogs, and notifications A practical UI flow is: 1. render a neutral/loading state; 2. load or receive the data model; 3. allow one action at a time per row or transaction; 4. show a success, failure, or retry state; 5. refresh from authoritative state after the action. Do not close a purchase dialog merely because a prompt was opened. Wait for the purchase or server result, and handle cancellation. ## 11. Safe areas and cross-device review Do not choose `ScreenGui.ScreenInsets`, `IgnoreGuiInset`, or `ClipToDeviceSafeArea` from a single Studio viewport. `Enum.ScreenInsets.CoreUISafeInsets` is one option, not a universal default. Test the actual target combinations and record which elements intentionally sit under or outside Roblox core UI. Test at minimum: - narrow phone portrait; - wide phone or tablet landscape; - desktop mouse/keyboard; - gamepad navigation; - console safe-area and top-bar behavior; - mobile cutout/notch and on-screen keyboard behavior; - a long localized string; - a missing or delayed asset; - respawn and UI reopening; - low frame rate while a list is updating. A layout that looks correct at one Studio viewport size is not finished. Capture the intended states and compare them after changing the viewport and preferred input. ## Custom loading flow 1. Create the replacement UI from a `LocalScript` in `ReplicatedFirst` before calling `RemoveDefaultLoadingScreen()`. 2. Treat `game:IsLoaded()` and `game.Loaded` as DataModel readiness, not proof that every asset is ready. 3. Use `ContentProvider:PreloadAsync()` only for a bounded critical set, with timeout and fallback behavior. 4. Gate controls and the first playable state explicitly. Do not move the character from the client as a loading lock. 5. Restore any changed controls, CoreGui state, camera state, and temporary connections on success, timeout, cancellation, and respawn. ## UI checklist - [ ] Containers use layout objects and constraints instead of scattered pixel positions. - [ ] Text growth, clipping, and scrolling have an explicit policy. - [ ] Buttons expose disabled and busy states. - [ ] UI actions are safe to repeat or are debounced. - [ ] Server responses, not local optimism, determine durable state. - [ ] The interface works with touch, mouse, keyboard, and gamepad where relevant. - [ ] All temporary connections, tweens, and rows have an owner lifetime. ## Existing style and visual verification Follow the project's existing components, typography roles, colors, spacing, and interaction states. Do not impose a genre palette or invent a new design system for one screen. If there is no art direction, make the smallest readable provisional choice and show it to the owner; a working instance tree is not proof that the UI looks right. Test the actual screen over bright and dark game scenes, not only a blank Studio canvas. Check clipping, contrast, gameplay visibility, and the rendered states that exist (such as active, selected, disabled, or purchase pending). Hover is not available on every input device; focus and touch must still work. Check `GuiService.ReducedMotionEnabled` before adding motion-heavy feedback. Layout and accessibility are engineering checks, not a prescribed visual style. ## Shared styling and flex, when needed For an existing stylesheet-based project, extend its tokens and rules rather than setting conflicting per-instance properties. Roblox `StyleSheet` rules are attached to a UI tree with `StyleLink`; `StyleQuery` can adapt to size, preferred input, text size, or reduced motion. For a one-off UI, direct properties may be simpler. See the [official styling guide](https://create.roblox.com/docs/ui/styling) before adding a new sheet. Flex is built into `UIListLayout` (`HorizontalFlex` or `VerticalFlex`); `UIFlexItem` customizes a child. There is no `UIFlexLayout` instance. Use it only when a row or column needs flexible space; otherwise ordinary list/grid layout is simpler. If items spread apart unexpectedly, inspect the layout's flex alignment before adding offsets. Keep `SortOrder = LayoutOrder` and set each child's `LayoutOrder` when order matters. See the [official list/flex guide](https://create.roblox.com/docs/ui/list-flex-layouts). ## Community ecosystem (leads, not sources) Top-sorted DevForum canon for UI libraries. Verify status in-thread before recommending. - [TopbarPlus v3](https://devforum.roblox.com/t/topbarplus-v340-construct-topbar-icons-with-ease-customise-them-with-themes-dropdowns-captions-labels-and-more/1017485): the topbar icon standard (4.4k likes). - [Iris](https://devforum.roblox.com/t/iris-immediate-mode-ui-library-based-on-dear-imgui/2302802): Dear ImGui-style immediate mode, good for debug tools/dev UIs, not player-facing polish. - [Screen3D](https://devforum.roblox.com/t/screen3d-a-3d-ui-framework-that-just-works/3273671) (2024); [Text+](https://devforum.roblox.com/t/text-custom-fonts-advanced-control/3521684) (2025) custom fonts. - [Satchel](https://devforum.roblox.com/t/satchel-open-source-modern-backpack-system/2451549): open-source inventory/backpack, study-grade. - [Vanilla 3](https://devforum.roblox.com/t/vanilla-3-the-pragmatic-icon-set-for-roblox-studio/935745): the pragmatic icon set. - Chat: BetterChat V3 discontinued; [NovaChat](https://devforum.roblox.com/t/novachat-v107-chat-update-part-2-a-modern-feature-rich-chat-replacement-update/4513813) (2026) is the active replacement line; [ViewportFrame masking](https://devforum.roblox.com/t/viewportframe-masking/2964839) (2024) heavily cited for UI VFX. - Design theory: [UI Design Starter Guide](https://devforum.roblox.com/t/ui-design-starter-guide/53461) (1.1k likes). - [Mobile button placement tutorial](https://devforum.roblox.com/t/the-correct-way-to-design-mobile-buttons/2494558) illustrates collisions with the default thumbstick/jump controls and touchscreen-PC detection pitfalls. Treat its coordinates and per-frame script as dated examples, not a portable recipe; test on the game's target devices. ## Pagination (UIPageLayout) A `UIPageLayout` parented to a `GuiObject` (usually a `Frame`) stacks its children as full-size pages; only the current page is visible. Members: `JumpTo(page)`, `JumpToIndex(index)`, `Next()`, `Previous()`, plus the `CurrentPage` property. Set `Circular = true` for wraparound looping and tune motion with `TweenTime`, `EasingStyle`, `EasingDirection` (`Animated = false` for instant snaps). The layout provides no buttons, so wire input yourself: ```luau local layout = Instance.new("UIPageLayout") layout.TweenTime = 0.25 layout.EasingStyle = Enum.EasingStyle.Quad layout.Parent = pageContainer nextButton.Activated:Connect(function() layout:Next() end) prevButton.Activated:Connect(function() layout:Previous() end) layout.PageEnter:Connect(function(page) updateDots(page) -- one dot per child; highlight CurrentPage end) ``` `PageEnter`/`PageLeave` fire on page transitions and `Stopped` when a tween settles; that is the hook for a page-indicator/paginator pattern. `ScrollWheelInputEnabled`, `TouchInputEnabled`, and `GamepadInputEnabled` control the built-in pan input. ## Selection outlines (SelectionBox) `SelectionBox` renders a 3D box outline around its `Adornee` (inherited from `InstanceAdornment`); it is purely visual and captures no input. Style with `LineThickness` (studs), `Color3` (outline color, from `GuiBase3d`), and `SurfaceColor3`/`SurfaceTransparency` (faces; surface transparency defaults to 1). Prefer the `Highlight` class when you need fill/outline effects over non-primitive geometry such as `MeshPart`. ```luau local box = Instance.new("SelectionBox") box.LineThickness = 0.05 box.Color3 = Color3.new(1, 1, 0) box.SurfaceTransparency = 1 box.Parent = workspace local mouse = Players.LocalPlayer:GetMouse() mouse.Move:Connect(function() box.Adornee = mouse.Target -- nil when pointing at nothing end) ``` ## Decals (Decal, Texture) `Decal` draws a single image on one face of a part (`Face = Enum.NormalId.Front`, etc.). The separate `Texture` class shares face placement and repeats/tiles the image via `StudsPerTileU/V` and `OffsetStudsU/V`; use `Texture` for tiled surfaces and `Decal` for posters and signs. The image property `Texture` (ContentId) is deprecated in favor of `ColorMap`/`ColorMapContent` but still functional. `Transparency`, `Color3`, and `Face` are runtime-writable: tween decals, or swap images in response to gameplay. User-uploaded image assets go through moderation; a failed review renders nothing, so ship a placeholder and handle it. ```luau local decal = Instance.new("Decal") decal.Face = Enum.NormalId.Front decal.Texture = "rbxassetid://699259085" decal.Parent = part ``` ## Video (VideoFrame, VideoPlayer) `VideoFrame` is the simple path: parent it to a `SurfaceGui` and set `Video` (ContentId) to a video-type asset; image IDs will not play. Control with `Play()`, `Pause()`, `Looped`, and `Volume`; wait for `IsLoaded` (or the `Loaded` event) before playing. `Ended`/`DidLoop` report playback progress. `VideoPlayer` is the newer wire-based source: set `VideoContent` (Content), then connect `Wire` instances to a `VideoDisplay` inside a `SurfaceGui` for visuals and an `AudioEmitter` for sound. It adds `PlaybackSpeed`, `TimePosition`, `LoadAsync()`, and a `PlayFailed` event for fetch failures. Prefer `VideoFrame` unless you need the video/audio wire split. ```luau local gui = Instance.new("SurfaceGui") gui.Parent = screenPart local video = Instance.new("VideoFrame") video.Size = UDim2.fromScale(1, 1) video.Looped = true video.Video = "rbxassetid://5608359401" video.Parent = gui if not video.IsLoaded then video.Loaded:Wait() end video:Play() ```
-
-
SKILL.md 2.9 KB
--- name: roblox-gui description: "Use when building Roblox menus, HUDs, shops, notifications, dialogs, or responsive cross-platform UI." last_reviewed: 2026-08-31 sources: - https://create.roblox.com/docs/ui - https://create.roblox.com/docs/ui/position-and-size - https://create.roblox.com/docs/ui/styling - https://create.roblox.com/docs/ui/list-flex-layouts - https://create.roblox.com/docs/input - https://create.roblox.com/docs/reference/engine/classes/GuiService - https://create.roblox.com/docs/reference/engine/classes/GuiObject - https://create.roblox.com/docs/reference/engine/classes/ScreenGui - https://create.roblox.com/docs/projects/server-authority - https://create.roblox.com/docs/input/input-action-system - https://create.roblox.com/docs/reference/engine/classes/ReplicatedFirst - https://create.roblox.com/docs/reference/engine/classes/ContentProvider - https://create.roblox.com/docs/reference/engine/classes/UIPageLayout - https://create.roblox.com/docs/reference/engine/classes/SelectionBox - https://create.roblox.com/docs/reference/engine/classes/Decal - https://create.roblox.com/docs/reference/engine/classes/VideoPlayer - https://raw.githubusercontent.com/Roblox/focus-navigation/main/README.md - https://devforum.roblox.com/t/introducing-improvements-to-directional-ui-selection-on-gamepad/3864317 - https://devforum.roblox.com/t/what-are-the-best-ui-screeninset-settings-for-buttons/3519333 - https://devforum.roblox.com/t/screenguiscreeninsets-topbarinsets-regression/4047230 - https://raw.githubusercontent.com/Roblox/react-luau/main/README.md - original --- # roblox gui ## When to Load Load when building a HUD, menu, shop, dialog, notification, or UI attached to a 3D object. ## Quick Reference - `ScreenGui` overlays; `SurfaceGui` on surfaces; `BillboardGui` for world labels. - Let `UIListLayout`, `UIGridLayout`, and constraints own repeated layout. Avoid per-frame pixel positioning. - Use `Scale` for responsive structure and `Offset` for deliberate padding or fixed-size details. - Design for touch and gamepad as well as mouse and keyboard. Bind gameplay actions with `ContextActionService` where it fits. - For gamepad UI, set a selected entry point and test directional focus. `GuiService.SelectedObject` is the native baseline. - Reuse the project's visual language; let the owner choose art direction. Verify the UI over the game world. - Keep UI state separate from the server state that it displays. A button is not an authority boundary. - Server Authority UI may show corrected predictions. Show confirmed inventory and currency; route gameplay input through the Input Action System. - Make scrolling, text growth, clipping, and safe-area behavior explicit before adding polish. - Build custom loading UI in `ReplicatedFirst`; never use a client-side character teleport as the readiness gate. **Need the details?** Load `references/full.md` for layout recipes and UI lifecycle patterns.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.