Crosswind CSS Framework
Crosswind (@cwcss/crosswind) is the utility-first CSS engine for Stacks — similar to Tailwind CSS but built for Bun.
Design & anti-slop skills
For taste-level guidance on which utilities to reach for (layout variance, spacing rhythm, color calibration, motion), see the design-taste family: stacks-design-taste (flagship), the presets stacks-design-soft / stacks-design-minimalist / stacks-design-brutalist, stacks-redesign, stacks-design-output, and the image-first stacks-image-to-code / stacks-imagegen-web / stacks-imagegen-mobile / stacks-brandkit.
Key Paths
- Package:
node_modules/@cwcss/crosswind/ - UI config:
config/ui.ts(Crosswind options referencing Crosswind) - Default styles:
storage/framework/defaults/styles/ - Output:
storage/framework/stx/cache/cw-<hash>.css, one file per page (stx'sstateDir, set inconfig/ui.ts)
Core API
CSSGenerator
import { CSSGenerator } from '@cwcss/crosswind'
const generator = new CSSGenerator(config: CrosswindConfig)
generator.generate(className: string): void // Process a single class
generator.toCSS(includePreflight?, minify?): string // Output CSS
generator.reset(): void // Clear generated CSS
Parser
import { parseClass, expandBracketSyntax, extractClasses, extractAttributifyClasses } from '@cwcss/crosswind'
parseClass('hover:bg-blue-500'): ParsedClass
// { raw: 'hover:bg-blue-500', variants: ['hover'], utility: 'bg-blue-500', important: false, arbitrary: false }
extractClasses(htmlContent: string, options?): Set<string>
expandBracketSyntax('text-[#1a1a1a]', config?): string[]
extractAttributifyClasses(content, config?): Set<string>
Scanner
import { Scanner } from '@cwcss/crosswind'
const scanner = new Scanner(patterns: string[], transformer?, extractOptions?)
await scanner.scan(): Promise<ScanResult>
await scanner.scanFile(filePath: string): Promise<Set<string>>
scanner.scanContent(content: string): Set<string>
Build Functions
import { build, writeCSS, buildAndWrite } from '@cwcss/crosswind'
const result = await build(config: CrosswindConfig): Promise<BuildResult>
await writeCSS(css: string, outputPath: string): Promise<void>
await buildAndWrite(config: CrosswindConfig): Promise<BuildResult>
interface BuildResult {
css: string
classes: Set<string>
duration: number
compiledClasses?: Map<string, { className: string, utilities: string[] }>
transformedFiles?: Map<string, string>
}
Bun Plugin
import { plugin } from '@cwcss/crosswind'
const bunPlugin = plugin(options?: CrosswindPluginOptions): BunPlugin
Configuration
interface CrosswindConfig {
content: string[] // Glob patterns for source files
output: string // Output CSS file path
minify: boolean
watch: boolean
verbose?: boolean
theme: Theme
shortcuts: Record<string, string | string[]>
rules: CustomRule[]
variants: VariantConfig
safelist: string[] // Always include these classes
blocklist: string[] // Never include these classes
preflights: Preflight[]
presets: Preset[]
compileClass?: CompileClassConfig
attributify?: AttributifyConfig
bracketSyntax?: BracketSyntaxConfig
cssVariables?: boolean
}
Theme
interface Theme {
colors: Record<string, string | Record<string, string>>
spacing: Record<string, string>
fontSize: Record<string, [string, { lineHeight: string }]>
fontFamily: Record<string, string[]>
screens: Record<string, string>
borderRadius: Record<string, string>
boxShadow: Record<string, string>
extend?: Partial<Omit<Theme, 'extend'>>
}
Variants (40+ built-in)
responsive, hover, focus, active, disabled, dark, group, peer,
before, after, marker, first, last, odd, even, first-of-type,
last-of-type, visited, checked, focus-within, focus-visible,
placeholder, selection, file, required, valid, invalid, read-only,
autofill, open, closed, empty, enabled, only, target, indeterminate,
default, optional, print, rtl, ltr, motion-safe, motion-reduce,
contrast-more, contrast-less
Parsed Class Structure
interface ParsedClass {
raw: string // Original class string
variants: string[] // Applied variants (hover, focus, etc.)
utility: string // Core utility name
value?: string // Arbitrary value if present
important: boolean // Has ! prefix
arbitrary: boolean // Uses [] brackets
typeHint?: string // Type hint for arbitrary values
}
Usage in Templates
<div class="flex items-center justify-between p-4 bg-white rounded-lg shadow">
<h1 class="font-bold text-2xl text-gray-900">Title</h1>
<button class="px-4 py-2 text-white bg-blue-500 hover:bg-blue-600 rounded">
Click me
</button>
</div>
<!-- Arbitrary values -->
<div class="grid-cols-[1fr_2fr] w-[calc(100%-2rem)] text-[#1a1a1a]">
<!-- Dark mode -->
<div class="text-black dark:text-white bg-white dark:bg-gray-900">
<!-- Responsive -->
<div class="w-full md:w-1/2 lg:w-1/3">
Built-in Utility Categories
- Display (flex, grid, block, hidden, container)
- Flexbox (direction, wrap, justify, align, gap)
- Spacing (margin, padding)
- Sizing (width, height, min/max)
- Typography (font-size, font-weight, line-height, text-align, text-color)
- Colors (background, text, border, ring)
- Borders (width, radius, style)
- Effects (shadow, opacity)
- Transitions & animations
Gotchas
- Not Tailwind — Crosswind is a separate implementation with Tailwind-compatible syntax
- Stacks uses Crosswind config —
config/ui.tsdefines Crosswind options which feed Crosswind - Output is per-page, content-hashed —
storage/framework/stx/cache/cw-<hash>.css, generated on demand rather than one bundled stylesheet. The directory is gitignored and safe to delete; the next request regenerates it - Bun plugin available — can be used as a Bun build plugin for automatic CSS generation
- Attributify mode — optional mode where utilities can be written as HTML attributes instead of classes
- Bracket syntax —
text-[#custom]for arbitrary values, same as Tailwind JIT - Custom rules — defined as
[RegExp, (match) => Record<string, string>]tuples - Presets — extensible via presets for shared configurations