| name | unocss |
| description | UnoCSS atomic CSS patterns for SvelteKit 2 + Svelte 5. Use when styling components, writing utility classes, configuring uno.config.ts, integrating icons with preset-icons, or styling Bits UI components. Includes safelist patterns, dynamic class gotchas, dark mode, and FOUC prevention. Essential for any class attribute work. (project) |
UnoCSS Patterns for Velociraptor
UnoCSS atomic CSS engine patterns for SvelteKit 2 + Svelte 5 projects.
Contents
- Critical Gotchas - Dynamic classes, file scanning, FOUC
- SvelteKit Integration - Vite plugin, import, scoped mode
- Configuration - Presets, shortcuts, theme, transformers
- Dynamic Classes - Static maps, safelist, conditionals
- Icons - Installation, usage, dynamic icons
- Variant Groups - hover:(bg-gray text-white)
- Shortcuts - Static and dynamic
- @apply Directive - In style blocks
- Dark Mode - Class-based toggle
- Bits UI Integration - Data attributes, CSS variables
- File Scanning - Enable JS/TS scanning
- Tailwind Migration Notes - Syntax differences
- Anti-Patterns - Common mistakes
- References - Detailed guides
Critical Gotchas
| Issue | Impact | Solution |
|---|
| Dynamic class interpolation | Classes not generated | Use safelist or static object maps |
.js/.ts files not scanned | Missing classes in production | Add @unocss-include comment |
| Tailwind comma syntax | Grid cols broken | Use underscore: grid-cols-[1fr_10px_max-content] |
| Icons not showing | Empty space in UI | Install icon collection AND safelist dynamic icons |
| FOUC in production | Flash of unstyled content | Set cssCodeSplit: false in vite config |
SvelteKit Integration
Vite Plugin Setup
import { sveltekit } from '@sveltejs/kit/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
UnoCSS(),
sveltekit(),
],
build: {
cssCodeSplit: false,
},
});
Import UnoCSS
<!-- src/routes/+layout.svelte -->
<script>
import 'uno.css';
let { children } = $props();
</script>
{@render children()}
Svelte Scoped Mode (Large Projects)
For component-scoped CSS instead of global stylesheet:
import UnoCSS from '@unocss/svelte-scoped/vite';
export default defineConfig({
plugins: [
UnoCSS({
injectReset: '@unocss/reset/tailwind.css',
}),
sveltekit(),
],
});
<head>
%unocss-svelte-scoped.global%
%sveltekit.head%
</head>
const unocssInject: Handle = async ({ event, resolve }) => {
return resolve(event, {
transformPageChunk: ({ html }) =>
html.replace('%unocss-svelte-scoped.global%', 'unocss_svelte_scoped_global_styles')
});
};
Configuration
Standard Config
import {
defineConfig,
presetWind3,
presetAttributify,
presetIcons,
presetTypography,
} from 'unocss';
import transformerDirectives from '@unocss/transformer-directives';
import transformerVariantGroup from '@unocss/transformer-variant-group';
export default defineConfig({
presets: [
presetWind3({ dark: 'class' }),
presetAttributify({
prefix: 'data-',
prefixedOnly: true,
}),
presetIcons({
scale: 1.2,
extraProperties: {
'display': 'inline-block',
'vertical-align': 'middle',
},
}),
presetTypography(),
],
shortcuts: {
'btn': 'px-4 py-2 rounded font-semibold shadow transition-colors',
'btn-primary': 'btn bg-primary text-white hover:bg-primary-dark',
'card': 'bg-white dark:bg-gray-800 rounded-lg shadow-md p-6',
},
theme: {
colors: {
primary: '#3b82f6',
'primary-dark': '#2563eb',
},
},
transformers: [
transformerDirectives(),
transformerVariantGroup(),
],
});
Preset Selection
| Preset | Purpose | When to Use |
|---|
presetWind3 | Tailwind v3 compat | Default choice |
presetWind4 | Tailwind v4 compat | New v4 projects |
presetMini | Minimal rules | Custom design systems |
presetIcons | Pure CSS icons | Icon libraries |
presetAttributify | HTML attribute mode | Cleaner templates |
presetTypography | Prose styling | Content pages |
Dynamic Classes
UnoCSS is compile-time. Dynamic interpolation does NOT work.
Wrong
<script>
let color = 'red';
</script>
<div class="bg-{color}-500"></div> <!-- Won't generate CSS! -->
Correct: Static Object Map
<script>
// @unocss-include
const colorMap = {
red: 'bg-red-500',
blue: 'bg-blue-500',
green: 'bg-green-500',
};
let color = 'red';
</script>
<div class={colorMap[color]}></div>
Correct: Safelist
export default defineConfig({
safelist: [
'bg-red-500',
'bg-blue-500',
'bg-green-500',
],
});
Correct: Conditional Classes
{#if color === 'red'}
<div class="bg-red-500"></div>
{:else if color === 'blue'}
<div class="bg-blue-500"></div>
{/if}
Icons
Requires @unocss/preset-icons and icon collections like @iconify-json/lucide.
Usage
<!-- Syntax: i-{collection}-{icon-name} -->
<span class="i-lucide-home text-icon-lg"></span>
<span class="i-lucide-settings text-icon-md"></span>
Dynamic Icons — MUST Safelist
Any icon used in a JS data structure (icon: 'i-lucide-...') MUST be added to the safelist in uno.config.ts. UnoCSS cannot reliably extract icon classes from JS objects/arrays. Without safelisting, icons silently render as invisible zero-width spans.
safelist: [
'i-lucide-database',
'i-lucide-bar-chart-2',
]
Static icon classes in Svelte templates (class="i-lucide-check") are extracted automatically and do NOT need safelisting.
Variant Groups
Cleaner syntax for grouped states:
<!-- Without variant groups -->
<div class="hover:bg-gray-400 hover:text-white focus:bg-gray-400 focus:text-white"></div>
<!-- With variant groups (transformer required) -->
<div class="hover:(bg-gray-400 text-white) focus:(bg-gray-400 text-white)"></div>
Shortcuts
Static Shortcuts
shortcuts: {
'btn': 'py-2 px-4 font-semibold rounded-lg shadow-md',
'btn-primary': 'btn bg-primary text-white hover:bg-primary-dark',
}
Dynamic Shortcuts
shortcuts: [
[/^btn-(.*)$/, ([, c]) => `bg-${c}-500 hover:bg-${c}-600 text-white px-4 py-2 rounded`],
]
@apply Directive
Use in <style> blocks with transformer:
<style>
.custom-button {
@apply px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600;
}
</style>
Dark Mode
<!-- Class-based (default with presetWind3) -->
<div class="bg-white dark:bg-gray-900 text-black dark:text-white"></div>
Toggle in parent:
<html class="dark">
Bits UI Integration
Bits UI is headless - apply UnoCSS directly:
<script>
import { Accordion } from 'bits-ui';
</script>
<Accordion.Root class="space-y-2">
<Accordion.Item value="item-1">
<Accordion.Trigger class="btn btn-primary w-full text-left">
Click me
</Accordion.Trigger>
<Accordion.Content class="p-4 bg-gray-100 dark:bg-gray-800">
Content here
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
Data Attribute Styling
<style>
:global([data-accordion-trigger][data-state="open"]) {
@apply bg-blue-700;
}
</style>
CSS Variables
<!-- Match anchor width -->
<Select.Content class="w-[var(--bits-select-anchor-width)]">
File Scanning
Default scanned extensions:
.jsx, .tsx, .vue, .svelte, .html, .md, .astro
NOT scanned: .js, .ts
Enable JS/TS Scanning
Per-file:
export const classes = 'px-4 py-2 bg-blue-500';
Global:
export default defineConfig({
content: {
pipeline: {
include: [
/\.(vue|svelte|[jt]sx|mdx?|html)($|\?)/,
'src/**/*.{js,ts}',
],
},
},
});
Tailwind Migration Notes
| Feature | Tailwind | UnoCSS |
|---|
| Grid cols spaces | grid-cols-[1fr,10px,max-content] | grid-cols-[1fr_10px_max-content] |
| Variant groups | Not built-in | hover:(bg-red text-white) |
| Icons | JS components | i-mdi-home (pure CSS) |
| Plugins | Supported | Not supported (use presets/rules) |
Anti-Patterns
Don't: Interpolate Classes
<!-- WRONG -->
<div class={`text-${size}`}></div>
Don't: Expect Auto-scanning of TS/JS
export const buttonClass = 'bg-blue-500';
export const buttonClass = 'bg-blue-500';
Don't: Massive Safelists
safelist: Object.keys(allIcons).map(i => `i-mdi-${i}`),
safelist: ['bg-red-500', 'bg-blue-500'],
Don't: Use @apply for Everything
<!-- WRONG - defeats purpose of atomic CSS -->
<style>
.my-div { @apply text-sm text-white bg-blue-500 p-4 m-2 rounded; }
</style>
<!-- RIGHT - use classes directly -->
<div class="text-sm text-white bg-blue-500 p-4 m-2 rounded"></div>
References
See references/ for detailed guides:
setup.md - Full installation and configuration
presets.md - Preset configuration patterns
icons.md - Icon integration deep dive
gotchas.md - Common issues and solutions
bits-ui.md - Bits UI styling patterns