Imported from eng-manager-xyz/auteur-rs (
.agents/skills/styling-system/SKILL.md). Install upstream withnpx skills add eng-manager-xyz/auteur-rs --skill styling-system. Copyright stays with the author.
Styletron + Base Web Theme Patterns
Overview
Styletron is an atomic CSS-in-JS engine. All styles are JavaScript objects — there are no utility classes, no CSS files, no class strings. Styletron generates atomic CSS classes at runtime for optimal performance.
Import Patterns
// Primary: from baseui (most common)
import { styled, useStyletron, withStyle } from 'baseui';
// From Fusion.js plugin (also common in app code)
import { styled, withStyle } from 'fusion-plugin-styletron-react';
// Direct styletron imports (rare — only for provider setup)
import { Provider as StyletronProvider } from 'styletron-react';
import { Client as Styletron } from 'styletron-engine-atomic';
Three Styling APIs
1. styled() — Component Factory (Most Common)
Creates a styled component from an HTML element or existing component:
import { styled } from 'baseui';
// Simple static styles
const Container = styled('div', {
display: 'flex',
alignItems: 'center',
padding: '16px',
backgroundColor: '#f5f5f5',
});
// Dynamic styles via function (access $theme and custom props)
const Card = styled<'div', { $elevated: boolean }>('div', ({ $theme, $elevated }) => ({
backgroundColor: $theme.colors.backgroundPrimary,
borderRadius: $theme.borders.radius300,
padding: $theme.sizing.scale600,
boxShadow: $elevated ? $theme.lighting.shadow600 : 'none',
}));
// Usage
<Card $elevated={true}>Content</Card>
Convention: Custom props passed to styled components use the $ prefix ($active, $disabled, $size) to avoid collision with HTML attributes.
2. useStyletron() — Hook for Inline Styles
Returns a css function and theme object. Best for conditional/dynamic styles within a component:
import { useStyletron } from 'baseui';
function StatusBadge({ status }: { status: 'active' | 'inactive' }) {
const [css, theme] = useStyletron();
return (
<span
className={css({
display: 'inline-flex',
alignItems: 'center',
padding: `${theme.sizing.scale100} ${theme.sizing.scale300}`,
borderRadius: theme.borders.radius200,
backgroundColor: status === 'active'
? theme.colors.backgroundPositive
: theme.colors.backgroundTertiary,
color: status === 'active'
? theme.colors.contentPositive
: theme.colors.contentSecondary,
...theme.typography.font100,
})}
>
{status}
</span>
);
}
3. withStyle() — HOC for Extending Styles
Extends an existing styled component with additional or overridden styles:
import { withStyle } from 'fusion-plugin-styletron-react';
// Extend a Base Web styled sub-component
import { StyledBaseButton } from 'baseui/button';
const WideButton = withStyle(StyledBaseButton, {
width: '100%',
marginTop: '16px',
});
// Or extend your own styled component
const BaseCard = styled('div', { padding: '16px', borderRadius: '8px' });
const ElevatedCard = withStyle(BaseCard, { boxShadow: '0 4px 12px rgba(0,0,0,0.1)' });
Theme Tokens (Design System)
Always use theme tokens instead of hardcoded values. Access via $theme in styled() or the second element of useStyletron().
Colors (Semantic — Use These)
// Background
$theme.colors.backgroundPrimary // Main background
$theme.colors.backgroundSecondary // Subtle background
$theme.colors.backgroundTertiary // Muted background
$theme.colors.backgroundInversePrimary // Dark background
// Content (text, icons)
$theme.colors.contentPrimary // Primary text
$theme.colors.contentSecondary // Secondary text
$theme.colors.contentTertiary // Muted text
$theme.colors.contentInversePrimary // Light text on dark bg
// Borders
$theme.colors.borderOpaque // Visible border
$theme.colors.borderTransparent // Subtle border
$theme.colors.borderSelected // Selected/active border
// Feedback states
$theme.colors.backgroundPositive // Success bg
$theme.colors.backgroundNegative // Error bg
$theme.colors.backgroundWarning // Warning bg
$theme.colors.backgroundAccent // Accent bg
$theme.colors.contentPositive // Success text
$theme.colors.contentNegative // Error text
$theme.colors.contentWarning // Warning text
$theme.colors.contentAccent // Accent text
$theme.colors.borderPositive // Success border
$theme.colors.borderNegative // Error border
// Brand
$theme.colors.brandBackgroundPrimary // Brand-colored background
$theme.colors.brandContentPrimary // Brand-colored text
$theme.colors.brandBorderAccessible // Brand-colored border (accessible contrast)
// State
$theme.colors.backgroundStateDisabled
$theme.colors.contentStateDisabled
$theme.colors.borderStateDisabled
Sizing Scale
$theme.sizing.scale0 // 2px
$theme.sizing.scale100 // 4px
$theme.sizing.scale200 // 6px
$theme.sizing.scale300 // 8px
$theme.sizing.scale400 // 10px
$theme.sizing.scale500 // 12px
$theme.sizing.scale550 // 14px
$theme.sizing.scale600 // 16px
$theme.sizing.scale700 // 20px
$theme.sizing.scale800 // 24px
$theme.sizing.scale900 // 32px
$theme.sizing.scale1000 // 40px
$theme.sizing.scale1200 // 48px
$theme.sizing.scale1400 // 56px
$theme.sizing.scale1600 // 64px
$theme.sizing.scale2400 // 96px
$theme.sizing.scale3200 // 128px
Typography
// Spread typography objects for font-family, size, weight, line-height
$theme.typography.font100 // 12px
$theme.typography.font200 // 14px
$theme.typography.font300 // 16px
$theme.typography.font400 // 16px semibold
$theme.typography.font450 // 18px
$theme.typography.font550 // 20px
$theme.typography.font650 // 24px
$theme.typography.font750 // 28px
$theme.typography.font850 // 32px
$theme.typography.font950 // 36px
$theme.typography.font1050 // 40px
$theme.typography.font1150 // 44px
$theme.typography.font1250 // 52px
$theme.typography.font1350 // 64px
$theme.typography.font1450 // 96px
// Usage in styled()
const Title = styled('h1', ({ $theme }) => ({
...$theme.typography.font750,
color: $theme.colors.contentPrimary,
marginBottom: $theme.sizing.scale600,
}));
Typography Components (Pre-Built)
import {
ParagraphSmall, ParagraphMedium, ParagraphLarge,
LabelSmall, LabelMedium, LabelLarge,
HeadingSmall, HeadingMedium, HeadingLarge, HeadingXLarge,
DisplaySmall, DisplayMedium, DisplayLarge,
} from 'baseui/typography';
<HeadingMedium>Page Title</HeadingMedium>
<ParagraphMedium>Body text content.</ParagraphMedium>
<LabelSmall>Form label</LabelSmall>
Borders
$theme.borders.border100 // Lightest border
$theme.borders.border200
$theme.borders.border300
$theme.borders.border400
$theme.borders.border500
$theme.borders.border600 // Heaviest border
$theme.borders.radius100 // 4px (subtle rounding)
$theme.borders.radius200 // 8px
$theme.borders.radius300 // 12px
$theme.borders.radius400 // 16px
$theme.borders.radius500 // 24px (pill)
// Component-specific radii
$theme.borders.buttonBorderRadius
$theme.borders.inputBorderRadius
$theme.borders.popoverBorderRadius
Shadows
$theme.lighting.shadow400 // Subtle elevation
$theme.lighting.shadow500
$theme.lighting.shadow600 // Medium elevation
$theme.lighting.shadow700 // High elevation
$theme.lighting.overlay0 // No overlay
$theme.lighting.overlay100 // Lightest overlay
$theme.lighting.overlay600 // Heaviest overlay
Animation
$theme.animation.timing100 // Fastest
$theme.animation.timing400 // Default
$theme.animation.timing700 // Slow
$theme.animation.timing1000 // Slowest
$theme.animation.easeOutCurve
$theme.animation.easeInCurve
$theme.animation.easeInOutCurve
$theme.animation.linearCurve
Responsive Design
Breakpoints
| Name | Value | Typical Use |
|---|---|---|
small |
320px | Mobile |
medium |
600px | Tablet |
large |
1136px | Desktop |
Media Queries in styled()
const ResponsiveCard = styled('div', ({ $theme }) => ({
padding: $theme.sizing.scale300,
display: 'flex',
flexDirection: 'column',
// Tablet and up
[$theme.mediaQuery.medium]: {
padding: $theme.sizing.scale600,
flexDirection: 'row',
},
// Desktop and up
[$theme.mediaQuery.large]: {
padding: $theme.sizing.scale800,
maxWidth: '1280px',
margin: '0 auto',
},
}));
Responsive Grid (Layout Grid)
import { Grid, Cell } from 'baseui/layout-grid';
// Grid auto-adjusts: 4 cols (mobile), 8 cols (tablet), 12 cols (desktop)
// span accepts responsive arrays: [mobile, tablet, desktop]
<Grid>
<Cell span={[4, 4, 6]}>Left column</Cell>
<Cell span={[4, 4, 6]}>Right column</Cell>
</Grid>
// Skip columns
<Cell span={[4, 6, 8]} skip={[0, 1, 2]}>Offset content</Cell>
Grid Defaults
- Columns:
[4, 8, 12](mobile, tablet, desktop) - Gutters:
[16, 36, 36]px - Margins:
[16, 36, 64]px - Max width: 1280px
Dark Mode / Theming
Theme Switching
import { ThemeProvider } from 'baseui/styles';
import { LightTheme, DarkTheme } from 'baseui';
function App() {
const [isDark, setIsDark] = useState(false);
return (
<ThemeProvider theme={isDark ? DarkTheme : LightTheme}>
{children}
</ThemeProvider>
);
}
Available Themes
LightTheme,DarkTheme— StandardLightThemeMove,DarkThemeMove— Move design- Brand factories:
createDefaultBrandThemeLight,createEatsBrandThemeLight,createAIBrandThemeLight - Custom:
createLightTheme(primitives, overrides),createDarkTheme(primitives, overrides)
Custom Theme
import { createLightTheme } from 'baseui';
const customTheme = createLightTheme(
// Primitive color overrides
{ primaryFontFamily: '"CustomFont", UberMoveText, system-ui, sans-serif' },
// Component-level overrides
{
colors: { brandBackgroundPrimary: '#1a73e8' },
borders: { radius200: '4px' },
}
);
<ThemeProvider theme={customTheme}>{children}</ThemeProvider>
Provider Setup
import { Provider as StyletronProvider } from 'styletron-react';
import { Client as Styletron } from 'styletron-engine-atomic';
import { ThemeProvider } from 'baseui/styles';
import { LightTheme } from 'baseui';
const engine = new Styletron();
function Root({ children }) {
return (
<StyletronProvider value={engine}>
<ThemeProvider theme={LightTheme}>
{children}
</ThemeProvider>
</StyletronProvider>
);
}
Note: In Fusion.js apps, fusion-plugin-styletron-react handles the StyletronProvider automatically. You only need ThemeProvider.
Common Patterns
Card
const Card = styled('div', ({ $theme }) => ({
backgroundColor: $theme.colors.backgroundPrimary,
borderRadius: $theme.borders.radius300,
border: `1px solid ${$theme.colors.borderOpaque}`,
padding: $theme.sizing.scale600,
boxShadow: $theme.lighting.shadow400,
}));
Flex Row with Gap
const Row = styled('div', ({ $theme }) => ({
display: 'flex',
alignItems: 'center',
gap: $theme.sizing.scale400,
}));
Visually Hidden (Screen Reader Only)
const ScreenReaderOnly = styled('span', {
position: 'absolute',
width: '1px',
height: '1px',
padding: '0',
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0, 0, 0, 0)',
whiteSpace: 'nowrap',
borderWidth: '0',
});
Truncated Text
const TruncatedText = styled('span', {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
display: 'block',
});
Transition
const FadeButton = styled('button', ({ $theme }) => ({
transition: `background-color ${$theme.animation.timing400} ${$theme.animation.easeOutCurve}`,
backgroundColor: $theme.colors.backgroundPrimary,
':hover': {
backgroundColor: $theme.colors.backgroundSecondary,
},
}));
Style Object Rules
Styletron style objects follow CSS-in-JS conventions:
- camelCase property names:
backgroundColor,fontSize,borderRadius - String values for most properties:
'16px','1px solid red','flex' - Number values for unitless properties:
opacity: 0.5,flex: 1,zIndex: 10 - Pseudo-selectors as nested keys:
':hover',':focus',':active',':disabled' - Media queries as nested keys:
[$theme.mediaQuery.medium] - No utility classes — everything is a style object
// WRONG: Tailwind-style classes
<div className="flex items-center p-4 bg-white rounded-lg shadow-md">
// CORRECT: Styletron style object
const [css, theme] = useStyletron();
<div className={css({
display: 'flex',
alignItems: 'center',
padding: theme.sizing.scale600,
backgroundColor: theme.colors.backgroundPrimary,
borderRadius: theme.borders.radius300,
boxShadow: theme.lighting.shadow400,
})}>
Evaluation
# Check for styled() usage
grep -rn "styled(" --include="*.tsx" --include="*.ts" | wc -l
# Check for useStyletron() hook usage
grep -rn "useStyletron()" --include="*.tsx" --include="*.ts" | wc -l
# Check for withStyle() usage
grep -rn "withStyle(" --include="*.tsx" --include="*.ts" | wc -l
# Check for theme token usage ($theme.colors, $theme.sizing, etc.)
grep -rn "\$theme\.\(colors\|sizing\|typography\|borders\|lighting\|animation\|mediaQuery\)" --include="*.tsx" --include="*.ts" | wc -l
# Check for ThemeProvider setup
grep -rn "ThemeProvider" --include="*.tsx" --include="*.ts" | wc -l
# Check for responsive mediaQuery usage
grep -rn "mediaQuery\.\(small\|medium\|large\)" --include="*.tsx" --include="*.ts" | wc -l
# Check for layout grid responsive arrays
grep -rn "span={\[" --include="*.tsx" | wc -l
# Verify no Tailwind utility classes remain
grep -rn "className=\"[^\"]*\b\(flex\|items-center\|justify-between\|p-[0-9]\|bg-\|text-\|rounded\|shadow\|hover:\|dark:\)\b" --include="*.tsx" | wc -l
echo "Should be 0 — all styles via Styletron"
# Verify no Tailwind imports remain
grep -rn "tailwindcss\|@tailwind\|@import.*tailwind" --include="*.css" --include="*.ts" --include="*.tsx" | wc -l
echo "Should be 0 — no Tailwind in this codebase"
# Check for proper pseudo-selector patterns (':hover', ':focus')
grep -rn "':hover'\|':focus'\|':active'" --include="*.tsx" --include="*.ts" | wc -l