smithery/neversight

ui-ux

Implements UI/UX systems including responsive design, animations, input handling, HUD elements, and menu systems. Use when building game interfaces, menus, HUDs, or any user-facing UI.

Installation

$ npx skills add smithery/neversight --skill ui-ux

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/neversight · top by installs.

npx skills add smithery/neversight

Browse all from smithery/neversight

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Allowed toolsRead, Write, Edit, Glob, Grep

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 15,976 B
  • docs SUMMARY.md 197 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Roblox UI/UX Systems

When implementing UI systems, follow these patterns for responsive and polished interfaces.

Responsive Design

UIAspectRatioConstraint

-- Maintain aspect ratio regardless of screen size
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0.5, 0, 0.5, 0)  -- Will be adjusted

local aspectRatio = Instance.new("UIAspectRatioConstraint")
aspectRatio.AspectRatio = 16/9
aspectRatio.AspectType = Enum.AspectType.FitWithinMaxSize
aspectRatio.DominantAxis = Enum.DominantAxis.Width
aspectRatio.Parent = frame

UISizeConstraint

-- Limit min/max size
local sizeConstraint = Instance.new("UISizeConstraint")
sizeConstraint.MinSize = Vector2.new(100, 50)
sizeConstraint.MaxSize = Vector2.new(500, 300)
sizeConstraint.Parent = frame

Screen Size Detection

local camera = workspace.CurrentCamera

local function getScreenCategory()
    local viewportSize = camera.ViewportSize

    if viewportSize.X < 600 then
        return "mobile"
    elseif viewportSize.X < 1200 then
        return "tablet"
    else
        return "desktop"
    end
end

local function updateLayout()
    local category = getScreenCategory()

    if category == "mobile" then
        mainFrame.Size = UDim2.new(0.95, 0, 0.9, 0)
        fontSize = 14
    elseif category == "tablet" then
        mainFrame.Size = UDim2.new(0.8, 0, 0.8, 0)
        fontSize = 16
    else
        mainFrame.Size = UDim2.new(0.6, 0, 0.7, 0)
        fontSize = 18
    end
end

camera:GetPropertyChangedSignal("ViewportSize"):Connect(updateLayout)

Safe Area (Notch/Button Handling)

local GuiService = game:GetService("GuiService")

local function getSafeInsets()
    local insets = GuiService:GetGuiInset()
    return insets
end

-- Apply safe area padding
local topInset = getSafeInsets()
mainFrame.Position = UDim2.new(0.5, 0, 0, topInset.Y + 10)

UI Animation

TweenService for UI

local TweenService = game:GetService("TweenService")

local function animateIn(frame)
    frame.Position = UDim2.new(0.5, 0, 1.5, 0)  -- Start below screen
    frame.Visible = true

    local tween = TweenService:Create(
        frame,
        TweenInfo.new(0.5, Enum.EasingStyle.Back, Enum.EasingDirection.Out),
        {Position = UDim2.new(0.5, 0, 0.5, 0)}
    )
    tween:Play()
    return tween
end

local function animateOut(frame)
    local tween = TweenService:Create(
        frame,
        TweenInfo.new(0.3, Enum.EasingStyle.Back, Enum.EasingDirection.In),
        {Position = UDim2.new(0.5, 0, 1.5, 0)}
    )
    tween:Play()
    tween.Completed:Connect(function()
        frame.Visible = false
    end)
    return tween
end

Spring Animation

local function springAnimation(frame, targetSize, stiffness, damping)
    stiffness = stiffness or 200
    damping = damping or 20

    local currentSize = {frame.Size.X.Scale, frame.Size.Y.Scale}
    local velocity = {0, 0}
    local target = {targetSize.X.Scale, targetSize.Y.Scale}

    local conn
    conn = RunService.RenderStepped:Connect(function(dt)
        for i = 1, 2 do
            local displacement = target[i] - currentSize[i]
            local springForce = displacement * stiffness
            local dampingForce = velocity[i] * damping
            local acceleration = springForce - dampingForce

            velocity[i] = velocity[i] + acceleration * dt
            currentSize[i] = currentSize[i] + velocity[i] * dt
        end

        frame.Size = UDim2.new(currentSize[1], 0, currentSize[2], 0)

        -- Check if settled
        local totalVelocity = math.abs(velocity[1]) + math.abs(velocity[2])
        local totalDisplacement = math.abs(target[1] - currentSize[1]) + math.abs(target[2] - currentSize[2])

        if totalVelocity < 0.001 and totalDisplacement < 0.001 then
            frame.Size = targetSize
            conn:Disconnect()
        end
    end)
end

Stagger Animation

local function staggerChildren(parent, delay, animateFunc)
    delay = delay or 0.1

    local children = parent:GetChildren()
    for i, child in ipairs(children) do
        task.delay((i - 1) * delay, function()
            animateFunc(child)
        end)
    end
end

-- Usage
staggerChildren(menuFrame, 0.05, function(button)
    button.Position = button.Position + UDim2.new(0.1, 0, 0, 0)
    button.BackgroundTransparency = 1

    TweenService:Create(button, TweenInfo.new(0.3), {
        Position = button.Position - UDim2.new(0.1, 0, 0, 0),
        BackgroundTransparency = 0
    }):Play()
end)

Input Handling

Button Interactions

local function setupButton(button)
    local originalSize = button.Size
    local originalColor = button.BackgroundColor3

    -- Hover
    button.MouseEnter:Connect(function()
        TweenService:Create(button, TweenInfo.new(0.1), {
            Size = originalSize + UDim2.new(0.02, 0, 0.02, 0),
            BackgroundColor3 = originalColor:Lerp(Color3.new(1, 1, 1), 0.1)
        }):Play()
    end)

    button.MouseLeave:Connect(function()
        TweenService:Create(button, TweenInfo.new(0.1), {
            Size = originalSize,
            BackgroundColor3 = originalColor
        }):Play()
    end)

    -- Click
    button.MouseButton1Down:Connect(function()
        TweenService:Create(button, TweenInfo.new(0.05), {
            Size = originalSize - UDim2.new(0.01, 0, 0.01, 0)
        }):Play()
    end)

    button.MouseButton1Up:Connect(function()
        TweenService:Create(button, TweenInfo.new(0.1), {
            Size = originalSize
        }):Play()
    end)
end

Drag and Drop

local function makeDraggable(frame)
    local dragging = false
    local dragStart
    local startPos

    frame.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 or
           input.UserInputType == Enum.UserInputType.Touch then
            dragging = true
            dragStart = input.Position
            startPos = frame.Position

            input.Changed:Connect(function()
                if input.UserInputState == Enum.UserInputState.End then
                    dragging = false
                end
            end)
        end
    end)

    frame.InputChanged:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseMovement or
           input.UserInputType == Enum.UserInputType.Touch then
            if dragging then
                local delta = input.Position - dragStart
                frame.Position = UDim2.new(
                    startPos.X.Scale,
                    startPos.X.Offset + delta.X,
                    startPos.Y.Scale,
                    startPos.Y.Offset + delta.Y
                )
            end
        end
    end)
end

Scroll Handling

local scrollingFrame = Instance.new("ScrollingFrame")
scrollingFrame.Size = UDim2.new(0.5, 0, 0.5, 0)
scrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)  -- Will be auto-sized
scrollingFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y
scrollingFrame.ScrollBarThickness = 8
scrollingFrame.ScrollBarImageColor3 = Color3.fromRGB(100, 100, 100)

-- List layout for automatic sizing
local listLayout = Instance.new("UIListLayout")
listLayout.SortOrder = Enum.SortOrder.LayoutOrder
listLayout.Padding = UDim.new(0, 5)
listLayout.Parent = scrollingFrame

-- Smooth scrolling
local function smoothScrollTo(targetPosition)
    local tween = TweenService:Create(
        scrollingFrame,
        TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
        {CanvasPosition = Vector2.new(0, targetPosition)}
    )
    tween:Play()
end

HUD Systems

Health Bar

local function createHealthBar(parent)
    local container = Instance.new("Frame")
    container.Size = UDim2.new(0.3, 0, 0, 30)
    container.Position = UDim2.new(0.02, 0, 0.05, 0)
    container.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
    container.Parent = parent

    local fill = Instance.new("Frame")
    fill.Size = UDim2.new(1, 0, 1, 0)
    fill.BackgroundColor3 = Color3.fromRGB(0, 200, 0)
    fill.Parent = container

    -- Delayed damage indicator
    local delayedFill = Instance.new("Frame")
    delayedFill.Size = UDim2.new(1, 0, 1, 0)
    delayedFill.BackgroundColor3 = Color3.fromRGB(200, 0, 0)
    delayedFill.ZIndex = fill.ZIndex - 1
    delayedFill.Parent = container

    local function updateHealth(current, max)
        local ratio = current / max
        local targetSize = UDim2.new(ratio, 0, 1, 0)

        -- Instant fill update
        fill.Size = targetSize

        -- Delayed red bar
        TweenService:Create(delayedFill, TweenInfo.new(0.5), {
            Size = targetSize
        }):Play()

        -- Color based on health
        if ratio > 0.5 then
            fill.BackgroundColor3 = Color3.fromRGB(0, 200, 0)
        elseif ratio > 0.25 then
            fill.BackgroundColor3 = Color3.fromRGB(200, 200, 0)
        else
            fill.BackgroundColor3 = Color3.fromRGB(200, 0, 0)
        end
    end

    return {container = container, update = updateHealth}
end

Minimap

local function createMinimap(parent, worldSize, minimapSize)
    local container = Instance.new("Frame")
    container.Size = UDim2.new(0, minimapSize, 0, minimapSize)
    container.Position = UDim2.new(1, -minimapSize - 10, 0, 10)
    container.BackgroundColor3 = Color3.fromRGB(20, 20, 20)
    container.ClipsDescendants = true
    container.Parent = parent

    -- Player marker
    local playerMarker = Instance.new("Frame")
    playerMarker.Size = UDim2.new(0, 10, 0, 10)
    playerMarker.AnchorPoint = Vector2.new(0.5, 0.5)
    playerMarker.BackgroundColor3 = Color3.fromRGB(0, 200, 0)
    playerMarker.Parent = container

    Instance.new("UICorner", playerMarker).CornerRadius = UDim.new(1, 0)

    local function worldToMinimap(worldPos)
        local x = (worldPos.X / worldSize + 0.5) * minimapSize
        local y = (worldPos.Z / worldSize + 0.5) * minimapSize
        return UDim2.new(0, x, 0, y)
    end

    local function update()
        local character = Players.LocalPlayer.Character
        if not character then return end

        local pos = character.PrimaryPart.Position
        playerMarker.Position = worldToMinimap(pos)

        -- Rotate marker based on facing
        local lookVector = character.PrimaryPart.CFrame.LookVector
        local angle = math.atan2(lookVector.X, lookVector.Z)
        playerMarker.Rotation = math.deg(angle)
    end

    RunService.RenderStepped:Connect(update)

    return container
end

Cooldown Indicator

local function createCooldownIndicator(button, duration)
    local overlay = Instance.new("Frame")
    overlay.Size = UDim2.new(1, 0, 1, 0)
    overlay.BackgroundColor3 = Color3.new(0, 0, 0)
    overlay.BackgroundTransparency = 0.5
    overlay.ZIndex = button.ZIndex + 1
    overlay.Parent = button

    local gradient = Instance.new("UIGradient")
    gradient.Rotation = -90
    gradient.Transparency = NumberSequence.new({
        NumberSequenceKeypoint.new(0, 0),
        NumberSequenceKeypoint.new(0.5, 0),
        NumberSequenceKeypoint.new(0.501, 1),
        NumberSequenceKeypoint.new(1, 1)
    })
    gradient.Parent = overlay

    local cooldownText = Instance.new("TextLabel")
    cooldownText.Size = UDim2.new(1, 0, 1, 0)
    cooldownText.BackgroundTransparency = 1
    cooldownText.TextColor3 = Color3.new(1, 1, 1)
    cooldownText.TextScaled = true
    cooldownText.ZIndex = overlay.ZIndex + 1
    cooldownText.Parent = overlay

    local startTime = os.clock()

    local conn
    conn = RunService.RenderStepped:Connect(function()
        local elapsed = os.clock() - startTime
        local remaining = duration - elapsed

        if remaining <= 0 then
            overlay:Destroy()
            conn:Disconnect()
            return
        end

        -- Update sweep
        local progress = elapsed / duration
        gradient.Offset = Vector2.new(0, progress * 2 - 1)

        -- Update text
        cooldownText.Text = string.format("%.1f", remaining)
    end)
end

Menu Systems

Page Navigation

local MenuController = {}
MenuController.pageStack = {}

function MenuController.pushPage(pageName)
    local currentPage = MenuController.pageStack[#MenuController.pageStack]
    if currentPage then
        currentPage.Visible = false
    end

    local newPage = Pages[pageName]
    newPage.Visible = true
    table.insert(MenuController.pageStack, newPage)
end

function MenuController.popPage()
    if #MenuController.pageStack <= 1 then return end

    local currentPage = table.remove(MenuController.pageStack)
    currentPage.Visible = false

    local previousPage = MenuController.pageStack[#MenuController.pageStack]
    previousPage.Visible = true
end

function MenuController.goToPage(pageName)
    -- Clear stack and go to specific page
    for _, page in ipairs(MenuController.pageStack) do
        page.Visible = false
    end
    MenuController.pageStack = {}
    MenuController.pushPage(pageName)
end

-- Back button
backButton.MouseButton1Click:Connect(function()
    MenuController.popPage()
end)

Modal Dialog

local function showModal(title, message, buttons)
    -- Darken background
    local backdrop = Instance.new("Frame")
    backdrop.Size = UDim2.new(1, 0, 1, 0)
    backdrop.BackgroundColor3 = Color3.new(0, 0, 0)
    backdrop.BackgroundTransparency = 0.5
    backdrop.ZIndex = 100
    backdrop.Parent = ScreenGui

    local modal = Instance.new("Frame")
    modal.Size = UDim2.new(0.4, 0, 0.3, 0)
    modal.Position = UDim2.new(0.5, 0, 0.5, 0)
    modal.AnchorPoint = Vector2.new(0.5, 0.5)
    modal.BackgroundColor3 = Color3.fromRGB(40, 40, 40)
    modal.ZIndex = 101
    modal.Parent = backdrop

    local titleLabel = Instance.new("TextLabel")
    titleLabel.Text = title
    titleLabel.Size = UDim2.new(1, 0, 0.2, 0)
    titleLabel.BackgroundTransparency = 1
    titleLabel.TextColor3 = Color3.new(1, 1, 1)
    titleLabel.TextScaled = true
    titleLabel.Parent = modal

    local messageLabel = Instance.new("TextLabel")
    messageLabel.Text = message
    messageLabel.Size = UDim2.new(0.9, 0, 0.4, 0)
    messageLabel.Position = UDim2.new(0.05, 0, 0.25, 0)
    messageLabel.BackgroundTransparency = 1
    messageLabel.TextColor3 = Color3.new(0.8, 0.8, 0.8)
    messageLabel.TextWrapped = true
    messageLabel.Parent = modal

    local buttonContainer = Instance.new("Frame")
    buttonContainer.Size = UDim2.new(0.9, 0, 0.25, 0)
    buttonContainer.Position = UDim2.new(0.05, 0, 0.7, 0)
    buttonContainer.BackgroundTransparency = 1
    buttonContainer.Parent = modal

    local listLayout = Instance.new("UIListLayout")
    listLayout.FillDirection = Enum.FillDirection.Horizontal
    listLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
    listLayout.Padding = UDim.new(0, 10)
    listLayout.Parent = buttonContainer

    local result = nil

    for _, buttonData in ipairs(buttons) do
        local btn = Instance.new("TextButton")
        btn.Size = UDim2.new(0, 100, 1, 0)
        btn.Text = buttonData.text
        btn.BackgroundColor3 = buttonData.color or Color3.fromRGB(60, 60, 60)
        btn.TextColor3 = Color3.new(1, 1, 1)
        btn.Parent = buttonContainer

        btn.MouseButton1Click:Connect(function()
            result = buttonData.value
            backdrop:Destroy()
        end)
    end

    -- Wait for result
    while not result and backdrop.Parent do
        task.wait()
    end

    return result
end

-- Usage
local choice = showModal("Confirm", "Are you sure?", {
    {text = "Yes", value = true, color = Color3.fromRGB(0, 150, 0)},
    {text = "No", value = false, color = Color3.fromRGB(150, 0, 0)}
})