Animations and effects

Hover and click effects, the dissolve, and the four tween curves the library uses.

Hover and Click#

Button takes a Hover and a Click table, and so does each entry in an icon strip. IconButton reads its own pair only when you omit Buttons; with Buttons set, every effect comes from the entries. The keys:

OptionTypeDefaultWhat it does
RotatenumberDegrees to turn the icon, added to its resting angle. On Hover it holds while the pointer is inside. On Click it turns and comes back.
ScalenumberMultiplier on the icon's size, clamped to 0-10. Hover only.
IconstringSwap to a different icon for the duration, then swap back.
TextstringSwap the button's caption for the duration.
Holdnumber0.9Seconds a Click keeps its Icon or Text swap. Click only.

A hover effect ends when the pointer leaves. A click has no natural end, so an Icon or Text swap on Click reverts after Hold seconds.

A Click rotation runs on a 0.45s Back Out tween. An exact multiple of 360 snaps back to the resting angle when it lands; any other angle tweens back.

The recipes#

Each of these is a complete control you can paste.

Refresh: one full spin on click#

Luau
main:Button({
    Text = "Reload data",
    ButtonText = "Refresh",
    ButtonIcon = "refresh-cw",
    Click = { Rotate = 360 },
    Callback = reload,
})

Confirm: swap to a tick, then back#

Luau
main:Button({
    Text = "Save configuration",
    ButtonText = "Save",
    ButtonIcon = "save",
    Click = { Icon = "check", Text = "Saved", Hold = 1.4 },
    Callback = save,
})

Destructive: warn on hover#

Luau
main:Button({
    Text = "Delete configuration",
    ButtonText = "Delete",
    ButtonIcon = "trash-2",
    Danger = true,
    Hover = { Icon = "triangle-alert", Scale = 1.1 },
    Click = { Text = "Deleted", Hold = 1.6 },
    Callback = deleteConfig,
})

Lift: grow a little on hover#

Luau
main:Button({
    Text = "Open settings",
    ButtonText = "Open",
    ButtonIcon = "settings",
    Hover = { Scale = 1.12 },
    Callback = openSettings,
})

Nudge: a quarter turn, for something that expands#

Luau
main:Button({
    Text = "Show advanced",
    ButtonText = "Expand",
    ButtonIcon = "chevron-right",
    Hover = { Rotate = 90 },
    Callback = expand,
})

Overdrive: two full turns#

Luau
main:Button({
    Text = "Rebuild everything",
    ButtonText = "Rebuild",
    ButtonIcon = "rotate-cw",
    Click = { Rotate = 720, Text = "Working…", Hold = 1.2 },
    Callback = rebuild,
})

Per-entry, on an icon strip#

Each button in a strip carries its own:

Luau
main:IconButton({
    Text = "Playback",
    Buttons = {
        { Icon = "play",       Callback = play,    Hover = { Scale = 1.15 } },
        { Icon = "pause",      Callback = pause,   Hover = { Scale = 1.15 } },
        { Icon = "refresh-cw", Callback = restart, Click = { Rotate = 360 } },
        { Icon = "trash-2",    Callback = clear,   Danger = true,
          Hover = { Icon = "triangle-alert" } },
    },
})

Spamming is safe#

Every effect is generation-guarded. Clicking a spinning button again restarts the spin rather than letting the first click's completion snap the icon back mid-turn. You do not need a debounce.

Renaming a button with SetText while no effect is running makes the new label the one a swap reverts to.

Hover colours#

Separate from the icon effects, Button, Dropdown, Keybind and each icon-strip entry take:

OptionTypeDefaultWhat it does
HoverColorColor3 | stringThe colour the border and glyph light up with. A theme role name works too. Defaults to the accent role, or the danger role on a Danger button.
HoverFillColor3 | stringThe colour the surface fills with on hover, or a theme role name. Defaults to the hover role.
Luau
main:Button({
    Text = "Custom hover",
    ButtonText = "Run",
    HoverColor = Color3.fromRGB(120, 180, 255),
    HoverFill = "panel3",              -- a theme role, or a Color3
    Callback = run,
})

On a Button these two are read by the outline style only. filled, ghost and soft each have their own hover built in.

The dissolve#

The window breaks into tiles when hidden and reassembles when shown. It is on by default and needs no setup.

The tiles are coloured by sampling what the window is actually showing, so the mosaic looks like the window rather than a grid of flat squares. The burn edge is ember orange whatever the theme is. Change it on Ember.Fx:

Luau
Ember.Fx.ember    = Color3.fromRGB(90, 170, 255)   -- the burn front
Ember.Fx.emberHot = Color3.fromRGB(235, 245, 255)  -- its hot core

Effects.Quality scales the budget: "Low" takes 0.6 of the tiles and particles, "High" takes 1.35, and "Balanced" leaves them alone. The tile count is clamped to 32-460 after scaling either way.

Lower MaxTiles and Particles on weak machines, or set Effects.Enabled to false for no animation at all. The keys and their defaults are in the configuration reference.

With Effects.RespectReducedMotion = true, animation is also off for any player whose Roblox client reports ReducedMotionEnabled. It is false by default.

The motion curves#

These are the curves the library animates with. Match them in your own tweens:

NameTweenInfoUsed for
fast0.14s Quad OutColour changes
base0.22s Quint OutHover, most state changes
open0.34s Quint OutMenus and groups expanding
shut0.24s Quart InThe same, closing
Luau
local TweenService = game:GetService("TweenService")
local BASE = TweenInfo.new(0.22, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)
 
TweenService:Create(myFrame, BASE, { BackgroundTransparency = 0 }):Play()