Paragraph UI
Key RequiredUniversal

Paragraph UI

Universal ScriptNolanzNolanz 1.3K views 6 executesUpdated May 13, 2025
2.5/ 5(2 votes)
1150% Positive
Lua Script•415 lines (12724 chars)
-- Services
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local TextService = game:GetService("TextService")
local Players = game:GetService("Players")

-- Utility: Dynamic Instance Constructor
local function Create(className, properties)
    local instance = Instance.new(className)
    for property, value in pairs(properties) do
        instance[property] = value
    end
    return instance
end

-- Centralized Theme System
local Theme = {
    Background = Color3.fromRGB(25, 25, 25),
    Elevated = Color3.fromRGB(35, 35, 35),
    Text = Color3.fromRGB(240, 240, 240),
    TextSecondary = Color3.fromRGB(180, 180, 180),
    Accent = Color3.fromRGB(0, 120, 215),
    Border = Color3.fromRGB(60, 60, 60),
    Hover = Color3.fromRGB(45, 45, 45),
    Font = Enum.Font.Gotham,
    TitleFont = Enum.Font.GothamBold,
    Padding = 10,
    CornerRadius = 8,
    UIScale = 0.75
}

-- Janitor for Resource Management
local Janitor = {}
Janitor.__index = Janitor

function Janitor.new()
    return setmetatable({_tasks = {}}, Janitor)
end

function Janitor:Add(task)
    table.insert(self._tasks, task)
    return task
end

function Janitor:Destroy()
    for _, task in ipairs(self._tasks) do
        if type(task) == "function" then
            task()
        elseif typeof(task) == "RBXScriptConnection" then
            task:Disconnect()
        elseif typeof(task) == "Instance" then
            task:Destroy()
        end
    end
    self._tasks = {}
end

-- Custom Signal Class
local Signal = {}
Signal.__index = Signal

function Signal.new()
    return setmetatable({_connections = {}}, Signal)
end

function Signal:Connect(callback)
    local connection = {Connected = true}
    connection.Disconnect = function()
        connection.Connected = false
        for i, conn in ipairs(self._connections) do
            if conn == connection then
                table.remove(self._connections, i)
                break
            end
        end
    end
    connection.Callback = callback
    table.insert(self._connections, connection)
    return connection
end

function Signal:Fire(...)
    for _, connection in ipairs(self._connections) do
        if connection.Connected then
            task.spawn(connection.Callback, ...)
        end
    end
end

-- Paragraph Module
local Paragraph = {}
Paragraph.__index = Paragraph

function Paragraph.new(config)
    local self = setmetatable({}, Paragraph)
    
    self._janitor = Janitor.new()
    self.OnClose = Signal.new()
    self._isVisible = true
    self._title = config.Title or "Information"
    self._content = config.Content or "This is a paragraph of text."
    self._width = config.Width or 400
    self._maxHeight = config.MaxHeight or 500
    
    self:_createUI(config)
    self:_setupInteractions()
    
    return self
end

function Paragraph:_createUI(config)
    -- Main Container
    self.Container = self._janitor:Add(Create("Frame", {
        Size = UDim2.new(0, self._width, 0, 100),
        Position = UDim2.new(0.5, 0, 0.5, 0),
        AnchorPoint = Vector2.new(0.5, 0.5),
        BackgroundColor3 = Theme.Elevated,
        BorderSizePixel = 0,
        Parent = config.Parent or Players.LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("ScreenGui") or Create("ScreenGui", {Parent = Players.LocalPlayer:WaitForChild("PlayerGui")})
    }))
    
    Create("UICorner", {
        CornerRadius = UDim.new(0, Theme.CornerRadius),
        Parent = self.Container
    })
    
    Create("UIStroke", {
        Color = Theme.Border,
        Thickness = 1,
        Parent = self.Container
    })
    
    Create("UIScale", {
        Scale = Theme.UIScale,
        Parent = self.Container
    })
    
    -- Header Bar
    self.Header = Create("Frame", {
        Size = UDim2.new(1, 0, 0, 45),
        BackgroundColor3 = Theme.Background,
        BorderSizePixel = 0,
        Parent = self.Container
    })
    
    Create("UICorner", {
        CornerRadius = UDim.new(0, Theme.CornerRadius),
        Parent = self.Header
    })
    
    -- Title
    self.TitleLabel = Create("TextLabel", {
        Size = UDim2.new(1, -50, 1, 0),
        Position = UDim2.new(0, Theme.Padding, 0, 0),
        BackgroundTransparency = 1,
        Text = self._title,
        TextColor3 = Theme.Text,
        Font = Theme.TitleFont,
        TextSize = 16,
        TextXAlignment = Enum.TextXAlignment.Left,
        TextYAlignment = Enum.TextYAlignment.Center,
        Parent = self.Header
    })
    
    -- Close Button
    self.CloseButton = Create("TextButton", {
        Size = UDim2.new(0, 35, 0, 35),
        Position = UDim2.new(1, -40, 0.5, 0),
        AnchorPoint = Vector2.new(0, 0.5),
        BackgroundColor3 = Theme.Background,
        BackgroundTransparency = 1,
        Text = "✕",
        TextColor3 = Theme.TextSecondary,
        Font = Theme.Font,
        TextSize = 18,
        Parent = self.Header
    })
    
    Create("UICorner", {
        CornerRadius = UDim.new(0, 6),
        Parent = self.CloseButton
    })
    
    -- Content Container with Scrolling
    self.ContentFrame = Create("ScrollingFrame", {
        Size = UDim2.new(1, -20, 1, -55),
        Position = UDim2.new(0, 10, 0, 50),
        BackgroundTransparency = 1,
        BorderSizePixel = 0,
        ScrollBarThickness = 4,
        ScrollBarImageColor3 = Theme.Border,
        CanvasSize = UDim2.new(0, 0, 0, 0),
        AutomaticCanvasSize = Enum.AutomaticSize.Y,
        Parent = self.Container
    })
    
    -- Content Text
    self.ContentLabel = Create("TextLabel", {
        Size = UDim2.new(1, -10, 0, 0),
        AutomaticSize = Enum.AutomaticSize.Y,
        BackgroundTransparency = 1,
        Text = self._content,
        TextColor3 = Theme.Text,
        Font = Theme.Font,
        TextSize = 14,
        TextXAlignment = Enum.TextXAlignment.Left,
        TextYAlignment = Enum.TextYAlignment.Top,
        TextWrapped = true,
        RichText = true,
        LineHeight = 1.4,
        Parent = self.ContentFrame
    })
    
    Create("UIPadding", {
        PaddingLeft = UDim.new(0, 5),
        PaddingRight = UDim.new(0, 5),
        PaddingTop = UDim.new(0, 5),
        PaddingBottom = UDim.new(0, 10),
        Parent = self.ContentFrame
    })
    
    -- Auto-resize container based on content
    task.wait()
    self:_updateSize()
end

function Paragraph:_updateSize()
    local contentHeight = self.ContentLabel.AbsoluteSize.Y + 65
    local finalHeight = math.min(contentHeight, self._maxHeight)
    
    TweenService:Create(self.Container, TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {
        Size = UDim2.new(0, self._width, 0, finalHeight)
    }):Play()
end

function Paragraph:_setupInteractions()
    -- Close button hover effect
    local hoverTween
    self._janitor:Add(self.CloseButton.MouseEnter:Connect(function()
        if hoverTween then hoverTween:Cancel() end
        hoverTween = TweenService:Create(self.CloseButton, TweenInfo.new(0.15, Enum.EasingStyle.Quad), {
            BackgroundTransparency = 0,
            BackgroundColor3 = Theme.Hover,
            TextColor3 = Theme.Text
        })
        hoverTween:Play()
    end))
    
    self._janitor:Add(self.CloseButton.MouseLeave:Connect(function()
        if hoverTween then hoverTween:Cancel() end
        hoverTween = TweenService:Create(self.CloseButton, TweenInfo.new(0.15, Enum.EasingStyle.Quad), {
            BackgroundTransparency = 1,
            TextColor3 = Theme.TextSecondary
        })
        hoverTween:Play()
    end))
    
    -- Close button click
    self._janitor:Add(self.CloseButton.MouseButton1Click:Connect(function()
        self:Close()
    end))
    
    -- Make draggable
    self:_makeDraggable()
end

function Paragraph:_makeDraggable()
    local dragging, dragInput, dragStart, startPos
    
    self._janitor:Add(self.Header.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
            dragging = true
            dragStart = input.Position
            startPos = self.Container.Position
            
            input.Changed:Connect(function()
                if input.UserInputState == Enum.UserInputState.End then
                    dragging = false
                end
            end)
        end
    end))
    
    self._janitor:Add(self.Header.InputChanged:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
            dragInput = input
        end
    end))
    
    self._janitor:Add(UserInputService.InputChanged:Connect(function(input)
        if input == dragInput and dragging then
            local delta = input.Position - dragStart
            self.Container.Position = UDim2.new(
                startPos.X.Scale,
                startPos.X.Offset + delta.X,
                startPos.Y.Scale,
                startPos.Y.Offset + delta.Y
            )
        end
    end))
end

function Paragraph:SetTitle(newTitle)
    self._title = newTitle
    self.TitleLabel.Text = newTitle
    
    TweenService:Create(self.TitleLabel, TweenInfo.new(0.2, Enum.EasingStyle.Quad), {
        TextColor3 = Theme.Accent
    }):Play()
    
    task.delay(0.2, function()
        TweenService:Create(self.TitleLabel, TweenInfo.new(0.3, Enum.EasingStyle.Quad), {
            TextColor3 = Theme.Text
        }):Play()
    end)
end

function Paragraph:SetContent(newContent)
    self._content = newContent
    self.ContentLabel.Text = newContent
    
    task.wait()
    self:_updateSize()
end

function Paragraph:Toggle()
    self._isVisible = not self._isVisible
    self.Container.Visible = self._isVisible
    
    if self._isVisible then
        self:_playShowAnimation()
    end
end

function Paragraph:Show()
    self._isVisible = true
    self.Container.Visible = true
    self:_playShowAnimation()
end

function Paragraph:_playShowAnimation()
    self.Container.Size = UDim2.new(0, self._width, 0, 0)
    
    local contentHeight = self.ContentLabel.AbsoluteSize.Y + 65
    local finalHeight = math.min(contentHeight, self._maxHeight)
    
    TweenService:Create(self.Container, TweenInfo.new(0.4, Enum.EasingStyle.Back, Enum.EasingDirection.Out), {
        Size = UDim2.new(0, self._width, 0, finalHeight)
    }):Play()
end

function Paragraph:Close()
    local tween = TweenService:Create(self.Container, TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {
        Size = UDim2.new(0, self._width, 0, 0)
    })
    tween:Play()
    
    tween.Completed:Connect(function()
        self._isVisible = false
        self.Container.Visible = false
        self.OnClose:Fire()
    end)
end

function Paragraph:Destroy()
    self._janitor:Destroy()
end

-- Example Usage
local screenGui = Create("ScreenGui", {
    Parent = Players.LocalPlayer:WaitForChild("PlayerGui"),
    ResetOnSpawn = false
})

local exampleContent = [[Welcome to the <b>Professional Paragraph System</b>!

This UI component demonstrates several advanced features including rich text formatting, automatic content sizing, smooth animations, and responsive design patterns.

<font color="rgb(0, 120, 215)">Key Features:</font>
• Dynamic height adjustment based on content length
• Smooth expand and collapse animations using TweenService
• Draggable header for repositioning
• Scrollable content area for lengthy paragraphs
• Clean, professional dark theme design
• Proper resource management with Janitor pattern

The text supports <b>bold formatting</b>, <i>italic text</i>, and <u>underlined content</u>. You can also use <font color="rgb(0, 200, 100)">custom colors</font> to highlight important information.

Try dragging this window around by clicking and holding the header bar. The close button in the top-right corner will smoothly animate the window closed when clicked.]]

local paragraph = Paragraph.new({
    Parent = screenGui,
    Title = "System Documentation",
    Content = exampleContent,
    Width = 450,
    MaxHeight = 550
})

-- Listen for close event
paragraph.OnClose:Connect(function()
    print("Paragraph closed")
end)

-- Example: Toggle with keybind
UserInputService.InputBegan:Connect(function(input, gameProcessed)
    if not gameProcessed and input.KeyCode == Enum.KeyCode.P then
        paragraph:Toggle()
    end
end)

-- Example: Update content dynamically
task.delay(5, function()
    paragraph:SetTitle("Updated Information")
    paragraph:SetContent("This content was updated after 5 seconds to demonstrate the dynamic updating capabilities of the paragraph system. The container automatically resizes to fit the new content!")
end)

Features & Information

Execute to see the features

Community Comments (2)

XxGoodChildrenxXXxGoodChildrenxXMay 13, 2025

Omg

yabutarcmiguelyabutarcmiguelMay 13, 2025

is it real bro

More Scripts for Universal Scripts