--!nonstrict --[[ Kuik 1.0 — a Roblox UI library in the hairline-rule style. The style: there is no panel. A window is two 1px vertical rules with nothing between them, floating over the game. No fill, no border, no corner radius, no accent colour. Hierarchy is carried entirely by typography and negative space; the only bright objects on screen are the controls themselves. Colour is reserved for meaning — red is a warning, amber is a log level, blue is a role. It is never decoration. Single-file ModuleScript. Put it in ReplicatedStorage, require from a LocalScript. local Kuik = require(game.ReplicatedStorage.Kuik) local Window = Kuik.Window({ Title = "Settings", Subtitle = "Manage your preferences", Key = Enum.KeyCode.Tab, }) local Categories = Window:Section("Categories", "Choose a category to configure.") local Tabs = Categories:Tabs() local Visual = Tabs:Page("Visual") Visual:Toggle({ Label = "Show UI/UX", Description = "Show and configure interface elements.", Default = true, Callback = function(On) print(On) end, }) Everything in Theme is live — assign to it before constructing and the whole library follows. ]] --============================================================================ -- Services --============================================================================ local TweenService = game:GetService("TweenService") local UserInputService = game:GetService("UserInputService") local RunService = game:GetService("RunService") local Lighting = game:GetService("Lighting") local Players = game:GetService("Players") local LocalPlayer = Players.LocalPlayer --============================================================================ -- Version --============================================================================ local VERSION = { Name = "Kuik", Version = "1.0.0", } --============================================================================ -- Theme -- -- Transparency values rather than colours for anything dim: the library is -- designed to sit over arbitrary game footage, so everything reads as a -- fraction of white rather than a fixed grey. --============================================================================ local Theme = { --- Montserrat is the closest match to the reference. Swap for --- Font.fromEnum(Enum.Font.Gotham) if you want a built-in. Font = Font.fromName("Montserrat", Enum.FontWeight.Regular), FontMedium = Font.fromName("Montserrat", Enum.FontWeight.Medium), FontBold = Font.fromName("Montserrat", Enum.FontWeight.SemiBold), FontMono = Font.fromEnum(Enum.Font.Code), White = Color3.fromRGB(255, 255, 255), Black = Color3.fromRGB(20, 20, 20), --- The backdrop darkens rather than lightens. Everything the library --- draws is white, so a light wash erases the interface over a bright --- scene — which is exactly what happened over a blue sky. Scrim = Color3.fromRGB(8, 8, 10), --- Text is always white; only its transparency changes. TextPrimary = 0.00, TextSecondary = 0.42, TextTertiary = 0.58, TextDisabled = 0.72, RuleTransparency = 0.45, RowTransparency = 0.94, RowHoverTransparency = 0.88, ChipTransparency = 0.45, --- Semantic. Never used for decoration. Danger = Color3.fromRGB(255, 72, 72), Warn = Color3.fromRGB(255, 176, 46), Info = Color3.fromRGB(96, 156, 255), Good = Color3.fromRGB(96, 210, 128), Neutral = Color3.fromRGB(255, 255, 255), --- Motion, lifted from Lume's token file — same role names, same --- durations, same easings — so anything built with both moves --- identically. Pass a role name to Tween, or a literal spec table. Motion = { instant = { Duration = 0 }, --- window open / close enter = { Duration = 0.46, Easing = "outQuint" }, exit = { Duration = 0.38, Easing = "outQuad" }, --- the tab pill sliding, a section unfolding expand = { Duration = 0.34, Easing = "outQuint" }, --- pointer feedback hover = { Duration = 0.12, Easing = "outQuad" }, press = { Duration = 0.07, Easing = "outQuad" }, --- text and opacity fade = { Duration = 0.34, Easing = "outQuad" }, hint = { Duration = 0.22, Easing = "outQuad" }, --- a control's own value changing item = { Duration = 0.30, Easing = "outQuad" }, list = { Duration = 0.28, Easing = "outQuad" }, --- the notify card's drop, which overshoots on purpose drop = { Duration = 0.46, Easing = "outBack" }, --- Springs. Anything whose target can change mid-flight belongs here: --- retargeting keeps the current position *and velocity*, so the --- motion bends toward the new destination instead of restarting. --- Damping is the coefficient, not the ratio — critical for a given --- stiffness is 2 * sqrt(stiffness). layout = { Stiffness = 190, Damping = 24 }, morph = { Stiffness = 210, Damping = 26 }, --- follows a pointer without wobbling under it drag = { Stiffness = 620, Damping = 42 }, --- the settle after release. Deliberately under critical (2*sqrt(340) --- is about 37) so it overshoots a little and comes back — that small --- bounce is what reads as weight. toss = { Stiffness = 340, Damping = 22 }, reveal = { Stiffness = 150, Damping = 20 }, snap = { Stiffness = 600, Damping = 40 }, --- Wheel scrolling, taken over from the engine so it glides. --- Deliberately *over* critical (2*sqrt(300) is about 34.6): a scroll --- that bounces past where you aimed it feels broken, not lively. scroll = { Stiffness = 300, Damping = 38 }, }, } local Metrics = { WindowWidth = 460, WindowHeight = 500, WindowPadding = 26, TitleSize = 27, SubtitleSize = 14, SectionTitleSize = 18, SectionDescSize = 12, RowLabelSize = 16, RowDescSize = 11, RowHeight = 46, RowHeightTall = 62, RowGap = 8, --- PillRadius caps the fully-round elements, so dropping it to 0 squares --- every toggle and tab in one line. RowRadius = 4, CardRadius = 8, PillRadius = 999, TabHeight = 30, TabPadding = 14, TabGap = 6, TabIcon = 14, ToggleWidth = 50, ToggleHeight = 25, KnobInset = 3, SliderTrack = 3, SliderThumbWidth = 4, SliderThumbHeight = 15, ChipWidth = 52, ChipHeight = 23, ChipRadius = 5, MonoSize = 10, } --============================================================================ -- Utilities --============================================================================ local function New(ClassName, Properties, Children) local Object = Instance.new(ClassName) for Key, Value in Properties or {} do if Key ~= "Parent" then Object[Key] = Value end end for _, Child in Children or {} do Child.Parent = Object end if Properties and Properties.Parent then Object.Parent = Properties.Parent end return Object end --- Easing names read