Build-time CSS-in-JS library with powerful variants and compound variants support
Zero-runtime styling solution that extracts your CSS at build time while maintaining full type safety and dynamic capabilities where you need them.
π Zero Runtime Overhead - CSS is extracted at build time
π― Type-Safe Variants - Full TypeScript support for variants and compound variants
β‘ Dynamic Properties - Support for runtime dynamic styles when needed
π₯ Powerful Variants System - Simple variants and complex compound variants
π οΈ Build-Time Optimized - Minimal JavaScript bundle size
π¨ CSS-in-JS DX - Familiar API with modern developer experience
π¦ Framework Agnostic - Works with React (more frameworks coming soon)
npm install @scape-css/react @scape-css/compiler
# or
pnpm add @scape-css/react @scape-css/compilerimport { styled } from '@scape-css/react';
const Button = styled('button', {
backgroundColor: 'blue',
color: 'white',
padding: '8px 16px',
borderRadius: '4px',
border: 'none',
cursor: 'pointer',
'&:hover': {
backgroundColor: 'darkblue',
},
});
function App() {
return <Button>Click me!</Button>;
}Create multiple style variations for your components:
const Button = styled('button', {
// Base styles
padding: '8px 16px',
borderRadius: '4px',
border: 'none',
cursor: 'pointer',
variants: {
intent: {
primary: {
backgroundColor: 'blue',
color: 'white',
},
secondary: {
backgroundColor: 'gray',
color: 'black',
},
danger: {
backgroundColor: 'red',
color: 'white',
},
},
size: {
small: {
padding: '4px 8px',
fontSize: '12px',
},
medium: {
padding: '8px 16px',
fontSize: '14px',
},
large: {
padding: '12px 24px',
fontSize: '16px',
},
},
},
});
// Usage with full type safety
<Button intent="primary" size="large">
Primary Large Button
</Button>Handle complex styling combinations with compound variants:
const Button = styled('button', {
// Base styles
padding: '8px 16px',
borderRadius: '4px',
border: 'none',
cursor: 'pointer',
variants: {
intent: {
primary: { backgroundColor: 'blue', color: 'white' },
secondary: { backgroundColor: 'gray', color: 'black' },
},
size: {
small: { padding: '4px 8px', fontSize: '12px' },
large: { padding: '12px 24px', fontSize: '16px' },
},
disabled: {
true: { opacity: 0.5, cursor: 'not-allowed' },
false: {},
},
},
// Compound variants for specific combinations
compoundVariants: [
{
intent: 'primary',
size: 'large',
css: {
boxShadow: '0 4px 8px rgba(0, 0, 255, 0.3)',
fontWeight: 'bold',
},
},
{
intent: 'secondary',
disabled: true,
css: {
backgroundColor: '#f5f5f5',
color: '#999',
},
},
],
});
// The compound variant styles are automatically applied
<Button intent="primary" size="large">
// Gets primary + large + compound variant styles
</Button>While scape optimizes for build-time extraction, it supports dynamic properties when you need runtime flexibility:
const Card = styled('div', {
padding: '16px',
borderRadius: '8px',
backgroundColor: 'white',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
});
// Dynamic styles are handled at runtime
function DynamicCard({ color }: { color: string }) {
return (
<Card style={{ borderLeft: `4px solid ${color}` }}>
Dynamic colored border
</Card>
);
}Scape uses a sophisticated build-time compiler that:
- Parses your styled components during build
- Extracts CSS styles into static stylesheets
- Generates optimized class names and runtime helpers
- Transforms your code to use the generated classes
- Preserves type safety throughout the process
// What you write:
const Button = styled('button', {
backgroundColor: 'blue',
variants: {
size: {
small: { padding: '4px' },
large: { padding: '8px' }
}
}
});
// What gets generated:
// CSS: .btn-abc123 { background-color: blue; }
// .btn-small-def456 { padding: 4px; }
// .btn-large-ghi789 { padding: 8px; }
//
// JS: Optimized runtime with minimal overhead| Package | Description |
|---|---|
@scape-css/react |
React bindings and runtime |
@scape-css/compiler |
Build-time compiler and transformer |
@scape-css/webpack-loader |
Webpack integration |
@scape-css/swc-parser |
SWC-based parser for optimal performance |
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: [
{
loader: '@scape-css/webpack-loader',
options: {
// Configuration options
},
},
// Your other loaders (ts-loader, etc.)
],
},
],
},
};// vite.config.ts
import { defineConfig } from 'vite';
import { scapePlugin } from '@scape-css/vite-plugin';
export default defineConfig({
plugins: [
scapePlugin({
// Configuration options
}),
],
});const Container = styled('div', {
padding: '16px',
'@media (min-width: 768px)': {
padding: '32px',
},
'@media (min-width: 1024px)': {
padding: '48px',
},
});const Navigation = styled('nav', {
'& ul': {
listStyle: 'none',
margin: 0,
padding: 0,
},
'& li': {
display: 'inline-block',
'& a': {
color: 'blue',
textDecoration: 'none',
'&:hover': {
textDecoration: 'underline',
},
},
},
});const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
},
spacing: {
sm: '8px',
md: '16px',
lg: '24px',
},
};
const ThemedButton = styled('button', {
backgroundColor: theme.colors.primary,
padding: `${theme.spacing.sm} ${theme.spacing.md}`,
color: 'white',
border: 'none',
borderRadius: '4px',
});- Base runtime: ~2KB gzipped
- Per component: ~50-100 bytes additional
- CSS extraction: Styles are moved to CSS files, reducing JS bundle size
- SWC-powered: Leverages SWC for ultra-fast parsing
- Incremental: Only reprocesses changed files
- Parallel: Supports parallel processing for large codebases
| Feature | scape | styled-components | emotion | stitches |
|---|---|---|---|---|
| Runtime Size | ~2KB | ~15KB | ~7KB | ~5KB |
| Build-time CSS | β | β | β | β |
| Variants | β | Manual | Manual | β |
| Compound Variants | β | Manual | Manual | β |
| TypeScript | β | β | β | β |
| SSR | β | β | β | β |
We welcome contributions! Please see our Contributing Guide for details.
# Clone the repository
git clone https://github.com/DanTsk/scape.git
cd scape
# Install dependencies
pnpm install
# Run tests
pnpm test
# Build packages
pnpm buildMIT Β© Dan T.
Documentation β’ Examples β’ Discord β’ Twitter
Made with β€οΈ for the React community