Welcome to Faency! This guide will help you integrate Faency into your React application and make the most of its theming capabilities.
- Installation
- Getting Started
- Theming
- Using Components
- Styling Components
- TypeScript Support
- Best Practices
Install Faency via npm or yarn:
# npm
npm install @traefik-labs/faency
# yarn
yarn add @traefik-labs/faencyFaency requires React 18+ as a peer dependency:
npm install react react-domWrap your application with the FaencyProvider:
import React from 'react';
import { FaencyProvider } from '@traefik-labs/faency';
import { Button, Box, Text } from '@traefik-labs/faency';
function App() {
return (
<FaencyProvider>
<Box css={{ p: '$4' }}>
<Text>Welcome to Faency!</Text>
<Button>Click Me</Button>
</Box>
</FaencyProvider>
);
}
export default App;For Vanilla Extract components, use the VanillaExtractThemeProvider:
import React from 'react';
import { VanillaExtractThemeProvider } from '@traefiklabs/faency';
import { BoxVanilla, BadgeVanilla } from '@traefiklabs/faency';
function App() {
return (
<VanillaExtractThemeProvider defaultTheme="light" primaryColor="blue">
<BoxVanilla css={{ p: '$4' }}>
<BadgeVanilla variant="blue">Welcome to Faency!</BadgeVanilla>
</BoxVanilla>
</VanillaExtractThemeProvider>
);
}
export default App;Note: CSS is automatically included when you import Vanilla Extract components - no separate CSS import needed.
Stitches components use runtime CSS-in-JS, so no separate CSS imports are needed. Styles are automatically injected when you use components.
Vanilla Extract components require importing the static CSS file. Add this import to your application's entry point (e.g., App.tsx or index.tsx):
import '@traefik-labs/faency/dist/style.css';This CSS file contains all the styles for Vanilla Extract components (components with Vanilla suffix like BadgeVanilla, BoxVanilla, etc.). Without this import, these components will render as unstyled elements.
Vanilla Extract components include their CSS automatically when imported. No separate CSS import is required - styles are bundled with each component module.
Faency includes a powerful theming system with support for light/dark modes and customizable primary colors.
///
For more control over theming, use the new Vanilla Extract theme provider:
import { VanillaExtractThemeProvider } from '@traefik-labs/faency';
function App() {
return (
<VanillaExtractThemeProvider defaultTheme="light">{/* Your app */}</VanillaExtractThemeProvider>
);
}Theme Mode Options:
"light"- Force light theme"dark"- Force dark theme"system"- Follow system preference (default)
Faency supports 7 primary color themes:
| Color | Description |
|---|---|
neon |
Bright green |
blue |
Classic blue (default) |
orange |
Vibrant orange |
red |
Bold red |
green |
Natural green |
deepBlue |
Deep navy blue |
grayBlue |
Muted blue-gray |
import { VanillaExtractThemeProvider } from '@traefik-labs/faency';
function App() {
return (
<VanillaExtractThemeProvider defaultTheme="light" primaryColor="orange">
{/* Your app */}
</VanillaExtractThemeProvider>
);
}Access and control the theme using the useVanillaExtractTheme hook:
import { useVanillaExtractTheme } from '@traefik-labs/faency';
function ThemeToggle() {
const { mode, resolvedTheme, primaryColor, setMode, setPrimaryColor } = useVanillaExtractTheme();
return (
<div>
<p>Current theme: {resolvedTheme}</p>
<p>Primary color: {primaryColor}</p>
<button onClick={() => setMode('light')}>Light</button>
<button onClick={() => setMode('dark')}>Dark</button>
<button onClick={() => setMode('system')}>System</button>
<button onClick={() => setPrimaryColor('blue')}>Blue</button>
<button onClick={() => setPrimaryColor('orange')}>Orange</button>
<button onClick={() => setPrimaryColor('neon')}>Neon</button>
</div>
);
}VanillaExtractThemeProvider Props:
| Prop | Type | Default | Description |
|---|---|---|---|
defaultTheme |
'light' | 'dark' | 'system' |
'system' |
Initial theme mode |
primaryColor |
PrimaryColor |
'blue' |
Initial primary color |
forcedTheme |
'light' | 'dark' |
undefined |
Override theme (useful for Storybook) |
useVanillaExtractTheme() Return Value:
{
mode: ThemeMode; // Current mode setting
resolvedTheme: 'light' | 'dark'; // Actual applied theme
primaryColor: PrimaryColor; // Current primary color
colors: ColorMap; // All color tokens
setMode: (mode: ThemeMode) => void;
setPrimaryColor: (color: PrimaryColor) => void;
}// Import individual components
import { Button, Box, Text, Input, Badge } from '@traefik-labs/faency';
// Or import everything
import * as Faency from '@traefik-labs/faency';import { Button } from '@traefik-labs/faency';
<Button variant="primary" size="medium">
Click Me
</Button>;Props:
variant:"primary" | "secondary" | "ghost" | "outline"size:"small" | "medium" | "large"disabled:boolean
import { Box } from '@traefik-labs/faency';
<Box css={{ p: '$4', bc: '$blue4', br: '$2' }}>Content goes here</Box>;The Box component is a flexible container for layouts. Use it with the css prop for custom styling.
import { Text } from '@traefik-labs/faency';
<Text size="4" css={{ c: '$blue11' }}>
Styled text content
</Text>;Props:
size:"1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"as: HTML element (default:"span")
Faency components accept a css prop for custom styling. This prop uses a shorthand syntax for common CSS properties:
<Box
css={{
p: '$4', // padding
px: '$6', // paddingLeft & paddingRight
py: '$3', // paddingTop & paddingBottom
m: '$2', // margin
mx: '$4', // marginLeft & marginRight
bc: '$blue6', // backgroundColor
c: '$blue11', // color
br: '$2', // borderRadius
ta: 'center', // textAlign
fd: 'column', // flexDirection
ai: 'center', // alignItems
jc: 'space-between', // justifyContent
fg: 1, // flexGrow
fs: 0, // flexShrink
fontSize: '$4', // fontSize
fontWeight: 'bold', // fontWeight
lh: 1.5, // lineHeight
}}
>
Content
</Box>| Shorthand | CSS Property | Example |
|---|---|---|
p |
padding |
p: '$4' |
pt |
padding-top |
pt: '$2' |
pr |
padding-right |
pr: '$2' |
pb |
padding-bottom |
pb: '$2' |
pl |
padding-left |
pl: '$2' |
px |
padding-left & padding-right |
px: '$4' |
py |
padding-top & padding-bottom |
py: '$3' |
m |
margin |
m: '$4' |
mt |
margin-top |
mt: '$2' |
mr |
margin-right |
mr: '$2' |
mb |
margin-bottom |
mb: '$2' |
ml |
margin-left |
ml: '$2' |
mx |
margin-left & margin-right |
mx: 'auto' |
my |
margin-top & margin-bottom |
my: '$3' |
bc |
background-color |
bc: '$blue4' |
c |
color |
c: '$blue11' |
br |
border-radius |
br: '$2' |
ta |
text-align |
ta: 'center' |
fd |
flex-direction |
fd: 'column' |
fw |
flex-wrap |
fw: 'wrap' |
ai |
align-items |
ai: 'center' |
jc |
justify-content |
jc: 'space-between' |
as |
align-self |
as: 'flex-start' |
fg |
flex-grow |
fg: 1 |
fs |
flex-shrink |
fs: 0 |
lh |
line-height |
lh: 1.5 |
fontSize |
font-size |
fontSize: '$4' |
fontWeight |
font-weight |
fontWeight: 'bold' |
Faency uses design tokens for consistent spacing, colors, and typography. Reference tokens with the $ prefix:
$1; // 4px
$2; // 8px
$3; // 16px
$4; // 20px
$5; // 24px
$6; // 32px
$7; // 48px
$8; // 64px
$9; // 80px$1; // 4px
$2; // 6px
$3; // 8px
$4; // 12px
$round; // 50%
$pill; // 9999px$0; // 11px
$1; // 12px
$2; // 13px
$3; // 14px
$4; // 16px
$5; // 18px
$6; // 20px
$7; // 22px
$8; // 24px
$9; // 26px
$10; // 28px
$11; // 32px
$12; // 38pxEach color has 12 steps, with lower numbers being lighter:
// Example with blue
$blue1; // Lightest
$blue6; // Medium (good for backgrounds)
$blue9; // Bright (primary color)
$blue11; // High contrast (good for text)
$blue12; // Darkest
// Available colors
($neon, $blue, $orange, $red, $green, $deepBlue, $grayBlue);
($gray, $slate, $purple);$primary; // Primary brand color
$contentBg; // Main content background
$hiContrast; // High contrast text
$loContrast; // Low contrast text
$border; // Border color
$accent; // Accent color for highlights
$destructive; // Error/danger color
$muted; // Muted background
$mutedForeground; // Muted text color<Box css={{ bc: '$blue6', c: '$blue11' }}>
Blue themed box
</Box>
<Box css={{ bc: '$primary', c: 'white' }}>
Primary colored box
</Box>
<Text css={{ c: '$hiContrast' }}>
High contrast text
</Text>Faency is built with TypeScript and provides full type definitions.
All component props are fully typed:
import { Button } from '@traefik-labs/faency';
import type { ComponentProps } from 'react';
// Get the prop types
type ButtonProps = ComponentProps<typeof Button>;
function MyButton(props: ButtonProps) {
return <Button {...props} />;
}import type { CSSProps } from '@traefik-labs/faency';
interface MyComponentProps extends CSSProps {
label: string;
}
function MyComponent({ label, css }: MyComponentProps) {
return <Box css={css}>{label}</Box>;
}import type { PrimaryColor, ThemeMode } from '@traefik-labs/faency';
const primaryColor: PrimaryColor = 'blue';
const themeMode: ThemeMode = 'light';Instead of hardcoding specific colors, use semantic tokens that adapt to the theme:
// ✅ Good - adapts to theme
<Box css={{ bc: '$contentBg', c: '$hiContrast' }}>
Content
</Box>
// ❌ Avoid - doesn't adapt to theme
<Box css={{ bc: '#ffffff', c: '#000000' }}>
Content
</Box>Use spacing and sizing tokens for consistency:
// ✅ Good - consistent spacing
<Box css={{ p: '$4', m: '$3' }}>
Content
</Box>
// ❌ Avoid - arbitrary values
<Box css={{ p: '23px', m: '17px' }}>
Content
</Box>Always wrap your application root with the theme provider:
// ✅ Good
function App() {
return (
<VanillaExtractThemeProvider>
<YourApp />
</VanillaExtractThemeProvider>
);
}
// ❌ Bad - components won't have theme context
function App() {
return <YourApp />;
}Use Box for layout composition:
<Box css={{ fd: 'column', gap: '$4' }}>
<Box css={{ fd: 'row', jc: 'space-between', ai: 'center' }}>
<Text size="5">Title</Text>
<Button>Action</Button>
</Box>
<Box css={{ p: '$4', bc: '$muted', br: '$2' }}>
<Text>Content area</Text>
</Box>
</Box>For new projects, use the Vanilla Extract components (when available) for better performance:
import { BoxVanilla } from '@traefik-labs/faency';
// Better performance with static CSS
<BoxVanilla css={{ p: '$4' }}>Content</BoxVanilla>;Here's a complete example showing theme integration:
import React from 'react';
import {
VanillaExtractThemeProvider,
useVanillaExtractTheme,
Box,
Button,
Text,
Badge,
} from '@traefik-labs/faency';
function ThemeControls() {
const { resolvedTheme, setMode, primaryColor, setPrimaryColor } = useVanillaExtractTheme();
return (
<Box css={{ p: '$4', bc: '$muted', br: '$2', mb: '$4' }}>
<Text size="4" css={{ fontWeight: 'bold', mb: '$3' }}>
Theme Controls
</Text>
<Box css={{ fd: 'row', gap: '$2', mb: '$3' }}>
<Button onClick={() => setMode('light')}>Light</Button>
<Button onClick={() => setMode('dark')}>Dark</Button>
<Button onClick={() => setMode('system')}>System</Button>
</Box>
<Box css={{ fd: 'row', gap: '$2' }}>
<Button onClick={() => setPrimaryColor('blue')}>Blue</Button>
<Button onClick={() => setPrimaryColor('orange')}>Orange</Button>
<Button onClick={() => setPrimaryColor('green')}>Green</Button>
</Box>
<Text css={{ mt: '$3' }}>
Current: <Badge variant={primaryColor}>{resolvedTheme}</Badge>
</Text>
</Box>
);
}
function App() {
return (
<VanillaExtractThemeProvider primaryColor="blue">
<Box css={{ p: '$6', minHeight: '100vh', bc: '$contentBg' }}>
<Text size="9" css={{ fontWeight: 'bold', mb: '$4', c: '$hiContrast' }}>
Faency Demo
</Text>
<ThemeControls />
<Box css={{ fd: 'column', gap: '$4' }}>
<Box css={{ p: '$4', bc: '$card', br: '$3', border: '1px solid $border' }}>
<Text size="5" css={{ fontWeight: 'bold', mb: '$2' }}>
Welcome to Faency
</Text>
<Text css={{ c: '$mutedForeground' }}>
A comprehensive design system for Traefik Labs applications
</Text>
</Box>
<Box css={{ fd: 'row', gap: '$3' }}>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
</Box>
</Box>
</Box>
</VanillaExtractThemeProvider>
);
}
export default App;- Component Documentation - Interactive component examples
- GitHub Repository - Source code and issues
- Vanilla Extract Migration Guide - For contributors
- Project Guidelines - Development guidelines