Skip to content
This repository has been archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
cxmeel committed Mar 27, 2021
1 parent 101e740 commit 4a739ea
Show file tree
Hide file tree
Showing 29 changed files with 670 additions and 2 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: ClockworkSquirrel
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
*.rbxlx.lock
*.rbxl.lock

*.rbxlx
*.rbxl

*.rbxmx
*.rbxm

roblox.toml
/dist
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "packages/TestEZ"]
path = packages/TestEZ
url = https://github.com/Roblox/testez/
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "TestEZ",
"type": "PowerShell",
"request": "launch",
"script": "${workspaceFolder}/test/test.ps1",
"cwd": "${workspaceFolder}"
}
]
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rojo.releaseBranch": "7.x"
}
127 changes: 125 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,125 @@
# colour-utils
Colour utility module for Roblox
# ColourUtils
ColourUtils provides handy methods for manipulating colours for your UI in Roblox.

## Installation
* [Grab a copy from the Library (Toolbox)](https://www.roblox.com/library/6573728888)
* [Sync in with Rojo](https://rojo.space)
* [Download the model from GitHub releases](/releases/latest)

## Usage
Here's a quick example of how ColourUtils can be used:

```lua
local ColourUtils = require(path.to.ColourUtils)
local TextLabel = path.to.awesome.TextLabel

local Background = ColourUtils.Hex.fromHex("#00A2FF")
local Foreground = ColourUtils.Emphasise(Background, 0.9)

TextLabel.BackgroundColor3 = Background
TextLabel.TextColor3 = Foreground
```

Here's how you might use ColourUtils to generate an "accessible" text colour, [as recommended by the W3C accessibility guidelines](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast-contrast):
```lua
local ColourUtils = require(game.ServerScriptService.ColourUtils)
local TextLabel = game.StarterGui.ScreenGui.TextLabel

local function GetAccessibleTextColour(background: Color3): Color3
local darkThemeText = Color3.new(1, 1, 1)
local lightThemeText = ColourUtils.Lighten(Color3.new(), .13)

local contrastRatio = ColourUtils.GetContrastRatio(background, darkThemeText)

return contrastRatio >= 3 and darkThemeText or lightThemeText
end

TextLabel.BackgroundColor3 = ColourUtils.Hex.fromHex("#00a2ff")
TextLabel.TextColor3 = GetAccessibleTextColour(TextLabel.BackgroundColor3)
```

# Documentation
## API Overview
* `ColourUtils.Darken(colour: Color3, coefficient: number): Color3`
* `ColourUtils.Lighten(colour: Color3, coefficient: number): Color3`
* `ColourUtils.Emphasise(colour: Color3, coefficient: number): Color3`
* `ColourUtils.GetContrastRatio(foreground: Color3, background: Color3): number`
* `ColourUtils.GetLuminance(colour: Color3): number`
* `ColourUtils.Hex.fromHex(hex: string): Color3`
* `ColourUtils.Hex.toHex(colour: Color3): string`
* `ColourUtils.Int.fromInt(int: number): Color3`
* `ColourUtils.Int.toInt(colour: Color3): number`

## API Methods
### Darken
Darkens a colour.
#### Arguments
colour (`Color3`) - The `Color3` to darken\
coefficient (`number`) - A multiplier in the range of 0-1
#### Returns
colour `Color3` - The darkened `Color3`

### Lighten
Lightens a colour.
#### Arguments
colour (`Color3`) - The `Color3` to lighten\
coefficient (`number`) - A multiplier in the range of 0-1
#### Returns
colour `Color3` - The lightened `Color3`

### Emphasise
Automatically darken a light colour or lighten a dark colour, depending on it luminance.
#### Arguments
colour (`Color3`) - The `Color3` to affect\
coefficient (`number`) - A multiplier in the range of 0-1
#### Returns
colour `Color3` - The lightened or darkened `Color3`

### GetLuminance
Get the relative brightness of a given `Color3`, using the [formula provided by WCAG](https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests).
#### Arguments
colour (`Color3`) - The `Color3` to get luminance of
#### Returns
luminance (`number`) - The relative luminance of the given `Color3`, in the range of 0-1

### GetContrastRatio
Calculates the contrast ratio between two colours, using the [formula provided by WCAG](https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests).
#### Arguments
background (`Color3`) - A `Color3` value representing the background\
foreground (`Color3`) - A `Color3` value representing the foreground
#### Returns
ratio (`number`) - The contrast ratio of two two given colours, in the range of 0-21.

## Hex
Hex is a submodule which enables conversion between Hex colour strings (e.g. `#8697F6`) and `Color3` values.

### fromHex
Converts a hex string into a `Color3`. This method accepts hex strings of any length (but will only respect the first 6 characters); with or without a preceding hash (`#`).
#### Arguments
hex (`string`) - A hex colour string
#### Returns
colour (`Color3`) - The `Color3` representation of the given hex colour

### toHex
Converts a `Color3` into a hex value. Note that this method does not prepend a hash (`#`) to the beginning of the string.
#### Arguments
colour (`Color3`) - A `Color3` to convert to hex
#### Returns
hex (`string`) - The resulting hex code of the given `Color3`

## Int
Like the Hex submodule, Int allows for conversion between integers (e.g. `8820726` or `0x8697F6`) and `Color3` values.

### fromInt
Converts an integer into a `Color3`.
#### Arguments
integer (`number`) - An integer representing a colour
#### Returns
colour (`Color3`) - The `Color3` representation of the given hex colour

### toInt
Converts a `Color3` into an integer value.
#### Arguments
colour (`Color3`) - A `Color3` to convert to an integer
#### Returns
int (`number`) - The resulting integer of the given `Color3`
6 changes: 6 additions & 0 deletions default.project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "ColourUtils",
"tree": {
"$path": "src"
}
}
1 change: 1 addition & 0 deletions packages/TestEZ
Submodule TestEZ added at 25d957
1 change: 1 addition & 0 deletions selene.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std = "roblox+testez"
20 changes: 20 additions & 0 deletions src/Darken.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
local clamp = math.clamp

local fmt = string.format

local ERR_INVALID_TYPE = "darken(...): The `%s` argument must be a %s, but you passed %q (%s)"

local function clampColour(colour: Color3)
local red = clamp(colour.R, 0, 1)
local green = clamp(colour.G, 0, 1)
local blue = clamp(colour.B, 0, 1)

return Color3.new(red, green, blue)
end

return function(colour: Color3, coefficient: number): Color3
assert(typeof(colour) == "Color3", fmt(ERR_INVALID_TYPE, "colour", "Color3", tostring(colour), typeof(colour)))
assert(type(coefficient) == "number", fmt(ERR_INVALID_TYPE, "coefficient", "number", tostring(coefficient), typeof(coefficient)))

return clampColour(colour:Lerp(Color3.new(), coefficient))
end
43 changes: 43 additions & 0 deletions src/Darken.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
return function()
local Darken = require(script.Parent.Darken)

it("throws if argument is not a Color3", function()
expect(pcall(Darken, true)).to.equal(false)
end)

it("doesn't modify black", function()
expect(Darken(Color3.new(), .1)).to.equal(Color3.new())
end)

it("doesn't overshoot if an above-range coefficient is supplied", function()
expect(Darken(Color3.new(0, .5, 1), 1.5)).to.equal(Color3.new())
end)

it("doesn't overshoot if a below-range coefficient is supplied", function()
expect(Darken(Color3.new(0, .5, 1), -1.5)).to.equal(Color3.new(0, 1, 1))
end)

it("darkens white by 10% when coefficient is 0.1", function()
local colour = Darken(Color3.new(1, 1, 1), .1)

expect(colour.R).to.be.near(.9, .01)
expect(colour.G).to.be.near(.9, .01)
expect(colour.B).to.be.near(.9, .01)
end)

it("darkens red by 50% when coefficient is 0.5", function()
local colour = Darken(Color3.new(1, 0, 0), .5)

expect(colour.R).to.be.near(.5, .01)
expect(colour.G).to.equal(0)
expect(colour.B).to.equal(0)
end)

it("darkens grey by 50% when coefficient is 0.5", function()
expect(Darken(Color3.new(.5, .5, .5), .5)).to.equal(Color3.new(.25, .25, .25))
end)

it("doesn't modify colours when coefficient is 0", function()
expect(Darken(Color3.new(1, 1, 1), 0)).to.equal(Color3.new(1, 1, 1))
end)
end
7 changes: 7 additions & 0 deletions src/Emphasise.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
local GetLuminance = require(script.Parent.GetLuminance)
local Lighten = require(script.Parent.Lighten)
local Darken = require(script.Parent.Darken)

return function(colour: Color3, coefficient: number): Color3
return GetLuminance(colour) > .5 and Darken(colour, coefficient) or Lighten(colour, coefficient)
end
15 changes: 15 additions & 0 deletions src/Emphasise.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
return function()
local Emphasise = require(script.Parent.Emphasise)
local Lighten = require(script.Parent.Lighten)
local Darken = require(script.Parent.Darken)

it("lightens a dark colour with the coefficient provided", function()
local colour = Color3.fromRGB(1, 2, 3)
expect(Emphasise(colour, .4)).to.equal(Lighten(colour, .4))
end)

it("darkens a light colour with the coefficient provided", function()
local colour = Color3.fromRGB(250, 240, 230)
expect(Emphasise(colour, .3)).to.equal(Darken(colour, .3))
end)
end
18 changes: 18 additions & 0 deletions src/GetContrastRatio.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
local GetLuminance = require(script.Parent.GetLuminance)

local max = math.max
local min = math.min

local fmt = string.format

local ERR_INVALID_TYPE = "getContrastRatio(...): The `%s` argument must be a Color3, but you passed %q (%s)"

return function(foreground: Color3, background: Color3): number
assert(typeof(foreground) == "Color3", fmt(ERR_INVALID_TYPE, "foreground", tostring(foreground), typeof(foreground)))
assert(typeof(background) == "Color3", fmt(ERR_INVALID_TYPE, "background", tostring(background), typeof(background)))

local lumA = GetLuminance(foreground)
local lumB = GetLuminance(background)

return (max(lumA, lumB) + .05) / (min(lumA, lumB) + .05)
end
39 changes: 39 additions & 0 deletions src/GetContrastRatio.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
return function()
local GetContrastRatio = require(script.Parent.GetContrastRatio)

it("throws if arguments are not Color3s", function()
expect(pcall(GetContrastRatio, Color3.new(), true)).to.equal(false)
expect(pcall(GetContrastRatio, true, Color3.new())).to.equal(false)
expect(pcall(GetContrastRatio, 100, true)).to.equal(false)
end)

it("returns a number between 0-21", function()
local result = GetContrastRatio(Color3.new(), Color3.new(1, 1, 1))
expect(result >= 0 and result <= 21).to.be.equal(true)
end)

it("returns a ratio for black:white", function()
local result = GetContrastRatio(Color3.new(), Color3.new(1, 1, 1))
expect(result).to.be.equal(21)
end)

it("returns a ratio for black:black", function()
local result = GetContrastRatio(Color3.new(), Color3.new())
expect(result).to.be.equal(1)
end)

it("returns a ratio for white:white", function()
local result = GetContrastRatio(Color3.new(1, 1, 1), Color3.new(1, 1, 1))
expect(result).to.be.equal(1)
end)

it("returns a ratio for dark grey:light grey", function()
local result = GetContrastRatio(Color3.fromRGB(112, 112, 112), Color3.fromRGB(230, 230, 230))
expect(result).to.be.near(3.96, .01)
end)

it("returns a ratio for black:light grey", function()
local result = GetContrastRatio(Color3.new(), Color3.fromRGB(136, 136, 136))
expect(result).to.be.near(5.92, .01)
end)
end
19 changes: 19 additions & 0 deletions src/GetLuminance.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
local fmt = string.format

local ERR_INVALID_TYPE = "getLuminance(...): The `%s` argument must be a %s, but you passed %q (%s)"

local function transformValue(value: number): number
return value <= .03928 and value / 12.92 or ((value + .055) / 1.055) ^ 2.4
end

return function(colour: Color3): number
assert(typeof(colour) == "Color3", fmt(ERR_INVALID_TYPE, "colour", "Color3", tostring(colour), typeof(colour)))

local red = transformValue(colour.R)
local green = transformValue(colour.G)
local blue = transformValue(colour.B)

local lum = fmt("%.3f", .2126 * red + .7152 * green + .0722 * blue)

return tonumber(lum)
end
19 changes: 19 additions & 0 deletions src/GetLuminance.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
return function()
local GetLuminance = require(script.Parent.GetLuminance)

it("throws if argument is not a Color3", function()
expect(pcall(GetLuminance, 100)).to.equal(false)
end)

it("returns a valid luminance for black", function()
expect(GetLuminance(Color3.new())).to.equal(0)
end)

it("returns a valid luminance for white", function()
expect(GetLuminance(Color3.new(1, 1, 1))).to.equal(1)
end)

it("returns a valid luminance for mid grey", function()
expect(GetLuminance(Color3.fromRGB(127, 127, 127))).to.equal(.212)
end)
end
Loading

0 comments on commit 4a739ea

Please sign in to comment.