Using simple utility classes to style complex component layouts.
In Sfumato you style markup using one or more single-purpose utility classes on your HTML elements.
You run the sfumato
tool in the background in a terminal or other command window, and it will watch as you make changes to your code and build a tiny output CSS file that "just works".
Using only utility classes, you can quickly create complex layouts and effects.
In the example above, some of the utility classes we used includes:
At first it may seem excessive to use stacks of classes on your HTML elements. But once you get used to it you'll realize that there are practical benefits:
These and other benefits are valuable and apply to projects of any size.
There are practical benefits to using Sfumato utility classes over inline style attributes. Here are some drawbacks to that approach:
To style an element based on states like hover or focus, prefix any utility class with the state, for example hover:bg-sky-500:
These prefixes are called variants in Sfumato and they only apply the utility class styles when the condition for the variant matches.
The generated CSS for the hover style hover:bg-sky-500 looks like this:
You can learn more about available state variants by visiting hover and other states.
Similar to states like hover, you can also style elements at different breakpoints by prefixing any utility class with the breakpoint:
This example uses container queries to simulate a page-level media query, but is still helpful for showing how they work. In this case the toggle changes the container width between 100% and 33%. At 100% width the "xs" media breakpoint is valid, changing the grid to 3 columns.
The generated CSS for the container breakpoint is:
Using breakpoints outside containers is the more common scenario. The example below shows the same example but based on the viewport width, not a container. Resize your web browser to see this one in action.
The generated CSS for the media breakpoints is:
Styling an element for dark themes uses a variant prefix (dark:), like element state and media queries:
Try the theme switcher widget above and watch how the buttons appear to switch positions as the color and text change based on the dark/light theme.
Some classes work together to style a single CSS property. One example of this are the various filter options that all use the CSS filter property.
Sfumato uses CSS variables so that these effects can be composed together:
Many Sfumato utility classes like bg-sky-500, text-lg, and shadow-sm map to default values and your configured color library, type scale, shadows, etc.
When you need to specify a different, one-off value, use the square bracket syntax for specifying arbitrary values:
These are especially useful for specifying complex styles, like a very specific grid configuration:
Important: utility classes cannot have spaces, so use underscores in place of spaces
You can also define entirely custom CSS, including user-defined property names—something that’s especially handy when working with CSS variables.
Unlike traditional CSS frameworks that come as a large, fixed stylesheet, Sfumato builds your styles on demand. It identifies only the classes you use in your project.
To achieve this, it scans your entire codebase for anything resembling a class name in a string delimiter. This works across languages and platforms, from HTML and JavaScript, to hybrid mobile app frameworks, and more.
Here is an example ASP.NET Razor component:
There are cases where you need to apply styles based on multiple conditions—like being in dark mode, hitting a certain screen size, on hover, and when a particular data attribute is present.
Here’s a simplified example of how you can handle that scenario:
Sfumato also includes support for features like group-hover, allowing you to apply styles to an element when a specific parent element is being hovered.
Most other pseudo classes (e.g. states like "focus") also work using this group-* syntax.
Inline styles remain valuable in Sfumato projects, especially when styling needs to be driven by dynamic data from sources like APIs or databases.
You may also choose to use an inline style when dealing with complex arbitrary values that become hard to interpret when written as class names.
When using only utility classes to build a full project, it’s common to repeat the same set of styles across multiple elements to maintain a consistent design.
Here's an example:
Often, a design element that appears multiple times on the page is only written once in the code, since it’s typically generated within a loop.
For example, the repeated list items shown above would almost certainly be rendered using a loop in a real-world project.
When you want to share styles across multiple files, the most effective approach is to create a component if you’re working with a front-end framework like React, Vue, or Svelte. You can also use this strategy with platforms like ASP.NET Razor/Blazor.
Below is an example of a news panel that would be reused across your application:
Another strategy for reducing duplication is using shared string constants or properties.
In CSS conflicting styles are resolved by selector specificity first, then placement in the CSS file second.
So if you need to ensure a specific utility class takes precedence and there’s no better way to handle specificity, you can append "!" to the class name to apply !important to all of its declarations.
You can also use CSS layers to isolate your classes such that utilities can override them, or not. Just wrap your classes in a @layer components and you will be able to override their styles with utilities. Otherwise your styles will always override utilities.