CSS Utility Functions

A collection of reusable CSS custom native functions using the new @function syntax.

⚠️ Experimental Feature: CSS @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 code

Creates 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 */ }
Opacity: 0.5

Examples

10% opacity
30% opacity
50% opacity
80% opacity

--lighten()

Source code

Adjust 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 */ }
Original
+10% lightness
+20% lightness
+30% lightness

--darken()

Source code

Adjust 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 */ }
Original
-10% lightness
-20% lightness
-30% lightness

--saturate()

Source code

Boost 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 */ }
Original
+0.1 saturation
+0.2 saturation
+0.3 saturation

--desaturate()

Source code

Reduce 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 */ }
Original
-0.1 saturation
-0.2 saturation
-0.3 saturation

--mix()

Source code

Blend 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 */ }
Color 1
25% mix
50% mix
75% mix
Color 2

--contrast-text()

Source code

Auto-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)); }
Dark Background
White text
Light Background
Black text
Medium Background
Auto contrast
Red Background
Auto contrast

--grayscale()

Source code

Convert 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)); }
var(--primary-color)
--grayscale(var(--primary-color))

--invert()

Source code

Invert 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)); }
#ffffff
--invert(#ffffff)
#000000
--invert(#000000)
var(--primary-color)
--invert(var(--primary-color))

Interactive Demo

.element { background: --invert(#3b82f6); }
Original Color
#3b82f6
Inverted Color
--invert(#3b82f6)

--complement()

Source code

Get 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)); }
var(--primary-color)
--complement(var(--primary-color))
#10b981
--complement(#10b981)
#f59e0b
--complement(#f59e0b)

--space()

Source code

Consistent 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 */ }
--space(2)
--space(4)
--space(6)
--space(8)

--fluid()

Source code

Responsive 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 --min is applied. Default: 20ch.
  • --max-cqw (optional) — Maximum container width at which --max is 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 code

Calculate 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 */ }
16:9
16:9 Ratio
1:1
Square (1:1)
4:3
4:3 Ratio

--shadow()

Source code

Elevation 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). Use 135deg for bottom-right, 270deg for 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); }
Level 1
Subtle shadow
Level 2
Card shadow
Level 3
Elevated
Level 4
High elevation
Level 5
Maximum elevation

Interactive Demo

.card { box-shadow: --shadow(3, oklch(0% 0 0 / 0.15), 90deg); }
Interactive
Shadow
Common angles: 0° (right), 90° (down), 180° (left), 270° (up), 135° (bottom-right), 315° (top-right)

--bevel()

Source code

Inset 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). Use 270deg for top lighting, 45deg for 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); }
Level 1
Subtle inset
Level 2
Pressed button
Level 3
Input field
Level 4
Deep recess
Level 5
Maximum depth

Interactive Demo

.button { box-shadow: --bevel(3, oklch(0% 0 0 / 0.15), 90deg); }
Interactive
Bevel
Common angles: 90° (lit from bottom), 270° (lit from top), 180° (lit from left), 0° (lit from right), 315° (lit from top-right)

--diagonal-lines()

Source code

Generate 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 code

Simple 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)); }
Normal Content
Respects container padding
Full Bleed Content
margin-inline: --neg(--space(6))

--lerp()

Source code

Interpolates 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 code

Calculate 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 */ }
Green: 45°, Orange: 90°, Red: 180°, Purple: 270°

--modular()

Source code

Calculate 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)); }
Step -1: Smaller
Step 0: Base (1rem)
Step 1: 1.25rem
Step 2: 1.5625rem
Step 3: 1.953125rem
Step 4: 2.44140625rem

--poly-angle()

Source code

Calculate 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 code

Convert 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 to var(--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 */ }
16px = 1rem
18px = 1.125rem
24px = 1.5rem
32px = 2rem

--to-px()

Source code

Convert 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 */ }
1rem = 16px
2rem = 32px
5rem = 80px
10rem = 160px

--not()

Source code

Boolean 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 */ }
Input: 1
Output: 0
Input: 0
Output: 1

--and()

Source code

Logical 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)); }
1 AND 1
= 1
1 AND 0
= 0
0 AND 1
= 0
0 AND 0
= 0

--or()

Source code

Logical 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 */ }
1 OR 1
= 1
1 OR 0
= 1
0 OR 1
= 1
0 OR 0
= 0

--xor()

Source code

Exclusive 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 */ }
1 XOR 1
= 0
1 XOR 0
= 1
0 XOR 1
= 1
0 XOR 0
= 0

--nand()

Source code

NOT 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)); }
1 NAND 1
= 0
1 NAND 0
= 1
0 NAND 1
= 1
0 NAND 0
= 1

--nor()

Source code

NOT 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 */ }
1 NOR 1
= 0
1 NOR 0
= 0
0 NOR 1
= 0
0 NOR 0
= 1

--xnor()

Source code

Exclusive 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 */ }
1 XNOR 1
= 1
1 XNOR 0
= 0
0 XNOR 1
= 0
0 XNOR 0
= 1