Starlight Solutions, Inc. - Stargaze UI Framework

Hello, I noticed that a lot of developers struggle to compete with modern UI standards or certain UI styles, especially slick themes, glass architecture, interactive UI, etc. This is why here at Starlight Solutions, Inc. we decided to release a framework to make it easier for developers to have a variation of interactive & customizable UI presets. We may update this in the feature as it does not have many features yet, but here is the current module.

--[[

⠀⠀⠀⢸⣦⡀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢸⣏⠻⣶⣤⡶⢾⡿⠁⠀⢠⣄⡀⢀⣴⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣀⣼⠷⠀⠀⠁⢀⣿⠃⠀⠀⢀⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠴⣾⣯⣅⣀⠀⠀⠀⠈⢻⣦⡀⠒⠻⠿⣿⡿⠿⠓⠂⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠉⢻⡇⣤⣾⣿⣷⣿⣿⣤⠀⠀⣿⠁⠀⠀⠀⢀⣴⣿⣿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⡿⠏⠀⢀⠀⠀⠿⣶⣤⣤⣤⣄⣀⣴⣿⡿⢻⣿⡆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠟⠁⠀⢀⣼⠀⠀⠀⠹⣿⣟⠿⠿⠿⡿⠋⠀⠘⣿⣇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢳⣶⣶⣿⣿⣇⣀⠀⠀⠙⣿⣆⠀⠀⠀⠀⠀⠀⠛⠿⣿⣦⣤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⣹⣿⣿⣿⣿⠿⠋⠁⠀⣹⣿⠳⠀⠀⠀⠀⠀⠀⢀⣠⣽⣿⡿⠟⠃
⠀⠀⠀⠀⠀⢰⠿⠛⠻⢿⡇⠀⠀⠀⣰⣿⠏⠀⠀⢀⠀⠀⠀⣾⣿⠟⠋⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⣰⣿⣿⣾⣿⠿⢿⣷⣀⢀⣿⡇⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠉⠁⠀⠀⠀⠀⠙⢿⣿⣿⠇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀

	Starlight Solutions, Inc.

	Project: Stargaze UI
	Author: festiveicing (@SirJolly)

]]

local ColorTable = {}

ColorTable.HexVals = {
	"#121212",
	"#1A1A1A",
	"#03000D",
}

ColorTable.States = {}
ColorTable.Lighting = {}
ColorTable.Effects = {}

local BLACK = Color3.new()
local WHITE = Color3.new(1, 1, 1)

local function Clamp(Value)
	if typeof(Value) ~= "number" then
		error(("Expected number, got %s."):format(typeof(Value)), 3)
	end

	return math.clamp(Value, 0, 1)
end

local function NormalizeHex(Hex)
	if typeof(Hex) ~= "string" then
		error(("Expected hex string, got %s."):format(typeof(Hex)), 3)
	end

	local Value = Hex:gsub("^#", ""):upper()

	if #Value == 3 then
		Value = Value:gsub(".", "%1%1")
	end

	if #Value ~= 6 or not Value:match("^[%x]+$") then
		error("Invalid hex color.", 3)
	end

	return "#" .. Value
end

local function ParseHex(Hex)
	local Normalized = NormalizeHex(Hex)
	local Value = Normalized:sub(2)

	local R = tonumber(Value:sub(1, 2), 16)
	local G = tonumber(Value:sub(3, 4), 16)
	local B = tonumber(Value:sub(5, 6), 16)

	if not R or not G or not B then
		error("Failed to parse hex color.", 3)
	end

	return R, G, B
end

local function ResolveColor(Color)
	local ColorType = typeof(Color)

	if ColorType == "Color3" then
		return Color
	end

	if ColorType == "string" then
		return Color3.fromRGB(ParseHex(Color))
	end

	error(("Expected Color3 or hex string, got %s."):format(ColorType), 3)
end

local function ToByte(Value)
	return math.clamp(math.round(Value * 255), 0, 255)
end

local function ToChannel(Value)
	if typeof(Value) ~= "number" then
		error(("Expected number, got %s."):format(typeof(Value)), 3)
	end

	return math.clamp(math.round(Value), 0, 255)
end

for Index, Hex in ipairs(ColorTable.HexVals) do
	ColorTable.HexVals[Index] = NormalizeHex(Hex)
end

function ColorTable.IsHex(Hex)
	local Success = pcall(NormalizeHex, Hex)
	return Success
end

function ColorTable.NormalizeHex(Hex)
	return NormalizeHex(Hex)
end

function ColorTable.Resolve(Color)
	return ResolveColor(Color)
end

function ColorTable.FromRGB(R, G, B)
	return Color3.fromRGB(
		ToChannel(R),
		ToChannel(G),
		ToChannel(B)
	)
end

function ColorTable.ToRGB(Color)
	if typeof(Color) == "Color3" then
		return ToByte(Color.R), ToByte(Color.G), ToByte(Color.B)
	end

	return ParseHex(Color)
end

function ColorTable.ToColor3(Color)
	return ResolveColor(Color)
end

function ColorTable.ToHex(Color)
	if typeof(Color) == "string" then
		return NormalizeHex(Color)
	end

	local Resolved = ResolveColor(Color)

	return string.format(
		"#%02X%02X%02X",
		ToByte(Resolved.R),
		ToByte(Resolved.G),
		ToByte(Resolved.B)
	)
end

function ColorTable.Blend(ColorA, ColorB, Alpha)
	return ResolveColor(ColorA):Lerp(ResolveColor(ColorB), Clamp(Alpha))
end

function ColorTable.Tint(Color, Tint, Alpha)
	return ResolveColor(Color):Lerp(ResolveColor(Tint), Clamp(Alpha))
end

function ColorTable.Lighten(Color, Amount)
	return ResolveColor(Color):Lerp(WHITE, Clamp(Amount))
end

function ColorTable.Darken(Color, Amount)
	return ResolveColor(Color):Lerp(BLACK, Clamp(Amount))
end

function ColorTable.Desaturate(Color, Amount)
	local H, S, V = ResolveColor(Color):ToHSV()
	return Color3.fromHSV(H, Clamp(S * (1 - Amount)), V)
end

function ColorTable.Saturate(Color, Amount)
	local H, S, V = ResolveColor(Color):ToHSV()
	return Color3.fromHSV(H, Clamp(S * (1 + Amount)), V)
end

function ColorTable.ShiftHue(Color, Degrees)
	if typeof(Degrees) ~= "number" then
		error(("Expected number, got %s."):format(typeof(Degrees)), 2)
	end

	local H, S, V = ResolveColor(Color):ToHSV()
	H = (H + (Degrees / 360)) % 1
	return Color3.fromHSV(H, S, V)
end

function ColorTable.WithOpacity(Color, Opacity)
	return {
		Color = ResolveColor(Color),
		Transparency = 1 - Clamp(Opacity),
	}
end

function ColorTable.States.Default(Color)
	return ResolveColor(Color)
end

function ColorTable.States.Hover(Color)
	return ColorTable.Lighten(
		ColorTable.Desaturate(Color, 0.08),
		0.05
	)
end

function ColorTable.States.Active(Color)
	return ColorTable.Lighten(Color, 0.10)
end

function ColorTable.States.Pressed(Color)
	return ColorTable.Darken(Color, 0.12)
end

function ColorTable.States.Selected(Color)
	return ColorTable.Tint(Color, WHITE, 0.08)
end

function ColorTable.States.Focus(Color)
	return ColorTable.Lighten(
		ColorTable.Saturate(Color, 0.05),
		0.08
	)
end

function ColorTable.States.Disabled(Color)
	return ColorTable.Desaturate(
		ColorTable.Darken(Color, 0.30),
		0.80
	)
end

function ColorTable.States.Drag(Color)
	return ColorTable.Lighten(
		ColorTable.Saturate(Color, 0.08),
		0.12
	)
end

function ColorTable.States.DropTarget(Color)
	return ColorTable.Tint(
		Color,
		Color3.fromRGB(96, 174, 255),
		0.15
	)
end

function ColorTable.States.Success(Color)
	return ColorTable.Tint(
		Color,
		Color3.fromRGB(90, 220, 120),
		0.12
	)
end

function ColorTable.States.Warning(Color)
	return ColorTable.Tint(
		Color,
		Color3.fromRGB(255, 196, 64),
		0.15
	)
end

function ColorTable.States.Error(Color)
	return ColorTable.Tint(
		Color,
		Color3.fromRGB(255, 92, 92),
		0.18
	)
end

function ColorTable.Lighting.Glow(Color)
	local H, S, V = ResolveColor(Color):ToHSV()

	return Color3.fromHSV(
		H,
		Clamp(S * 0.85),
		Clamp(V * 1.15)
	)
end

function ColorTable.Lighting.Highlight(Color)
	return ColorTable.Tint(Color, WHITE, 0.12)
end

function ColorTable.Lighting.Shadow(Color)
	return ColorTable.Darken(Color, 0.25)
end

function ColorTable.Lighting.Overlay(Color)
	return ColorTable.Tint(Color, WHITE, 0.08)
end

function ColorTable.Lighting.Backdrop(Color)
	return ColorTable.Desaturate(
		ColorTable.Darken(Color, 0.15),
		0.20
	)
end

function ColorTable.Effects.Surface(Color)
	return ColorTable.Desaturate(Color, 0.12)
end

function ColorTable.Effects.Muted(Color)
	return ColorTable.Desaturate(Color, 0.35)
end

function ColorTable.Effects.Elevated(Color)
	return ColorTable.Lighten(Color, 0.04)
end

function ColorTable.Effects.Recessed(Color)
	return ColorTable.Darken(Color, 0.08)
end

function ColorTable.Effects.Border(Color)
	return ColorTable.Lighten(
		ColorTable.Desaturate(Color, 0.10),
		0.10
	)
end

function ColorTable.Effects.Accent(Color)
	return ColorTable.Saturate(
		ColorTable.Lighten(Color, 0.03),
		0.08
	)
end

function ColorTable.Effects.Glass(Color)
	return {
		Color = ColorTable.Desaturate(Color, 0.45),
		Transparency = 0.35,
	}
end

return ColorTable