OnyxUI - Quick, customizable UI components for Fusion

GithubBanner

Quick, quality UI components for Fusion

📦 Latest | 🔧 Source | 📖 Docs


Features :sparkles:

High quality components :jigsaw:

Make quality UI faster with dozens of fully-typed components.

Comprehensive theming :art:

Give your UI the look and feel you desire with OnyxUI’s featureful theming system.

Styling, now not stupid :paintbrush:

Messy markup sucks. Style any component, with less code, using OnyxUI’s passthrough styling props.

Examples :star:

These projects use OnyxUI to power their user experience

lonekaMenu

Links :link:

Installation :package:

Documentation :page_facing_up:


8 Likes

Was this already released? I feel like i’ve already seen this

2 Likes

Welcome to ROBLOX, where 5 different versions of the same principle get released, along-side 2 remakes and 9 ‘forks’.

Just wait till you see the collection of networking libaries!

3 Likes

They all have different flippin purposes!11!

im joking…

No I mean like the same exact thing, even the name

You should see the JS ecosystem lol

2 Likes

I also announced it on the Roblox OSS Discord if you’ve been active on there

1 Like

OnyxUI - 0.4.0

  • Upgraded for Fusion 0.3 :tada:
  • New utilities :wrench:
  • Restructured codebase :construction_site:
  • Improved select components :jigsaw:
1 Like

OnyxUI - v0.4.1

What’s Changed

  • Fixed improper structuring with TypedPackages :bangbang:
1 Like

while working with onyxui module, i got a problem, the [Children] function wasnt working why?

here is my code (i made a template for onyxui stuff)

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local OnyxUI = require(ReplicatedStorage.OnyxUI)
local Fusion = require(ReplicatedStorage.Fusion)

local ROnyxUI = ReplicatedStorage.OnyxUI
local Components = require(ROnyxUI.Packages.OnyxUI.Components)
local Themer = OnyxUI.Themer
local ColorUtils = require(ROnyxUI.Packages.ColorUtils)
local Util = OnyxUI.Util
local InnerScope = Fusion.innerScope
local scoped = Fusion.scoped
local Children = Fusion.Children
local Peek = Fusion.peek

local INDICATOR_COLORS = { 
	Util.Colors.Red["500"], 
	Util.Colors.Green["400"], 
	Util.Colors.Orange["500"], 
	Util.Colors.Stone["600"] 
}

return function(Parent: GuiObject, ParentFrame: Frame)
	local Scope = scoped(Fusion, Components)
	local Theme = Themer.Theme:now()
	local RandomDecimalNumber = Scope:Value(0)
	local NotificationCount = Scope:Value(0)
	local IndicatorColor = Scope:Value(INDICATOR_COLORS[2])
	local RingThickness = Scope:Value(Theme.StrokeThickness["2"])

	local RandomizerThread = task.spawn(function()
		while task.wait(1) do
			IndicatorColor:set(INDICATOR_COLORS[math.random(1, #INDICATOR_COLORS)])

			if Peek(RingThickness) == Peek(Theme.StrokeThickness["2"]) then
				RingThickness:set(Peek(Theme.StrokeThickness["3"]))
			else
				RingThickness:set(Peek(Theme.StrokeThickness["2"]))
			end
		end
	end)
	local RandomizerDecimalThread = task.spawn(function()
		while task.wait(1) do
			RandomDecimalNumber:set(math.random(1, 100000))
		end
	end)
	local CountThread = task.spawn(function()
		while task.wait(0.08) do
			if Peek(NotificationCount) == 100 then
				task.wait(3)
				NotificationCount:set(1)
			else
				NotificationCount:set(Peek(NotificationCount) + 1)
			end
		end
	end)

	Scope:Button {
		Parent = ParentFrame,
		Content = { 
			"Shop ",
			"rbxassetid://75029721407761",
			".",
			" Loneka ",
			"rbxassetid://16735398592",
			". Hi!", 
		}
	}
	Scope:NumberSpinner {
		Parent = ParentFrame,
		Value = RandomDecimalNumber,
		Commas = true,
		Prefix = "$",
		Font = Enum.Font.FredokaOne,
	}
	Scope:Badge {
		Parent = ParentFrame,
		Content = { "Badge" },
	}
	Scope:Badge {
		Parent = ParentFrame,
		Content = { " PREMIUM" },
		Color = Util.Colors.Amber["500"],
	}
	Scope:Badge {
		Parent = ParentFrame,
		Content = { "rbxassetid://103798699021677", "-50%" },
		Color = Util.Colors.Red["500"],
	}
	Scope:Badge {
		Parent = ParentFrame,
		Content = { "rbxassetid://140490867216758", "LIMITED" },
		Color = Util.Colors.Red["500"],
	}
	Scope:Badge {
		Content = Scope:Computed(function(Use)
			if Use(NotificationCount) >= 100 then
				return { "99+" }
			else
				return { Use(NotificationCount) }
			end
		end),
		Color = Theme.Colors.Primary.Main,
	}
	Scope:Text {
		Text = "It's also proportional alongside text.",
	}
	Scope:Badge {
		Content = { "rbxassetid://137979359742656", "TRUE" },
		Color = Util.Colors.Green["500"],
	}
	Scope:IconButton {
		Parent = ParentFrame,
		Image = "rbxassetid://10814531047",
	}
	Scope:IconButton {
		Parent = ParentFrame,
		Image = "rbxassetid://10814531047",
		Color = Theme.Colors.Primary.Main,
	}
	Scope:IconButton {
		Parent = ParentFrame,
		Image = "rbxassetid://11560341132",
		Color = Util.Colors.Amber["500"],
	}
	Scope:IconButton {
		Parent = ParentFrame,
		Image = "rbxassetid://13405228418",
		Color = Util.Colors.Red["500"],
		Style = "Outlined",
	}
	Scope:IconButton {
		Parent = ParentFrame,
		Image = "rbxassetid://13405228418",
		Color = Util.Colors.Red["500"],
		Style = "Ghost",
	}
	Scope:IconButton {
		Parent = ParentFrame,
		Image = "rbxassetid://10814531047",
		Disabled = true,
	}
	Scope:IconButton {
		Parent = ParentFrame,
		Image = "rbxassetid://10814531047",
		Style = "Ghost",
		Disabled = true,
	}
	Scope:Checkbox {
		Parent = ParentFrame,
		Icon = "rbxassetid://16743550373",
		Color = Util.Colors.Red["500"],
	}
	Scope:Checkbox {
		Parent = ParentFrame, 
		Color = Util.Colors.Green["500"],
	}
	
	Scope:SwitchInput {
		Parent = ParentFrame,
	}
	Scope:SwitchInput {
		Parent = ParentFrame,
		Disabled = true,
	}
	Scope:TextArea {
		Parent = ParentFrame,
		Size = Scope:Computed(function(Use)
			return UDim2.new(
				UDim.new(1, 0),
				UDim.new(0, (Use(Theme.TextSize["1"]) * 2) + (Use(Theme.Spacing["0.5"]) * 2))
			)
		end),
	}
	Scope:TextArea {
		Parent = ParentFrame,
		Size = UDim2.new(UDim.new(1, 0), UDim.new(0, 100)),
		CharacterLimit = 60,
		PlaceholderText = "This TextArea has a character limit.",
	}
	Scope:TextArea {
		Parent = ParentFrame,
		Size = UDim2.new(UDim.new(1, 0), UDim.new(0, 0)),
		AutomaticSize = Enum.AutomaticSize.Y,
		PlaceholderText = "This one will expand as you type.",
	}
	Scope:TextInput {
		Parent = ParentFrame,
	}
	Scope:TextInput {
		Parent = ParentFrame,
		PlaceholderText = "Disabled TextInput.",
		Disabled = true,
	}
	Scope:TextInput {
		Parent = ParentFrame,
		PlaceholderText = "Twenty characters only!",
		CharacterLimit = 20,
	}
	Scope:TextInput {
		Parent = ParentFrame,
		PlaceholderText = "Something dangerous!",
		Color = Theme.Colors.Error.Main,
	}
	Scope:TitleBar {
		Parent = ParentFrame,
		Content = { "Title" },
	}
	Scope:TitleBar {
		Parent = ParentFrame,
		Content = { "rbxassetid://75029721407761", "Shop" },
	}
	
	local Progress = Scope:Value(0)

	local ColorThread = task.spawn(function()
		while task.wait(1) do
			Progress:set(math.random(0, 1000) / 1000)
		end
	end)

	Scope:Text {
		Parent = ParentFrame,
		Text = "HP",
	}
	Scope:ProgressBar {
		Parent = ParentFrame,
		Progress = Progress,
		Color = Util.Colors.Red["500"],
		FlexMode = Enum.UIFlexMode.Fill,
	}
	Scope:Avatar {
		Parent = ParentFrame,
		Image = "rbxthumb://type=AvatarHeadShot&id=144146784&w=150&h=150",
	}
	Scope:Avatar {
		Parent = ParentFrame,
		Image = "rbxthumb://type=AvatarHeadShot&id=144146784&w=150&h=150",
		CornerRadius = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.CornerRadius.Full))
		end),
	}
	Scope:Avatar {
		Parent = ParentFrame,
		Image = "rbxthumb://type=AvatarHeadShot&id=144146784&w=150&h=150",
		CornerRadius = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.CornerRadius.Full))
		end),
		RingEnabled = true,
		RingColor = IndicatorColor,
	}
	Scope:Avatar {
		Parent = ParentFrame,
		Image = "rbxthumb://type=AvatarHeadShot&id=144146784&w=150&h=150",
		CornerRadius = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.CornerRadius.Full))
		end),
		IndicatorEnabled = true,
		IndicatorColor = IndicatorColor,
	}
	Scope:Avatar {
		Parent = ParentFrame,
		Image = "rbxthumb://type=AvatarHeadShot&id=144146784&w=150&h=150",
		CornerRadius = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.CornerRadius.Full))
		end),
		RingEnabled = true,
		RingColor = Util.Colors.Green["400"],
		RingThickness = RingThickness,
	}
	Scope:Avatar {
		Parent = ParentFrame,
		Image = "rbxthumb://type=AvatarHeadShot&id=144146784&w=150&h=150",
		CornerRadius = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.CornerRadius.Full))
		end),
		IndicatorEnabled = true,
		IndicatorColor = Util.Colors.Sky["500"],
		IndicatorIcon = "rbxassetid://13805569043",
	}
end

try in any singular frame or thing just put [Children] and the other stuff and wont work why is that? btw here is my loader:

require(script.UI)(script.Parent, script.Parent.MainFrame.ScrollingFrame)
1 Like

I cannot stress how good this resource is, I looked into it after you linked me it. Wow Avafe, seriously just wow…

Truly a blessing to us, thank you!

2 Likes

ikr i got into fusion UI designing (today in the last 5 hours ive been learning fusion and onyxui)

btw avafe, can you release a uncopylocked place with ALL of the components in one script or multiple if you want/can, this will be wayy easier it was hard trying to learn how to add components but then i figured it out.

1 Like

This might be a big ask, but it WOULD be super helpful, especially considering that ONYX and FUSION are so useful but quite advanced in some cases.

thats what im saying
because for starters it is REALLLYYYY hard for them

( i have 5 years of ui designing and scripting experience and it still took me 2-3 hours jsut to learn and make those ui components template in my script i provided, but after all of that scripting i still didnt add alll of the components so it would be rly helpful,

(avafe im seeing you type : ) )

Are you trying to make a component? I’d recommend looking at OnyxUI’s source code to see how components are formatted internally. In short, you should to expose a Props argument, and make use of OnyxUI.Util.CombineProps(Props, {}) for children to be passed down.

If it’s a top-level GUI, doing [Children] = Props[Children] should work fine.

You can download the .rbxm file here for OnyxUI’s entire source code in Roblox format, including its components.

1 Like

I am trying to do this:

local OnyxUI = script.Parent.Parent

local Fusion = require(OnyxUI.Parent.Fusion)
local ColorUtils = require(OnyxUI.Parent.ColorUtils)
local Themer = require(OnyxUI.Themer)
local Util = require(OnyxUI.Util)

local Scoped = Fusion.scoped
local Children = Fusion.Children
local Peek = Fusion.peek

local Frame = require(script.Parent.Frame)
local ProgressBar = require(script.Parent.ProgressBar)
local Text = require(script.Parent.Text)
local Components = {
	Frame = Frame,
	ProgressBar = ProgressBar,
	Text = Text,
}

return {
	story = function(Parent: GuiObject)
		local Scope = Scoped(Fusion, Components)
		local Theme = Themer.Theme:now()

		local Progress = Scope:Value(0)
		local Color = Scope:Value(Color3.fromRGB(255, 0, 0))

		local ColorThread = task.spawn(function()
			while task.wait(1) do
				Progress:set(math.random(0, 1000) / 1000)
				Color:set(ColorUtils.Rotate(Peek(Color), 100))
			end
		end)

		Scope:innerScope({
			function()
				task.cancel(ColorThread)
			end,
		})

		Scope:Frame {
			Parent = Parent,
			ListEnabled = true,
			ListHorizontalFlex = Enum.UIFlexAlignment.Fill,
			ListPadding = Scope:Computed(function(Use)
				return UDim.new(0, Use(Theme.Spacing["1.5"]))
			end),

			[Children] = {
				Scope:Frame {
					ListEnabled = true,
					ListHorizontalFlex = Enum.UIFlexAlignment.Fill,
					ListPadding = Scope:Computed(function(Use)
						return UDim.new(0, Use(Theme.Spacing["0.5"]))
					end),

					[Children] = {
						Scope:Text {
							Text = "Horizontal",
						},
						Scope:ProgressBar {
							Progress = 0.75,
						},
						Scope:ProgressBar {
							Progress = Progress,
							Color = Color,
						},
					},
				},
				Scope:Frame {
					ListEnabled = true,
					ListHorizontalFlex = Enum.UIFlexAlignment.Fill,
					ListPadding = Scope:Computed(function(Use)
						return UDim.new(0, Use(Theme.Spacing["0.5"]))
					end),

					[Children] = {
						Scope:Text {
							Text = "With text",
						},
						Scope:Frame {
							ListEnabled = true,
							ListFillDirection = Enum.FillDirection.Horizontal,
							ListVerticalAlignment = Enum.VerticalAlignment.Center,
							ListPadding = Scope:Computed(function(Use)
								return UDim.new(0, Use(Theme.Spacing["0.5"]))
							end),

							[Children] = {
								Scope:Text {
									Text = "HP",
								},
								Scope:ProgressBar {
									Progress = Progress,
									Color = Util.Colors.Red["500"],
									FlexMode = Enum.UIFlexMode.Fill,
								},
							},
						},
					},
				},
				Scope:Frame {
					ListEnabled = true,
					ListHorizontalFlex = Enum.UIFlexAlignment.Fill,
					ListPadding = Scope:Computed(function(Use)
						return UDim.new(0, Use(Theme.Spacing["0.5"]))
					end),

					[Children] = {
						Scope:Text {
							Text = "Inverted",
						},
						Scope:ProgressBar {
							Progress = Progress,
							Inverted = true,
						},
					},
				},
				Scope:Frame {
					ListEnabled = true,
					ListHorizontalFlex = Enum.UIFlexAlignment.Fill,
					ListPadding = Scope:Computed(function(Use)
						return UDim.new(0, Use(Theme.Spacing["0.5"]))
					end),

					[Children] = {
						Scope:Text {
							Text = "Vertical",
						},
						Scope:Frame {
							ListEnabled = true,
							ListFillDirection = Enum.FillDirection.Horizontal,
							ListPadding = Scope:Computed(function(Use)
								return UDim.new(0, Use(Theme.Spacing["0.5"]))
							end),

							[Children] = {

								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Inverted = true,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Inverted = true,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Inverted = true,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Inverted = true,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Color = Color,
								},
								Scope:ProgressBar {
									Direction = Enum.FillDirection.Vertical,
									Progress = Progress,
									Inverted = true,
									Color = Color,
								},
							},
						},
					},
				},
			},
		}

		return function()
			Scope:doCleanup()
		end
	end,
}

progessbar.story

when i copy the stuff under the story function and make it fully compatible with my scripting it just makes a frame without the [Children] part

this has caused me many problems i had to add Parent = ParentMainFrame, to the top of component parts so it parents to where i want it so please if you are able to make a uncopylocked place do so,

every developer will be thankful

Ah… those are stories for Hoarcekat and Flipbook. They’re for display and development purposes, not to be used as components that can receive children or arbitrary parameters.

1 Like

Yeah i use flipbook to view them, but with using them as examples to import them into my module i made this:


using the script i provided in my first reply.

as i previously said please provide a uncopylocked place

I know, but adding the components to a screengui thats the hard part.