๐ŸŽจ Color Utilities

April 21, 2026 ยท View on GitHub

Manipulation Functions

Neopywal offers a neat set of utility functions that allows the user to manipulate Neopywal's colors in case you want, for example, a darker color than color1. Or a brighter and more saturated version of the color color3.
These functions are all present under utils/color.lua module and can be required like so:

local U = require("neopywal.utils.color")

U.darken(color, factor)
U.lighten(color, factor)
U.blend(color1, color2, percentage)
U.brighten(color, percentage)
U.blacken(color, percentage)
U.saturate(color, percentage)
U.invert(color)

Important

All color parameters for the functions have to be in hexadecimal format (e.g.: "#000000").

Darken and Lighten

As the name suggests, the darken() and lighten() functions are able to create new colors by darkening/lightening existing colors.
Both functions take two parameters, the first one is the hexadecimal code of color you want to modify, the second is an integer from 0 to 100 that defines how much said color will be darken/lighten.

local C = require("neopywal").get_colors() -- Using catppuccin-mocha theme.
local U = require("neopywal.utils.color")

print(C.color1) -- For comparison: #F38BA8"
print(U.darken(C.color1, 70)) -- Output: "#AD4562"
print(U.lighten(C.color1, 70)) -- Output: "#FFD1EE"

Here's some visual interpretation for the colors.

Normal vs Darken/Lighten Hex Color
U.darken(C.color1, 70)
Hex: "#AD4562"
C.color1
Hex: "#F38BA8"
U.lighten(C.color1, 70)
Hex: "#FFD1EE"

It is important to know that these functions do NOT modify the color based on percentage values. Instead they work in a more linear way by converting the hexadecimal color to it's numeric RGB values and then subtracting/adding the value present on the factor argument given to them.

Blend

The blend() function combines two colors to create a new color that is a mixture of the two colors specified.
The function takes three parameters, the first two parameters define the colors to be blended. While the third percentage parameter is a number between 0 and 1 that determines the proportion of each color in the final output. Where 1 means "use the exact same color as the first parameter" and 0 means "use the exact same color as the second parameter".

local C = require("neopywal").get_colors() -- Using catppuccin-mocha theme.
local U = require("neopywal.utils.color")

-- In this example:

-- `U.blend(C.color1, C.color3, 0)` will be identical to `C.color3`.
-- `U.blend(C.color1, C.color3, 1)` will be identical to `C.color1`.
-- `U.blend(C.color1, C.color3, 0.5)` will be a 50/50 mix of `C.color1` and `C.color3`.

print(C.color1) -- For comparison: #F38BA8"
print(C.color3) -- For comparison: "#F9E2AF"
print(U.blend(C.color1, C.color3, 0)) -- Output: "#F9E2AF"
print(U.blend(C.color1, C.color3, 1)) -- Output: "#F38BA8"
print(U.blend(C.color1, C.color3, 0.5)) -- Output: "#F6B7AC"

Here's some visual interpretation for the colors.

Normal vs Blend Hex Color
C.color1
U.blend(C.color1, C.color3, 1)
Hex: "#F38BA8"
C.color3
U.blend(C.color1, C.color3, 0)
Hex: "#F9E2AF"
U.blend(C.color1, C.color3, 0.5)
Hex: "#F6B7AC"

Blacken and Brighten

The blacken() and the brighten() functions are similar to the darken() and lighten() functions. However they work in completely different ways.
While darken()/lighten() first convert the input hexadecimal color as a RGB number before manipulating it. blacken()/brighten() instead take the hexadecimal color and convert it to the HSL color space, were it will be manipulated by increasing/decreasing the "L" lightness value of the color based on a percentage.
Both functions take two parameters, the first one is the hexadecimal code of color you want to modify, and the second is an number from 0 to 1 that defines how much said color will be brightened/blackened out.

local C = require("neopywal").get_colors() -- Using catppuccin-mocha theme.
local U = require("neopywal.utils.color")

print(C.color1) -- For comparison: #F38BA8"

print(U.darken(C.color1, 70)) -- For comparison: "#AD4562"
print(U.blacken(C.color1, 0.7)) -- Output: "#621130"
print(U.darken(C.color1, 100)) -- For comparison: "#8F2744"
print(U.blacken(C.color1, 1)) -- Output: "#000000"

print(U.lighten(C.color1, 70)) -- For comparison: "#FFD1EE"
print(U.brighten(C.color1, 0.7)) -- Output: "#FBDEE4"
print(U.lighten(C.color1, 100)) -- For comparison: "#FFEFFF"
print(U.brighten(C.color1, 1)) -- Output: "#FFFFFF"

If you want to see the difference in the outputs of darken()/lighten() vs blacken()/brighten(), here's an table that compares the output of all functions using the catppuccin-mocha theme as the main Neopywal source palette.

Darken vs Blacken Hex Color
C.color1
Hex: "#F38BA8"
U.darken(C.color1, 70)
Hex: "#AD4562"
U.blacken(C.color1, 0.7)
Hex: "#621130"
U.darken(C.color1, 100)
Hex: "#8F2744"
-- Outputs pure black.
U.blacken(C.color1, 1)
Hex: "#000000"
Lighten vs Brighten Hex Color
C.color1
Hex: "#F38BA8"
U.lighten(C.color1, 70)
Hex: "#FFD1EE"
U.brighten(C.color1, 0.7)
Hex: "#FBDEE4"
U.lighten(C.color1, 100)
Hex: "#FFEFFF"
-- Outputs pure white.
U.brighten(C.color1, 1)
Hex: "#FFFFFF"

Saturate

The saturate() function increases/decreases the saturation of a given color.
It take two parameters, the first one is the hexadecimal code of color you want to modify, and the second is an number between -1 and 1 that defines how much said color will be saturated/desaturated.

local C = require("neopywal").get_colors() -- Using catppuccin-mocha theme.
local U = require("neopywal.utils.color")

print(C.color4) -- For comparison: "#89B4FA"
print(U.saturate(C.color4, 0.7)) -- Output: "#FA4B8D"
print(U.saturate(C.color4, -0.7)) -- Output: "#A1ADC6"

Here's some visual interpretation for the colors.

Normal vs Saturated Hex Color
C.color4
Hex: "#89B4FA"
U.saturate(C.color4, 0.7)
Hex: "#FA4B8D"
U.saturate(C.color4, -0.7)
hex: "#A1ADC6"

Invert

The invert() simply takes an hexadecimal color code and, well ..., inverts it's color.

local C = require("neopywal").get_colors() -- Using catppuccin-mocha theme.
local U = require("neopywal.utils.color")

print(C.background) -- For comparison: "#1E1E2E"
print(U.invert(C.background)) -- Output: "#DCDCD4"

Here's some visual interpretation for the colors.

Normal vs Invert Hex Color
C.background
Hex: "#1E1E2E"
U.invert(C.background)
Hex: "#DCDCD4"