What’s up, Developers!
We are excited to announce several new settings for the Roblox app - you can now enable Reduced Motion, set your preferred Background Transparency, and enable or disable the shortcut for Keyboard Navigation. These have been added to both the in-experience menu settings, as well as app settings.
Overview of Changes
There are two new properties available for developers that provide you with the user’s preference for motion and transparency, as well as events for when they change.
Note: PreferredTransparency is referred to as Background Transparency in the app, for a better user experience, but the API returns the inverse value for compatibility with how the engine represents transparency. Similarly, ReducedMotion is referred to as Reduce Motion in the app for more consistency with other settings.
How to Use
Usage of these settings should be fairly straightforward. The values are read directly from GuiService and their changed events are retrieved by using GetPropertyChangedSignal.
Reduced Motion
We followed some common patterns in handling reduced motion and would like to share them with you for inspiration!
When reduced motion is enabled…
- When a UI element is flying in from far off screen, fade it in instead, or if that is not feasible have an instant transition. CanvasGroups are very helpful for fading in complex UI elements altogether.
- When an element has a consistently looping animation that involves motion, stop the animation altogether or replace it with something in-place.
The following code snippet shows a consistently animated “rain” effect made with UI elements, pausing the animation when reduced motion is enabled.
Click here to view the code!
local GuiService = game:GetService("GuiService")
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local PlayerGui = Players.LocalPlayer:WaitForChild("PlayerGui", 3)
local screenGui = Instance.new("ScreenGui")
screenGui.IgnoreGuiInset = true
screenGui.Parent = PlayerGui
local container = Instance.new("Frame")
container.Size = UDim2.fromScale(1, 1)
container.BackgroundTransparency = 1
container.Parent = screenGui
local tweens = {}
-- Create 100 frames of random colors and random positions
local FRAME_SIZE = 20
for i = 1, 100 do
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, FRAME_SIZE, 0, FRAME_SIZE)
frame.Position = UDim2.fromScale(math.random(), math.random())
frame.BackgroundColor3 = Color3.new(math.random(), math.random(), math.random())
frame.BorderSizePixel = 0
frame.Parent = container
local tweenInfoStart = TweenInfo.new(1 - frame.Position.Y.Scale, Enum.EasingStyle.Linear)
local tweenStart = TweenService:Create(frame, tweenInfoStart, { Position = UDim2.fromScale(frame.Position.X.Scale, 1) })
local tweenInfoEnd = TweenInfo.new(frame.Position.Y.Scale, Enum.EasingStyle.Linear)
local tweenEnd = TweenService:Create(frame, tweenInfoEnd, { Position = frame.Position })
tweens[tweenStart] = true
local startTweenLocation = UDim2.fromScale(frame.Position.X.Scale, 0)
tweenStart.Completed:Connect(function ()
tweens[tweenStart] = true
frame.Position = startTweenLocation
tweenEnd:Play()
tweens[tweenEnd] = true
end)
tweens[tweenEnd] = false
tweenEnd.Completed:Connect(function ()
tweens[tweenEnd] = false
tweenStart:Play()
tweens[tweenStart] = true
end)
end
function onReducedMotionUpdate()
for tween, isActive in pairs(tweens) do
if isActive then
if GuiService.ReducedMotionEnabled then
tween:Pause()
else
tween:Play()
end
end
end
end
local reducedMotionSignal = GuiService:GetPropertyChangedSignal("ReducedMotionEnabled")
reducedMotionSignal:Connect(onReducedMotionUpdate)
onReducedMotionUpdate()
Here is an example of a menu option fading in screen instead of flying in from the top.
Click here to view the code!
local GuiService = game:GetService("GuiService")
local TweenService = game:GetService("TweenService")
local screenGui = script:FindFirstAncestorOfClass("ScreenGui")
-- CanvasGroup only supports Sibling ZIndexBehavior
screenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
-- This script expects to be parented to a button that controls the menu,
-- a sibling of the button.
local button = script.Parent
local canvasGroup = button.Parent.Menu
local canvasGroupOpenPosition = canvasGroup.Position
local canvasGroupClosedPosition = canvasGroupOpenPosition - UDim2.fromScale(0, 2)
local menuIsOpen = false
canvasGroup.Position = canvasGroupClosedPosition
-- Tweens to slide the canvasGroup into and out of the screen
local slideInTween = TweenService:Create(canvasGroup, TweenInfo.new(0.35), { Position = canvasGroupOpenPosition })
local slideOutTween = TweenService:Create(canvasGroup, TweenInfo.new(0.35), { Position = canvasGroupClosedPosition })
-- Tweens to fade the canvasGroup in and out
local fadeInTween = TweenService:Create(canvasGroup, TweenInfo.new(0.2), { GroupTransparency = 0 })
local fadeOutTween = TweenService:Create(canvasGroup, TweenInfo.new(0.2), { GroupTransparency = 1 })
fadeOutTween.Completed:Connect(function()
canvasGroup.Position = canvasGroupClosedPosition
canvasGroup.GroupTransparency = 0
end)
button.Activated:Connect(function()
local reducedMotionEnabled = GuiService.ReducedMotionEnabled
if menuIsOpen then
if reducedMotionEnabled then
fadeOutTween:Play()
else
slideOutTween:Play()
end
else
if reducedMotionEnabled then
canvasGroup.GroupTransparency = 1
canvasGroup.Position = canvasGroupOpenPosition
fadeInTween:Play()
else
slideInTween:Play()
end
end
menuIsOpen = not menuIsOpen
end)
Preferred Transparency
We chose to provide the users transparency preference in a way that could be used very easily by developers. If you have a UI element with a semi- or fully-transparent background that you think should respect the value of a user’s transparency preference, simply multiply your default background transparency by the user’s preference. As the value approaches 0 it will become more opaque, becoming fully opaque at 0. Make sure to hook this up to the signal so it updates in real time!
Click here to view the code!
-- Parent this to any TextLabel
local GuiService = game:GetService("GuiService")
local TextLabel = script.Parent
-- Set the TextLabel's background transparency by combining the default background transparency and the player's preferred transparency
local DEFAULT_TRANSPARENCY = TextLabel.BackgroundTransparency
local function setBackgroundTransparency()
print(GuiService.PreferredTransparency)
TextLabel.BackgroundTransparency = GuiService.PreferredTransparency * DEFAULT_TRANSPARENCY
end
GuiService:GetPropertyChangedSignal("PreferredTransparency"):Connect(setBackgroundTransparency)
setBackgroundTransparency()
Next Steps
This feature is enabled for all users and is ready to use in development now Please leave any requests and feedback in the comments below. If you end up implementing support for these features, consider sharing with us! What other accessibility settings would you like to see in the future?
With that, and a big thank you to @coolpenguino1 for his hard work on this project this summer, we are looking forward to being more accessible with you all