Utilities
Overview
Utilities in Styleframe are reusable, single-purpose CSS classes that provide a foundation for atomic design patterns. They offer full type safety, auto-complete, and seamless integration with recipes to create flexible, maintainable styling systems that scale with your project.
Why use utilities?
Utilities help you:
- Build atomic design patterns: Create small, focused CSS classes that do one thing well and can be composed together.
- Maintain consistency: Ensure uniform spacing, colors, and typography across your entire application.
- Enable rapid prototyping: Quickly build interfaces by combining pre-defined utility classes.
- Integrate with recipes: Seamlessly work with responsive, state, and custom variants for maximum flexibility.
Defining Utilities
You define utilities using the utility() function from your styleframe instance:
import { styleframe } from "styleframe";
const s = styleframe();
const { variable, ref, utility } = s;
const spacing = variable("spacing", "1rem");
const spacingSm = variable("spacing--sm", "0.75rem");
const spacingMd = variable("spacing--md", ref(spacing));
const spacingLg = variable("spacing--lg", "1.25rem");
const createMarginUtility = utility("margin", ({ value }) => ({
margin: value,
}));
createMarginUtility({
default: ref(spacing),
sm: ref(spacingSm),
md: ref(spacingMd),
lg: ref(spacingLg),
});
export default s;
:root {
--spacing: 1rem;
--spacing--sm: 0.75rem;
--spacing--md: var(--spacing);
--spacing--lg: 1.25rem;
}
._margin {
margin: var(--spacing);
}
._margin\:sm {
margin: var(--spacing--sm);
}
._margin\:md {
margin: var(--spacing--md);
}
._margin\:lg {
margin: var(--spacing--lg);
}
The utility() function takes a base name and a factory function. The base name is used to generate the utility class names, while the factory function defines how the utility should be applied.
The factory function receives a context object that includes:
value: The current value being processed from the entries object- Other declaration context helpers such as
selector,variable,ref,media, etc.
The function returns a utility creator that you can call with an object containing value mappings (required), and an optional array of modifiers.
Each key in the entries object corresponds to a utility class, and the value is passed to the factory function.
kebab-case, camelCase, or PascalCase. When generating the utility classes, Styleframe will automatically convert them to kebab-case for consistency.The default generated utility class format is _property-name:value, which is intuitive and CSS-like. You can customize this format in your styleframe configuration if needed.
Defining Modifiers
Modifiers are functions that transform utility declarations to create variations based on conditions like hover states, media queries, or theme variations. They allow you to generate multiple related utility classes from a single base utility definition, extending your utilities with powerful conditional styling.
Modifiers are passed as the second parameter to the utility creator function:
import { styleframe } from "styleframe";
const s = styleframe();
const { utility, variable, ref, modifier } = s;
const colorPrimary = variable("color-primary", "#007bff");
const hover = modifier("hover", ({ declarations }) => ({
[`&:hover`]: declarations,
}));
const createBackgroundUtility = utility("background", ({ value }) => ({
background: value,
}));
createBackgroundUtility(
{
primary: ref(colorPrimary),
},
[hover],
);
export default s;
:root {
--color-primary: #007bff;
}
._background\:primary {
background: var(--color-primary);
}
._hover\:background\:primary {
&:hover {
background: var(--color-primary);
}
}
Complex Modifiers
Modifiers can contain complex logic to handle various conditions. The modifier function receives a context object with:
declarations: The CSS declarations generated by the utility factory function.variables: An array containing variables defined in the utility factory function.children: An array of child selectors generated by the utility factory function.- Other declaration context helpers such as
selector,variable,ref,media, etc.
import { styleframe } from "styleframe";
const s = styleframe();
const { utility, variable, ref, modifier } = s;
const colorPrimary = variable("color-primary", "#007bff");
const hover = modifier(
"hover",
({ declarations, variables, children, selector }) => {
selector("&:hover", { declarations, variables, children });
},
);
const createBackgroundUtility = utility("background", ({ value }) => ({
background: value,
}));
createBackgroundUtility(
{
primary: ref(colorPrimary),
},
[hover],
);
export default s;
:root {
--color-primary: #007bff;
}
._background\:primary {
background: var(--color-primary);
}
._hover\:background\:primary {
&:hover {
background: var(--color-primary);
}
}
Multi-key Modifiers
You can create modifiers that apply one of multiple keys, useful such as responsive breakpoints or custom conditions:
import { styleframe, valueOf } from "styleframe";
import { useBreakpoints } from "@styleframe/theme";
const s = styleframe();
const { utility, variable, ref, modifier } = s;
const { breakpointSm, breakpointMd, breakpointLg } = useBreakpoints(s);
const breakpoints = {
sm: valueOf(breakpointSm),
md: valueOf(breakpointMd),
lg: valueOf(breakpointLg),
};
const breakpointsMax = modifier(
["max-sm", "max-md", "max-lg"],
({ key, declarations }) => ({
[`@media screen and (max-width: ${
breakpoints[key.replace("max-", "")]
})`]: declarations,
}),
);
const createHiddenUtility = utility("hidden", () => ({
display: "none",
}));
createHiddenUtility({ default: undefined }, [breakpointsMax]);
export default s;
:root {
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
}
._hidden {
display: none;
}
._max-sm\:hidden {
@media screen and (max-width: 640px) {
display: none;
}
}
._max-md\:hidden {
@media screen and (max-width: 768px) {
display: none;
}
}
._max-lg\:hidden {
@media screen and (max-width: 1024px) {
display: none;
}
}
Examples
Layout Utilities
Build flexible layout systems with display and positioning utilities:
import { styleframe } from "styleframe";
const s = styleframe();
const { utility } = s;
const createDisplayUtility = utility("display", ({ value }) => ({
display: value,
}));
createDisplayUtility({
block: "block",
"inline-block": "inline-block",
inline: "inline",
flex: "flex",
"inline-flex": "inline-flex",
grid: "grid",
none: "none",
});
const createFlexDirectionUtility = utility("flex-direction", ({ value }) => ({
flexDirection: value,
}));
createFlexDirectionUtility({
row: "row",
column: "column",
});
const createJustifyContentUtility = utility("justify-content", ({ value }) => ({
justifyContent: value,
}));
createJustifyContentUtility({
start: "flex-start",
center: "center",
end: "flex-end",
between: "space-between",
});
const createAlignItemsUtility = utility("align-items", ({ value }) => ({
alignItems: value,
}));
createAlignItemsUtility({
start: "flex-start",
center: "center",
end: "flex-end",
stretch: "stretch",
});
export default s;
._display\:block {
display: block;
}
._display\:inline-block {
display: inline-block;
}
._display\:inline {
display: inline;
}
._display\:flex {
display: flex;
}
._display\:inline-flex {
display: inline-flex;
}
._display\:grid {
display: grid;
}
._display\:none {
display: none;
}
._flex-direction\:row {
flex-direction: row;
}
._flex-direction\:column {
flex-direction: column;
}
._justify-content\:start {
justify-content: flex-start;
}
._justify-content\:center {
justify-content: center;
}
._justify-content\:end {
justify-content: flex-end;
}
._justify-content\:between {
justify-content: space-between;
}
._align-items\:start {
align-items: flex-start;
}
._align-items\:center {
align-items: center;
}
._align-items\:end {
align-items: flex-end;
}
._align-items\:stretch {
align-items: stretch;
}
Complex Utilities
Create sophisticated utility systems that handle complex CSS properties and calculations:
import { styleframe, css } from "styleframe";
const s = styleframe();
const { variable, ref, utility } = s;
const createGridColumnUtility = utility("grid-column", ({ value }) => ({
gridColumn: css`span ${value} / span ${value}`,
}));
const gridSpanMap = {};
for (let i = 1; i <= 12; i++) {
gridSpanMap[i] = i;
}
createGridColumnUtility(gridSpanMap);
export default s;
._grid-column\:1 {
grid-column: span 1 / span 1;
}
._grid-column\:2 {
grid-column: span 2 / span 2;
}
._grid-column\:3 {
grid-column: span 3 / span 3;
}
/* ... */
._grid-column\:12 {
grid-column: span 12 / span 12;
}
Media Query Modifiers
You can create modifiers for responsive design using media queries:
import { styleframe } from "styleframe";
const s = styleframe();
const { utility, variable, ref, modifier } = s;
const colorPrimary = variable("color-primary", "#007bff");
const colorSecondary = variable("color-secondary", "#6c757d");
// Media query modifier for dark mode
const dark = modifier("dark", ({ declarations }) => ({
["@media (prefers-color-scheme: dark)"]: declarations,
}));
const createBackgroundUtility = utility("background", ({ value }) => ({
background: value,
}));
createBackgroundUtility(
{
primary: ref(colorPrimary),
secondary: ref(colorSecondary),
},
[dark],
);
export default s;
:root {
--color-primary: #007bff;
--color-secondary: #6c757d;
}
._background\:primary {
background: var(--color-primary);
}
._background\:secondary {
background: var(--color-secondary);
}
._dark\:background\:primary {
@media (prefers-color-scheme: dark) {
background: var(--color-primary);
}
}
._dark\:background\:secondary {
@media (prefers-color-scheme: dark) {
background: var(--color-secondary);
}
}
Combining Multiple Modifiers
Modifiers can be combined to create complex utility variations that handle multiple conditions:
import { styleframe } from "styleframe";
const s = styleframe();
const { utility, variable, ref, modifier } = s;
const colorPrimary = variable("color-primary", "#007bff");
const colorSecondary = variable("color-secondary", "#6c757d");
// Define multiple modifiers
const hover = modifier("hover", (declarations) => ({
[`&:hover`]: declarations,
}));
const dark = modifier("dark", (declarations) => ({
["@media (prefers-color-scheme: dark)"]: declarations,
}));
const focus = modifier("focus", (declarations) => ({
[`&:focus`]: declarations,
}));
const createBackgroundUtility = utility("background", ({ value }) => ({
background: value,
}));
// Apply multiple modifiers to the same utility
createBackgroundUtility(
{
primary: ref(colorPrimary),
secondary: ref(colorSecondary),
},
[hover, dark, focus],
);
export default s;
:root {
--color-primary: #007bff;
--color-secondary: #6c757d;
}
/* Base utilities */
._background\:primary {
background: var(--color-primary);
}
._background\:secondary {
background: var(--color-secondary);
}
/* Hover variations */
._hover\:background\:primary {
&:hover {
background: var(--color-primary);
}
}
._hover\:background\:secondary {
&:hover {
background: var(--color-secondary);
}
}
/* Dark mode variations */
._dark\:background\:primary {
@media (prefers-color-scheme: dark) {
background: var(--color-primary);
}
}
._dark\:background\:secondary {
@media (prefers-color-scheme: dark) {
background: var(--color-secondary);
}
}
/* Focus variations */
._focus\:background\:primary {
&:focus {
background: var(--color-primary);
}
}
._focus\:background\:secondary {
&:focus {
background: var(--color-secondary);
}
}
/* Combined modifier variations (dark + hover, dark + focus, etc.) */
._dark\:hover\:background\:primary {
@media (prefers-color-scheme: dark) {
&:hover {
background: var(--color-primary);
}
}
}
/* ... and more combinations */
Best Practices
- Keep utilities focused: Each utility should have a single, clear responsibility and do one thing well.
- Use descriptive naming: The
_property:valueformat makes utilities intuitive and CSS-like to use. - Leverage variables: Build utilities on top of your design system variables for consistency and maintainability.
- Create reusable modifiers: Define common modifiers (hover, focus, dark mode) once and reuse them across multiple utilities.
- Plan modifier combinations: Consider which modifiers work well together and design your utilities to handle common combinations.
- Use multi-key modifiers for mutually exclusive states: Breakpoints, themes, and directional variants benefit from multi-key modifier patterns.
- Create composables: Group related utilities and their modifiers into composable functions for better organization and reusability.
- Integrate with variants: Design utilities to work seamlessly with responsive, state, and custom variants.
- Batch utility creation: Use object mapping to create multiple related utilities efficiently.
- Test modifier combinations: Ensure that complex modifier combinations produce the expected CSS output.
- Document modifier behavior: Clear documentation helps team members understand when and how to use specific modifier combinations.
FAQ
_property:value format is more natural and CSS-like, making it intuitive for developers familiar with CSS. It clearly separates the property from the value while maintaining readability.hover) apply the same transformation regardless of the utility value. Multi-key modifiers (like responsive breakpoints) apply different transformations based on the specific key, making them perfect for mutually exclusive conditions._property:value format naturally prevents most conflicts by being explicit about the CSS property being targeted. Modifiers further namespace utilities by prefixing them with the modifier name.Interpolation
Styleframe interpolation provides a powerful CSS template literal utility that enables dynamic value insertion with full type safety. Create flexible, maintainable CSS values that adapt to your design system.
Themes
Styleframe themes provide powerful design system theming capabilities with type-safe variable overrides. Create consistent light/dark modes, brand variants, and user personalization with ease.