Scale vs. Offset: The Fundamental Problem
Every UDim2 value in Roblox has two components: Scale (a percentage of the parent container) and Offset (fixed pixels). When you drag elements in Studio's UI editor, it often generates Offset values. A button at Position UDim2.new(0, 500, 0, 300) is 500 pixels from the left and 300 from the top, which is fine on a 1080p screen but completely wrong on a 720x1280 phone screen where 500 pixels might be off the right edge. The fix is to use Scale for positioning and sizing in almost every case. UDim2.fromScale(0.5, 0.5) places an element at 50% of its parent's width and height regardless of screen resolution. Convert all Offset-based positions and sizes to Scale. For the rare cases where you need a fixed pixel value (like a 1-pixel border), use Offset only for that specific dimension while keeping everything else Scale-based.
UIAspectRatioConstraint: Preventing Stretching
Using Scale for both width and height causes elements to stretch on screens with different aspect ratios. A square button sized at UDim2.fromScale(0.1, 0.1) will be a rectangle on any non-square screen because 10% of the width is a different pixel count than 10% of the height. Add a UIAspectRatioConstraint to any element that must maintain its proportions. Set the AspectRatio to the desired width-to-height ratio (1 for square, 1.5 for 3:2, etc.) and DominantAxis to Width. The constraint will automatically adjust the element's height to maintain the ratio based on its width. For entire layouts, add the constraint to the parent Frame and let all children inherit the proportioned space. This single component fixes the majority of mobile scaling distortion.
Safe Zones and Screen Insets
Modern phones have notches, rounded corners, and system UI that eat into the usable screen area. Roblox provides GuiService:GetGuiInset() which returns the top inset (where the Roblox top bar lives). On mobile, this inset is larger. Never place interactive elements within 48 pixels of any screen edge. For the bottom of the screen, the Roblox jump button and mobile controls occupy space. Use ScreenGui.ScreenInsets set to CoreUISafeInsets to automatically account for these. If you need manual control, query GuiService:GetGuiInset() and offset your UI container accordingly. Test with different devices in the Studio device emulator (Test tab, select device type) to verify nothing is clipped by notches or overlaps with system controls.
- ScreenGui.ScreenInsets = CoreUISafeInsets to respect device-specific safe areas
- Minimum 48px margin from all screen edges for interactive elements
- Test with iPhone and Android profiles in Studio's device emulator
- Account for the Roblox top bar, mobile jump button, and joystick areas
Touch Targets and Mobile Usability
Fingers are larger and less precise than mouse cursors. The minimum touch target size should be 44x44 points, which translates to roughly UDim2.fromScale(0.12, 0.07) on most phone screens. Buttons that are comfortable on desktop (small X to close, tiny inventory slots) become impossible to tap on mobile. Increase the clickable area of small buttons by adding an invisible TextButton as a parent with a larger hit area, while keeping the visible button at its original size inside it. For inventory grids, increase cell spacing on mobile. Detect the player's platform using UserInputService.TouchEnabled and adjust UI sizes accordingly. A common pattern is defining a scale multiplier (1.0 for desktop, 1.3-1.5 for mobile) and applying it to all interactive element sizes on load.
AutomaticSize and Modern Layout Tools
Roblox's AutomaticSize property lets elements resize based on their content, which is invaluable for text-heavy UI. Set AutomaticSize to Y on a Frame containing a TextLabel and the Frame will grow vertically to fit the text regardless of screen size. Combine this with UIListLayout or UIGridLayout for responsive containers that reflow content automatically. TextLabel.TextScaled is tempting but imprecise: it shrinks text to fit the container, which often makes text unreadably small on mobile. Instead, use a fixed TextSize of 14-18 for body text and rely on AutomaticSize containers to accommodate the text. For scroll-based content like inventories or leaderboards, ScrollingFrame with AutomaticCanvasSize set to Y grows the scrollable area to fit all children. This eliminates the need to manually calculate canvas sizes across different screen dimensions.
