styled-bidi
v1.0.0
Published
A styled-components wrapper with automatic RTL/LTR directional CSS flipping and theme token merging.
Maintainers
Readme
styled-bidi
A lightweight wrapper around styled-components that adds automatic RTL/LTR directional CSS flipping and theme token merging — so you write styles once and they just work in both directions.
Features
- Automatic BiDi flipping —
margin-leftbecomesmargin-rightin LTR, and vice versa. Covers margins, paddings, borders, border-radii, positioning, and text alignment. - Theme token merging — Deep-merges component-level design tokens from your theme with instance styles.
$styleprop — Override styles per-instance without creating a new component.- Zero config — Works with your existing styled-components theme provider.
- Tiny — No dependencies beyond
styled-components.
Install
npm install styled-bidi styled-componentsQuick Start
import { styledBidi } from 'styled-bidi';
const Card = styledBidi('Card', 'div', ({ theme }) => ({
padding: '16px',
marginRight: '12px', // ← flipped to marginLeft in LTR
borderLeft: '3px solid blue', // ← flipped to borderRight in LTR
borderTopLeftRadius: '8px', // ← flipped to borderTopRightRadius in LTR
backgroundColor: theme.colors?.surface || '#fff',
}));That's it. Render <Card /> inside a <ThemeProvider> and directional properties are handled automatically based on theme.direction.
How It Works
1. Direction Flipping
Set direction in your theme to control the behavior:
| theme.direction | Behavior |
|---|---|
| 'rtl' (default) | Styles are used as-is (RTL is the base direction) |
| 'ltr' | Directional properties are flipped |
| 'single' | No flipping — styles pass through unchanged |
import { ThemeProvider } from 'styled-components';
// RTL app — styles used as written
<ThemeProvider theme={{ direction: 'rtl' }}>
<Card />
</ThemeProvider>
// LTR app — directional props auto-flipped
<ThemeProvider theme={{ direction: 'ltr' }}>
<Card />
</ThemeProvider>Properties That Flip
| RTL (as written) | LTR (auto-flipped) |
|---|---|
| left / right | right / left |
| marginLeft / marginRight | marginRight / marginLeft |
| paddingLeft / paddingRight | paddingRight / paddingLeft |
| borderLeft / borderRight | borderRight / borderLeft |
| borderLeftColor / borderRightColor | borderRightColor / borderLeftColor |
| borderLeftWidth / borderRightWidth | borderRightWidth / borderLeftWidth |
| borderLeftStyle / borderRightStyle | borderRightStyle / borderLeftStyle |
| borderTopLeftRadius / borderTopRightRadius | borderTopRightRadius / borderTopLeftRadius |
| borderBottomLeftRadius / borderBottomRightRadius | borderBottomRightRadius / borderBottomLeftRadius |
| text-align: left / right | text-align: right / left |
Both camelCase and kebab-case properties are supported.
2. Theme Token Merging
Define component-level tokens in your theme under theme.components[tokenName]. They are deep-merged with the styles returned by your style function:
const theme = {
direction: 'rtl',
components: {
Card: {
backgroundColor: '#f5f5f5',
borderRadius: '12px',
},
},
};
// The Card component's styles are merged with theme.components.Card
const Card = styledBidi('Card', 'div', ({ theme }) => ({
padding: '16px',
backgroundColor: '#fff', // ← overridden by theme token '#f5f5f5'
}));3. Per-Instance Overrides
Use the $style transient prop to override styles on a specific instance:
<Card $style={{ padding: '24px', border: '1px solid red' }} />API
styledBidi(tokenName, component, styles)
| Parameter | Type | Description |
|---|---|---|
| tokenName | string | Key to look up in theme.components for token merging |
| component | React.ComponentType \| string | Base component or HTML tag to style |
| styles | (props) => object | Function receiving { theme, ...props }, returns a CSS-in-JS object |
Returns: A styled-component with BiDi support and theme token merging.
Full Example
import { ThemeProvider } from 'styled-components';
import { styledBidi } from 'styled-bidi';
const theme = {
direction: 'ltr',
colors: { primary: '#0066ff', surface: '#ffffff' },
components: {
Alert: {
borderRadius: '8px',
},
},
};
const Alert = styledBidi('Alert', 'div', ({ theme }) => ({
padding: '12px 16px',
borderRight: `4px solid ${theme.colors.primary}`, // flipped to borderLeft in LTR
paddingRight: '20px', // flipped to paddingLeft in LTR
backgroundColor: theme.colors.surface,
borderTopRightRadius: '8px', // flipped to borderTopLeftRadius in LTR
}));
function App() {
return (
<ThemeProvider theme={theme}>
<Alert>This alert adapts to text direction automatically.</Alert>
<Alert $style={{ backgroundColor: '#fff3cd' }}>Custom background.</Alert>
</ThemeProvider>
);
}License
MIT
