Utilities
Maple utility classes are the building blocks of your UI. They follow a flexible property-value pair syntax that resolves through a variable-first architecture.
Utility Format
Utilities are composed of a property and a value, separated by either a hyphen or an equal sign:
property-valueproperty=valueSemantic Fallback (-)
bgc-primary). Literal String (=)
bgc=#010101). Property and Value
property: The CSS property to apply. All CSS properties are supported in camelCase format. Some properties have shorthand versions. See the Abbreviations reference for the full list.value: The value to apply to the property. With equal sign, the value is treated as a literal string. With hyphen, the value is resolved to semantic fallback chain of CSS variables. The type of the property determines how the value is resolved. There are four types of properties:variable,color,number, andcustom.
CSS Variables
Maple allows you to define CSS variables directly in class names. This allows you to create scoped themes and dynamic styles entirely within your HTML:
<!-- Simple variable -->
<div class="--primary=blue"></div>
<!-- Complex values -->
<div class="--gradient=linear-gradient(to_right,red,blue)"></div>
<!-- Using other variables -->
<div class="--accent=var(--primary)"></div>Deep Dive: Variable Utilities
Color Resolution
Colors in Maple are high-dimensional. When you use a color utility (like c-primary or bgc-accent), it resolves into a sophisticated OKLCH color space manipulation:
.c-primary {
color: oklch(
from var(--c-primary, var(--color-primary, var(--primary, primary)))
calc(l * var(--c-primary-l-scale, var(--primary-l-scale, var(--c-l-scale, var(--l-scale, 1)))))
calc(c * var(--c-primary-c-scale, var(--primary-c-scale, var(--c-c-scale, var(--c-scale, 1)))))
calc(h + var(--c-primary-h-rotate, var(--primary-h-rotate, var(--c-h-rotate, var(--h-rotate, 0))))) /
alpha
);
}This architecture allows you to create a complete design system by defining just a few base variables. The resolution follows a strict priority:
- Property-Specific:
--bgc-primary. Each property first looks for a CSS variable prefixed with its name (e.g.,bgc-primaryresolves looking for --bgc-primary). - Type-Specific:
--color-primary. The first fallback is the type of the property (e.g., color properties look for --color-primary). - Value-Specific:
--primary. The second fallback is the name of the value itself as a CSS variable. - Literal:
primary. The final fallback is the value itself. You can use named CSS colors that browsers support directly (e.g.,c-red,bgc-blue,brc-silver). Invalid literals are silently ignored.
Tones and Transparency
You can generate an wide range of tones and alpha variations from any base color without defining them explicitly:
- Tones: Append
-{number}(e.g.,c-primary-200). 500 is the mid-tone; lower numbers move toward white, higher numbers move toward black. - Transparency: Append
/{number}(e.g.,c-primary-200/68for 68% opacity).
In the example below, we define a primary color using the --primary variable. Then, we use the bgc-primary-200/50 class to set the background color to 50% of the primary color's 200 shade, and the c-primary-700 class to set the text color to the primary color's 700 shade.
<div class="--primary=oklch(0.6_0.2_90)">
<div class="bgc-primary-200/50 c-primary-700 p-4 rad-3 fs-3.5">
<div class="fw-600 mb-2">Locally Themed Component</div>
This component mathematically generates its own
shades and contrast on the fly.
</div>
</div>
Color System
Number Resolution
Properties that accept numeric values (spacing, time, angles, unitless) handle resolutions automatically. If a number is provided, Maple applies a default unit or calculation:
/* Spacing: Resolved via --spacer (default 0.25rem) */
.p-4 { padding: var(--p-4, var(--space-4, calc(4rem * var(--spacer, 0.25)))); }
/* Timing: Defaults to ms */
.tsdur-300 { transition-duration: var(--tsdur-300, var(--time-300, 300ms)); }
/* Angle: Defaults to deg */
.rot-15 { --tf-rot: rotate(var(--rot-15, var(--angle-15, 15deg))); }
/* Unitless: Passed as is */
.z-4 { z-index: var(--z-4, 4); }
/* Opacity: Special handling (0-100 to 0.0-1.0) */
.o-6 { opacity: 0.06; }This architecture allows you to create a fluid design system while maintaining granular control. The resolution follows a strict priority:
- Property-Specific:
--p-4. Each property first looks for a CSS variable prefixed with its name and value name. - Type-Specific:
--space-4. The first fallback is the shared type of the property (e.g., all spacing utilities look for --space-*). - Literal / Calculation: The final fallback is either a calculated value for numbers (e.g.,
calc(4rem * var(--spacer, 0.25))) or the literal name for tokens.
Tokens and Strings
Number properties can also accept string values to be used as design tokens. When using a string, at least one fallback variable must be defined to ensure the rule is valid:
/* Spacing */
.p-md { padding: var(--p-md, var(--space-md, var(--md, md))); }
/* Time */
.tsdur-fast {
transition-duration: var(--tsdur-fast, var(--time-fast, var(--fast, fast)));
}
/* Angle */
.rot-lg { --tf-rot: rotate(var(--rot-lg, var(--angle-lg, var(--lg, lg)))); }
/* Unitless */
.z-max { z-index: var(--z-max, var(--max, max)); }
/* Opacity */
.o-half { opacity: var(--o-half, var(--half, half)); }
Negative Values
Maple supports two syntaxes for negative values:
- Prefix Syntax:
-m-4. Adding-before the property negates the entire resolved value. Best for tokens. - Inline Negative:
m--4. Uses a negative number in the value directly. This generates a negative calculation that can still be overridden via specific variables like--space--4.
/* Prefix Syntax: Negates the result */
.-m-4 {
margin: calc(var(--m-4, var(--space-4, calc(4rem * var(--spacer, 0.25)))) * -1);
}
/* Inline Negative: Resolved as negative number */
.m--4 {
margin: var(--m--4, var(--space--4, calc(-4rem * var(--spacer, 0.25))));
}
The prefix syntax is ideal for negating shared design tokens, while inline negatives are best for specific layout offsets:
<!-- Prefix syntax negating a spacer-based token -->
<div class="-m-4"></div>
<!-- Inline negative for a specific offset -->
<div class="m--4"></div>
<div class="m--4_-5"></div>
<div class="--spacer=0.5 bgc-body-100/50 p-4 rad-2 fs-3">
<!-- All numeric values (p-4, rad-2, fs-3, etc.) will be doubled -->
</div>
1 Unit Shorthands
Absolute and relative units have shorthand syntax for "1 unit". This allows for extremely concise class names when setting base sizes or resetting dimensions:
/* Absolute Shorthand (resolves to 1 unit) */
.w-px { width: 1px; }
.m-cm { margin: 1cm; }
/* Relative Shorthand (resolves to 1 unit) */
.w-rem { width: 1rem; }
.fs-em { font-size: 1em; }
The following units are supported for the "1 unit" shorthand:
px, cm, mm, in, pt, pc, Q
rem, em, ch, ex, cap, ic, lh, rlh, fr
Cover Unit Shorthands
There are also shorthands for covering the viewport or container. These resolve to 100% or 100 units of the viewport/container:
/* Percentage (resolves to 100%, <img class="w-%">) */
.w-\% { width: 100%; }
/* Viewport (resolves to 100 viewport units, <div class="h-vh"></div>) */
.h-vh { height: 100vh; }
.w-vw { width: 100vw; }
/* Container (resolves to 100 container units, <div class="h-cqh"></div>) */
.h-cqh { height: 100cqh; }
.w-cqw { width: 100cqw; }
The following units are supported for the "cover" shorthand:
%
vh, vw, vmin, vmax, vi, vb
dvh, dvw, dvmin, dvmax
svh, svw, svmin, svmax
lvh, lvw, lvmin, lvmax
cqw, cqh, cqi, cqb, cqmin, cqmax
Custom Resolution
Any property that is not a color or number is considered a custom property. These follow a standard semantic fallback chain without additional transformations:
/* Font Family */
.ff-main { font-family: var(--ff-main, var(--main, main)); }
/* Opacity */
.o-half { opacity: var(--o-half, var(--half, half)); }
This allows you to define generic tokens that can be used across different properties. The resolution follows these steps:
- Property-Specific:
--ff-main. Looks for a variable prefixed with the property name (or shorthand) and the value. - Value-Specific:
--main. Falls back to a variable named directly after the value. - Literal: The final fallback is the value name itself (e.g.,
main).
Reserved Values
When you use standard CSS keywords in a fallback chain, Maple treats them as literal strings instead of trying to resolve them as variables. This includes values like: initial, inherit, unset, revert, none, auto, transparent, currentColor, solid, dashed, and dotted.
/* Treating keywords as literals */
.w-auto { width: auto; }
.fs-inherit { font-size: inherit; }
.bgc-transparent { background-color: transparent; }
.bgimg-none { background-image: none; }
Arbitrary Values
When you need to bypass Maple's variable resolution and set an arbitrary value directly, you can use the Equal Sign = or Bracket [] syntax:
Equal Sign: Use instead of a hyphen to pass the value directly to CSS
<div class="bgc=red"><!-- background-color: red --></div> <div class="w=100px"><!-- width: 100px --></div> <div class="br=1px_solid_black"><!-- border: 1px solid black --></div>Brackets: Use for inline custom values or when your value contains characters that Maple uses as delimiters (like colons or spaces).
<div class="bgimg-url|[https://example.com/image.jpg]"> <!-- background-image: url(https://example.com/image.jpg) --> </div> <div class="m-4_[calc(100%_-_20px)]"> <!-- margin: 1rem calc(100% - 20px) --> </div>
Pro Tip
bgimg=[url(https://example.com/image.jpg)]. Special Character Handling
Maple uses reverse scanning to detect reserved delimiters (:, (), -, etc.). Single quotes, double quotes, and brackets control parsing depth—content inside them is treated as a single value. The key difference is:
- Brackets
[]are removed from the output - Quotes
'...'or"..."are preserved in the output
<div class="bgimg=[url(https://example.com/image.jpg)]">
<!-- background-image: url(https://example.com/image.jpg); -->
</div>
<div class="bgimg=url('https://example.com/image.jpg')">
<!-- background-image: url('https://example.com/image.jpg'); -->
</div>
<div class="@supports=[backdrop-filter:blur(1px)]:bdblur-4">
<!-- Brackets protect the colon in the @supports query -->
<!-- @supports (backdrop-filter:blur(1px)) { ... } -->
</div>
Dynamic Values
For values that change frequently (like scroll position, mouse coordinates, or sliders), generating unique persistent classes for every value can grow the CSSOM and hurt performance. Maple provides Dynamic Classes.
Prefix any class with $$ to mark it as dynamic. Dynamic classes:
- Are written to a special ephemeral CSS layer.
- Do not pollute the main stylesheet.
- Are automatically cleared and replaced on the next update cycle.
This is useful for integrating JS-driven values with Maple's utility system without adding a permanent rule for every intermediate value.
/* Example: using a slider to update CSS variables dynamically */
const slider = document.getElementById('slider');
const preview = document.getElementById('preview');
// Input event: Frequent updates use $$ (Dynamic)
slider.addEventListener('input', (e) => {
const val = e.target.value;
/**
* These classes are transient and cleared automatically
* on next frame/update
*/
preview.classList.add(`$$--p-spacer=${val}`, `$$--g-spacer=${val}`);
});
// Change event: Final value uses standard class (Persistent)
slider.addEventListener('change', (e) => {
const val = e.target.value;
// These are standard static classes
preview.classList.add(`--p-spacer=${val}`, `--g-spacer=${val}`);
});
Pro Tip
$$ for high-frequency runtime updates to keep the main stylesheet lean. Important Modifier
When you need to forcefully override a style, you can apply the native CSS !important flag directly to any utility class. Maple provides two ways to write this syntax, though the prefix approach is highly recommended for readability.
1. The Prefix Syntax (Recommended)
Place an exclamation mark ! at the very beginning of the utility string. Even if you are writing a long chain of media queries and selectors, the ! always goes at the front of the class name.
<div class="!bgc-red"></div>
<div class="!bgc=red"></div>
<div class="!@md:^:rtl:bgc=red"></div>
2. The Suffix Syntax
Alternatively, you can append _!important directly to the end of the value like regular CSS.
<div class="bgc-red_!important"></div>
<div class="bgc=red_!important"></div>
<div class="@md:^:rtl:bgc=red_!important"></div>