CSS Utility Functions
A collection of reusable CSS custom native functions using the new @function syntax.
@function is currently experimental. Check browser compatibility before using in production.
These utility functions provide powerful, reusable CSS capabilities that work seamlessly with CSS custom properties. Each function is designed to solve common styling challenges while maintaining type safety and performance.
Features
- ✨ Type-safe: Functions use CSS type system for validation
- 🚀 Performant: Compiled at build time, zero runtime overhead
- 🎨 Modern: Built with OKLCH color space for better color manipulation
- 📦 Modular: Import only what you need
- 🔧 Customizable: Works with CSS custom properties
Installation
npm install css-utility-functions
Usage
Import the bundled version (recommended):
@import 'css-utility-functions';
Or import individual functions:
@import 'css-utility-functions/src/colors/alpha.css';
@import 'css-utility-functions/src/spacing/space.css';
/* Import only what you need */
CDN Usage
Use via unpkg.com CDN with a <link> tag:
<link rel="stylesheet" href="https://unpkg.com/css-utility-functions/dist/index.css">
Or use the minified version:
<link rel="stylesheet" href="https://unpkg.com/css-utility-functions/dist/index.min.css">
You can also import via CSS @import:
@import url('https://unpkg.com/css-utility-functions/dist/index.css');
For more details, examples, and to contribute, visit the GitHub repository .
--alpha()
Source codeCreates a semi-transparent version of any color. Works exactly like CSS opacity property: 0 = fully transparent (invisible), 1 = fully opaque (solid), 0.5 = 50% opaque.
Parameters
-
--color(required) — The base color. -
--opacity(optional) — Opacity level (0-1). Higher values = more opaque, lower values = more transparent. Default:0.5.
Basic Usage
.overlay {
background: --alpha(var(--primary-color), 0.2); /* 20% opaque */
}
.backdrop {
background: --alpha(var(--brand), 0.85); /* 85% opaque */
}
Examples
--lighten()
Source codeAdjust a color's lightness. Use decimal values (0.1 = 10%, 0.2 = 20%). Increases the lightness component of the color.
Parameters
-
--color(required) — The base color. -
--amount(optional) — Amount to lighten (0-1, e.g., 0.1 = 10%, 0.2 = 20%). Default:0.1.
Basic Usage
.btn:hover {
background: --lighten(var(--btn-bg), 0.15); /* 15% lighter */
}
.card {
border-color: --lighten(var(--primary-color), 0.2); /* 20% lighter */
}
--darken()
Source codeAdjust a color's lightness downward. Use decimal values (0.1 = 10%, 0.2 = 20%). Decreases the lightness component of the color.
Parameters
-
--color(required) — The base color. -
--amount(optional) — Amount to darken (0-1, e.g., 0.1 = 10%, 0.2 = 20%). Default:0.1.
Basic Usage
.btn:active {
background: --darken(var(--btn-bg), 0.1); /* 10% darker */
}
.shadow {
color: --darken(var(--primary-color), 0.3); /* 30% darker */
}
--saturate()
Source codeBoost a color's saturation/chroma. Use decimal values (0.4 = 40%, 1.0 = 100%). Increases the chroma component of the color.
Parameters
-
--color(required) — The base color. -
--amount(optional) — Percentage as decimal (e.g., 0.4 = 40%). Default:0.2.
Basic Usage
.vibrant {
background: --saturate(var(--primary), 0.4); /* 40% more saturated */
}
.accent {
color: --saturate(var(--primary-color), 0.5); /* 50% more saturated */
}
--desaturate()
Source codeReduce a color's saturation/chroma. Use decimal values (0.3 = 30%, 0.5 = 50%). Decreases the chroma component of the color, making it more gray.
Parameters
-
--color(required) — The base color. -
--amount(optional) — Percentage as decimal (e.g., 0.5 = 50%). Default:0.2.
Basic Usage
.muted {
background: --desaturate(var(--primary), 0.3); /* 30% less saturated */
}
.disabled {
color: --desaturate(var(--primary-color), 0.5); /* 50% less saturated */
}
--mix()
Source codeBlend two colors by a given percentage. The weight parameter determines how much of the first color to use (50% = equal mix).
Parameters
-
--color1(required) — First color. -
--color2(required) — Second color. -
--weight(optional) — Weight of first color. Default:50%.
Basic Usage
.gradient-mid {
background: --mix(var(--start), var(--end), 50%);
}
.tinted {
background: --mix(white, var(--brand), 90%); /* 90% white, 10% brand */
}
--contrast-text()
Source codeAuto-select black or white text depending on background luminance for optimal readability. Uses OKLCH lightness to determine the best contrast.
Parameters
-
--bg(required) — Background color.
Basic Usage
.badge {
background: var(--badge-color);
color: --contrast-text(var(--badge-color)); /* Auto black or white */
}
.card {
background: --alpha(var(--primary), 0.1);
color: --contrast-text(--alpha(var(--primary), 0.1));
}
--grayscale()
Source codeConvert any color to grayscale by removing chroma (saturation). Uses OKLCH color space, setting chroma to 0 while preserving lightness.
Parameters
-
--color(required) — The base color.
Basic Usage
.monochrome {
background: --grayscale(var(--primary-color)); // Blue becomes gray
}
.desaturated-image {
filter: --grayscale(var(--image-color));
}
--invert()
Source codeInvert a color by flipping its lightness in OKLCH space. Dark colors become light, light colors become dark.
Parameters
-
--color(required) — The base color.
Basic Usage
.inverted {
background: --invert(#ffffff); // White becomes black
}
.dark-mode-text {
color: --invert(var(--light-text));
}
Interactive Demo
.element {
background: --invert(#3b82f6);
}
--complement()
Source codeGet the complementary color (opposite on color wheel) by rotating hue 180 degrees. Uses OKLCH color space for accurate hue rotation.
Parameters
-
--color(required) — The base color.
Note: Achromatic colors (grays) have no hue, so their complement is unchanged.
Basic Usage
.accent {
background: --complement(var(--primary-color)); // Blue's complement (orange)
}
.contrast-border {
border-color: --complement(var(--primary));
}
--space()
Source codeConsistent spacing scale based on a multiplier. The base unit can be customized via the --space-base CSS custom property (default: 0.25rem).
Parameters
-
--multiplier(required) — Scale multiplier. -
--base(optional) — Base spacing unit. Default:var(--space-base, 0.25rem).
Basic Usage
.card {
padding: --space(4); /* 1rem (4 × 0.25rem) */
margin-bottom: --space(6); /* 1.5rem */
gap: --space(2); /* 0.5rem */
}
.special {
padding: --space(4, 0.5rem); /* Custom base */
}
--fluid()
Source codeResponsive sizing that scales smoothly between viewport widths. Generates a clamp() value that scales between min and max sizes based on viewport width (default: 375px to 1440px).
Parameters
-
--min(required) — Minimum size. -
--max(required) — Maximum size. -
--min-vw(optional) — Minimum viewport width. Default:375px. -
--max-vw(optional) — Maximum viewport width. Default:1440px.
Basic Usage
h1 {
font-size: --fluid(1.5rem, 4rem);
}
.container {
padding: --fluid(1rem, 3rem, 320px, 1200px);
}
Resize your browser to see fluid sizing
This text scales smoothly between viewport sizes
--fluid-container()
Source code
Container-responsive sizing that scales based on container width rather than viewport. Requires the parent element to have container-type: inline-size. Perfect for component-level responsive design.
Parameters
-
--min(required) — Minimum size value. The size will never go below this value. -
--max(required) — Maximum size value. The size will never exceed this value. -
--min-cqw(optional) — Minimum container width at which--minis applied. Default:20ch. -
--max-cqw(optional) — Maximum container width at which--maxis applied. Default:65ch.
The function generates a clamp() value that scales smoothly between --min and --max based on the container's width, using container query units (cqw).
Basic Usage
.card {
container-type: inline-size;
}
.card h2 {
font-size: --fluid-container(1rem, 2rem);
}
.card p {
font-size: --fluid-container(0.875rem, 1.125rem, 30ch, 60ch);
}
Container-Responsive Heading
Resize this container to see the text scale based on container width, not viewport.
--ratio-height()
Source codeCalculate height from width and aspect ratio. Useful for maintaining aspect ratios in images, videos, or containers.
Parameters
-
--width(required) — Element width. -
--ratio-w(optional) — Ratio width part. Default:16. -
--ratio-h(optional) — Ratio height part. Default:9.
Basic Usage
.video-thumb {
width: 320px;
height: --ratio-height(320px, 16, 9); /* 180px */
}
.square-avatar {
width: 48px;
height: --ratio-height(48px, 1, 1); /* 48px */
}
--shadow()
Source codeElevation shadows based on level (1-5). Generates layered box-shadows for depth and elevation. Higher levels create more pronounced shadows.
Parameters
-
--level(required) — Elevation level (1-5). -
--color(optional) — Shadow color. Default:oklch(0% 0 0 / 0.1). -
--angle(optional) — Shadow direction angle. Default:90deg(downward). Use135degfor bottom-right,270degfor upward.
Basic Usage
.card {
box-shadow: --shadow(2);
}
.modal {
box-shadow: --shadow(5, oklch(0% 0 0 / 0.25));
}
.lit-from-top-right {
box-shadow: --shadow(3, oklch(0% 0 0 / 0.15), 135deg);
}
.lit-from-left {
box-shadow: --shadow(2, oklch(0% 0 0 / 0.1), 180deg);
}
Interactive Demo
.card {
box-shadow: --shadow(3, oklch(0% 0 0 / 0.15), 90deg);
}
Shadow
--bevel()
Source codeInset elevation shadows for embossed/debossed effects. Creates recessed appearance for pressed buttons, input fields, and sunken UI elements. Higher levels create more pronounced inset shadows.
Parameters
-
--level(required) — Elevation level (1-5). -
--color(optional) — Shadow color. Default:oklch(0% 0 0 / 0.1). -
--angle(optional) — Light source direction angle. Default:90deg(lit from bottom). Use270degfor top lighting,45degfor top-right lighting.
Basic Usage
.pressed-button {
box-shadow: --bevel(2);
}
.input-field {
box-shadow: --bevel(1, oklch(0% 0 0 / 0.08));
}
.sunken-panel {
box-shadow: --bevel(3, oklch(0% 0 0 / 0.15));
}
.lit-from-top {
box-shadow: --bevel(2, oklch(0% 0 0 / 0.1), 270deg);
}
Interactive Demo
.button {
box-shadow: --bevel(3, oklch(0% 0 0 / 0.15), 90deg);
}
Bevel
--diagonal-lines()
Source codeGenerate a repeating diagonal line pattern with configurable angle, colors, and spacing. Returns a complete repeating-linear-gradient that can be used directly as a background value.
Parameters
-
--angle(optional) — Angle of the lines. Default:-45deg. -
--line-color(optional) — Color of the lines. Default:currentColor. -
--line-width(optional) — Width of each line. Default:1px. -
--gap-width(optional) — Width of the gap between lines. Default:4px. -
--gap-color(optional) — Color of the gap (background). Default:transparent.
Interactive Demo
.bg-diagonal {
background: --diagonal-lines(
--angle: -45deg,
--line-color: #3b82f6,
--line-width: 1px,
--gap-width: 4px,
--gap-color: transparent
);
}
--neg()
Source codeSimple negation — useful in calc-heavy layouts. Negates any length value, making it negative.
Parameters
-
--value(required) — Value to negate.
Bleed Effect
Extend content beyond container padding using negative margins.
.full-bleed {
margin-inline: --neg(var(--container-padding));
}
--lerp()
Source codeInterpolates between two values using a t parameter (0-1). When t=0, returns the first value; when t=1, returns the second value; when t=0.5, returns the midpoint. Hides the complex calc expression.
Parameters
-
--a(required) — Start value. -
--b(required) — End value. -
--t(optional) — Interpolation factor (0-1). Default:0.5.
Interactive Demo
width: --lerp(10%, 80%, var(--t));
width: --lerp(50px, 400px, var(--t));
--circle-x() / --circle-y()
Source codeCalculate X and Y coordinates on a circle using radius and angle. Uses cosine for X and sine for Y positions.
Parameters
-
--radius(required) — Circle radius. -
--angle(optional) — Angle in degrees. Default:0deg.
Both --circle-x() and --circle-y() use the same parameters. --circle-x() uses cosine, --circle-y() uses sine.
Basic Usage
.orbit {
left: calc(50% + --circle-x(100px, 45deg)); /* 45° X position */
top: calc(50% + --circle-y(100px, 45deg)); /* 45° Y position */
}
--modular()
Source codeCalculate values using a modular scale (typographic ratio). Hides the pow calculation for creating consistent type scales.
Parameters
-
--step(required) — Scale step (can be negative for smaller values). -
--base(optional) — Base value. Default:1rem. -
--ratio(optional) — Scale ratio. Default:1.25(major third).
Basic Usage
:root {
--type-base: 1rem;
--type-ratio: 1.25;
}
h1 {
font-size: --modular(4, var(--type-base), var(--type-ratio));
}
small {
font-size: --modular(-1, var(--type-base), var(--type-ratio));
}
--poly-angle()
Source codeCalculate the angle for a vertex in a regular polygon. Useful for creating polygon shapes with CSS transforms.
Parameters
-
--sides(required) — Number of sides in polygon. -
--index(optional) — Vertex index (0-based). Default:0.
Formula: (360deg / sides × index) - 90deg
Interactive Demo
transform: rotate(--poly-angle(6, 0));
--to-rem()
Source codeConvert a pixel value to rem units. Base pixel size defaults to --rem-base (or 16px). Useful for converting legacy pixel-based designs to rem-based responsive layouts.
Parameters
-
--px(required) — Pixel value (unitless). -
--base(optional) — Base pixel size for conversion. Defaults tovar(--rem-base, 16).
Basic Usage
.legacy-compat {
font-size: --to-rem(18); /* 1.125rem */
padding: --to-rem(24); /* 1.5rem */
}
.custom-base {
font-size: --to-rem(18, 10); /* Custom base */
}
--to-px()
Source codeConvert a rem value to pixels (assumes 16px base). Useful when you need pixel values for legacy systems or specific calculations.
Parameters
-
--rem(required) — REM value (unitless).
Basic Usage
.legacy-system {
width: --to-px(10); /* 160px */
height: --to-px(5); /* 80px */
}
--not()
Source codeBoolean negation. Returns 0 if input is 1, and 1 if input is 0. Useful for toggling CSS properties based on state variables.
Parameters
-
--value(required) — Switch variable (0 or 1).
Basic Usage
.toggle {
--is-hidden: 0;
opacity: --not(var(--is-hidden)); /* 1 = visible */
}
.toggle.hidden {
--is-hidden: 1;
/* opacity becomes 0 = invisible */
}
--and()
Source codeLogical AND operation. Returns 1 only if both operands are 1, otherwise returns 0. Useful for conditional styling based on multiple state variables.
Parameters
-
--a(required) — First switch variable (0 or 1). -
--b(required) — Second switch variable (0 or 1).
Basic Usage
.button {
--is-hovered: 1;
--is-enabled: 1;
opacity: --and(var(--is-hovered), var(--is-enabled));
}
--or()
Source codeLogical OR operation. Returns 1 if at least one operand is 1, otherwise returns 0. Useful for fallback styling when any condition is met.
Parameters
-
--a(required) — First switch variable (0 or 1). -
--b(required) — Second switch variable (0 or 1).
Basic Usage
.alert {
--is-error: 0;
--is-warning: 1;
display: block;
opacity: --or(var(--is-error), var(--is-warning)); /* 1 */
}
--xor()
Source codeExclusive OR operation. Returns 1 if exactly one operand is 1 (they differ), otherwise returns 0. Useful for detecting state changes or differences.
Parameters
-
--a(required) — First switch variable (0 or 1). -
--b(required) — Second switch variable (0 or 1).
Basic Usage
.highlight-when-different {
--user-theme: 1;
--system-theme: 0;
opacity: --xor(var(--user-theme), var(--system-theme)); /* 1 */
}
--nand()
Source codeNOT AND operation. Returns 0 only if both operands are 1, otherwise returns 1. The inverse of AND operation.
Parameters
-
--a(required) — First switch variable (0 or 1). -
--b(required) — Second switch variable (0 or 1).
Basic Usage
.disabled-state {
--is-loading: 0;
--is-error: 0;
pointer-events: --nand(var(--is-loading), var(--is-error));
}
--nor()
Source codeNOT OR operation. Returns 1 only if both operands are 0, otherwise returns 0. The inverse of OR operation.
Parameters
-
--a(required) — First switch variable (0 or 1). -
--b(required) — Second switch variable (0 or 1).
Basic Usage
.hidden-state {
--has-error: 0;
--has-warning: 0;
display: none;
opacity: --nor(var(--has-error), var(--has-warning)); /* 1 = hidden */
}
--xnor()
Source codeExclusive NOR operation. Returns 1 if both operands are the same (both 0 or both 1), otherwise returns 0. The inverse of XOR operation.
Parameters
-
--a(required) — First switch variable (0 or 1). -
--b(required) — Second switch variable (0 or 1).
Basic Usage
.match-state {
--value-a: 1;
--value-b: 1;
opacity: --xnor(var(--value-a), var(--value-b)); /* 1 = match */
}